QB / ESX

Agency-LifeinvaderV3

Modern advertisement system for FiveM with custom Liquid Glass UI, tiered pricing based on character count, and ad queue management. Supports ad categories with filtering, priority/boost ads, anonymous posting, and database persistence via oxmysql. Includes admin panel for approving/deleting ads and works with ESX, QBCore, and Standalone.

v3.0.0Paid4 Pages
Agency-LifeinvaderV3

01 Installation

Extract to resources directory
Import agency_lifeinvader.sql (optional, has auto-upgrader)
Add ensure Agency-LifeinvaderV3 to server.cfg
Configure config.lua (framework, pricing, notifications)

Dependencies

  • oxmysql — required if using database
  • ESX or QBCore — optional, supports standalone

SQL File

Import this SQL file into your database before starting the resource:

⬇ lifeinvader.sql
-- ═══════════════════════════════════════════
-- AGENCY LIFEINVADER V3 - DATABASE SETUP
-- by Agency Scripts
-- ═══════════════════════════════════════════

CREATE TABLE IF NOT EXISTS `lifeinvader_ads` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `identifier` VARCHAR(100) NOT NULL,
    `author_name` VARCHAR(100) DEFAULT 'Anonymous',
    `message` TEXT NOT NULL,
    `category` VARCHAR(50) DEFAULT 'other',
    `anonymous` TINYINT DEFAULT 0,
    `boosted` TINYINT DEFAULT 0,
    `repetitions_total` INT DEFAULT 1,
    `repetitions_left` INT DEFAULT 1,
    `interval_seconds` INT DEFAULT 60,
    `display_duration` INT DEFAULT 8,
    `status` ENUM('pending','active','completed','rejected') DEFAULT 'active',
    `approved` TINYINT DEFAULT 1,
    `approved_by` VARCHAR(100) DEFAULT NULL,
    `rejected_reason` TEXT DEFAULT NULL,
    `next_send_time` DATETIME DEFAULT NULL,
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX `idx_identifier` (`identifier`),
    INDEX `idx_status` (`status`),
    INDEX `idx_approved` (`approved`),
    INDEX `idx_category` (`category`),
    INDEX `idx_boosted` (`boosted`),
    INDEX `idx_next_send` (`next_send_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS `lifeinvader_player_settings` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `identifier` VARCHAR(100) NOT NULL UNIQUE,
    `notification_position` VARCHAR(20) DEFAULT '0,50',
    `notification_enabled` TINYINT DEFAULT 1,
    `notification_scale` FLOAT DEFAULT 1.0,
    `ui_transparency` FLOAT DEFAULT 0.55,
    `accent_color` VARCHAR(20) DEFAULT '#3b82f6',
    `sound_enabled` TINYINT DEFAULT 1,
    `admin_panel_scale` FLOAT DEFAULT 1.0,
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX `idx_identifier` (`identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS `lifeinvader_ad_drafts` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `identifier` VARCHAR(100) NOT NULL,
    `title` VARCHAR(120) DEFAULT NULL,
    `message` TEXT NOT NULL,
    `category` VARCHAR(50) DEFAULT 'other',
    `anonymous` TINYINT DEFAULT 0,
    `boosted` TINYINT DEFAULT 0,
    `repetitions_total` INT DEFAULT 1,
    `interval_seconds` INT DEFAULT 60,
    `display_duration` INT DEFAULT 8,
    `delay` INT DEFAULT 0,
    `scheduled_for` DATETIME DEFAULT NULL,
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX `idx_identifier` (`identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS `lifeinvader_admin_logs` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `admin_identifier` VARCHAR(100) DEFAULT NULL,
    `admin_name` VARCHAR(100) DEFAULT NULL,
    `action` VARCHAR(64) NOT NULL,
    `ad_id` INT DEFAULT NULL,
    `metadata` LONGTEXT DEFAULT NULL,
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX `idx_action` (`action`),
    INDEX `idx_ad_id` (`ad_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ═══ AUTO UPGRADE SYSTEM ═══
-- Safely adds missing columns without throwing errors on existing databases

DROP PROCEDURE IF EXISTS `UpgradeLifeinvaderColumns`;

DELIMITER //
CREATE PROCEDURE `UpgradeLifeinvaderColumns`()
BEGIN
    DECLARE _dbName VARCHAR(255) DEFAULT DATABASE();
    DECLARE cnt INT;

    -- Add category column
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'category';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `category` VARCHAR(50) DEFAULT 'other' AFTER `message`;
    END IF;

    -- Add anonymous column
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'anonymous';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `anonymous` TINYINT DEFAULT 0 AFTER `category`;
    END IF;

    -- Add boosted column
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'boosted';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `boosted` TINYINT DEFAULT 0 AFTER `anonymous`;
    END IF;

    -- Add ui_transparency column to player settings
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_player_settings' AND COLUMN_NAME = 'ui_transparency';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_player_settings` ADD COLUMN `ui_transparency` FLOAT DEFAULT 0.55 AFTER `notification_scale`;
    END IF;

    -- Add accent_color column to player settings
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_player_settings' AND COLUMN_NAME = 'accent_color';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_player_settings` ADD COLUMN `accent_color` VARCHAR(20) DEFAULT '#3b82f6' AFTER `ui_transparency`;
    END IF;

    -- Add admin_panel_scale column to player settings
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_player_settings' AND COLUMN_NAME = 'admin_panel_scale';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_player_settings` ADD COLUMN `admin_panel_scale` FLOAT DEFAULT 1.0 AFTER `sound_enabled`;
    END IF;

    -- Add display_duration column to drafts
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ad_drafts' AND COLUMN_NAME = 'display_duration';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ad_drafts` ADD COLUMN `display_duration` INT DEFAULT 8 AFTER `interval_seconds`;
    END IF;

    -- Add delay column to drafts
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ad_drafts' AND COLUMN_NAME = 'delay';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ad_drafts` ADD COLUMN `delay` INT DEFAULT 0 AFTER `display_duration`;
    END IF;

    -- Add scheduling / review / AI fields
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'scheduled_for';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `scheduled_for` DATETIME DEFAULT NULL AFTER `next_send_time`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'review_requested_at';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `review_requested_at` DATETIME DEFAULT NULL AFTER `approved`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'reviewed_at';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `reviewed_at` DATETIME DEFAULT NULL AFTER `review_requested_at`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'review_decision';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `review_decision` VARCHAR(32) DEFAULT NULL AFTER `reviewed_at`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'queue_priority';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `queue_priority` INT DEFAULT 0 AFTER `review_decision`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'ai_score';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `ai_score` INT DEFAULT 0 AFTER `queue_priority`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'ai_flags';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `ai_flags` LONGTEXT DEFAULT NULL AFTER `ai_score`;
    END IF;

    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'admin_notes';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `admin_notes` LONGTEXT DEFAULT NULL AFTER `ai_flags`;
    END IF;

    -- Add display_duration column
    SELECT COUNT(*) INTO cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = _dbName AND TABLE_NAME = 'lifeinvader_ads' AND COLUMN_NAME = 'display_duration';
    IF cnt = 0 THEN
        ALTER TABLE `lifeinvader_ads` ADD COLUMN `display_duration` INT DEFAULT 8 AFTER `interval_seconds`;
    END IF;

    -- Indexes cannot be safely checked inline as easily in older MySQL, 
    -- but usually they don't break queries if missing. We only add them on fresh install via CREATE TABLE above.
END //
DELIMITER ;

CALL UpgradeLifeinvaderColumns();
DROP PROCEDURE IF EXISTS `UpgradeLifeinvaderColumns`;

Need help? Join our Discord community for support!

Join Discord