Проблема с effil.lua (attempt to call field 'queue')

kalonne

Участник
Автор темы
23
15
Версия SA-MP
  1. 0.3.7-R2
Всем привет!

Столкнулся с проблемой при запуске скрипта, который очень важен для меня. При загрузке он сразу вылетает, а в логе moonloader появляется следующая ошибка:

[ML] (error) init.lua: C:\Games\arizona gay\moonloader\init.lua:28: attempt to call field 'queue' (a nil value)
stack traceback:
C:\Games\arizona gay\moonloader\init.lua:28: in main chunk
[ML] (error) init.lua: Script died due to an error. (1D9E1D9C)

Я так понимаю, проблема в том, что у меня установлена устаревшая версия библиотеки effil, в которой еще нет необходимой для работы скрипта функции queue.

Огромная просьба, поделитесь, пожалуйста, ссылкой на актуальную и рабочую версию библиотеки effil.lua. Уже несколько часов ищу, но попадаются либо нерабочие, либо такие же старые версии.

Заранее огромное спасибо всем, кто откликнется!



lua:
 script_name                            ("AAAAAAAAAAAAAAAAAAAAAA")
-- Улучшенная проверка наличия и версии библиотек
local mimgui_ok, imgui = pcall(require, 'mimgui')
local effil_ok, effil = pcall(require, 'effil')

if not mimgui_ok or not effil_ok then
    print("~r~[TaimerPRO] КРИТИЧЕСКАЯ ОШИБКА:")
    if not mimgui_ok then print("~r~ - Библиотека 'mimgui' не найдена. Скрипт не может работать без нее.") end
    if not effil_ok then print("~r~ - Библиотека 'effil' не найдена. Скрипт не может работать без нее.") end
    print("~y~[TaimerPRO] Пожалуйста, установите недостающие библиотеки и перезапустите скрипт.")
    thisScript():unload()
    return
end

if not effil.queue then
    print("~r~[TaimerPRO] КРИТИЧЕСКАЯ ОШИБКА:")
    print("~r~ - Ваша версия библиотеки 'effil' устарела и не поддерживает функцию 'queue'.")
    print("~y~[TaimerPRO] Пожалуйста, обновите 'effil' до последней версии, чтобы скрипт мог работать корректно.")
    print("~y~[TaimerPRO] Скачать можно с Blast.hk или других ресурсов MoonLoader.")
    thisScript():unload()
    return
end

-- Подключение остальных необходимых библиотек
local dkjson = require 'dkjson'
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local https = require 'ssl.https'
local sampev = require 'lib.samp.events'
local vkeys = require 'vkeys'

-- Встроенная библиотека Base64 для самодостаточности скрипта
local b64 = {}
do
    local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    function b64.encode(data)
        return ((data:gsub('.', function(x)
            local r, b = '', x:byte()
            for i = 8, 1, -1 do r = r .. (b % 2 ^ i - b % 2 ^ (i - 1) > 0 and '1' or '0') end
            return r;
        end) .. '0000'):gsub('%d%d%d%d%d%d', function(x)
            if (#x < 6) then return '' end
            local c = 0
            for i = 1, 6 do c = c + (x:sub(i, i) == '1' and 2 ^ (6 - i) or 0) end
            return b:sub(c + 1, c + 1)
        end) .. ({ '', '==', '=' })[#data % 3 + 1])
    end
    function b64.decode(data)
        data = string.gsub(data, '[^'..b..'=]', '')
        return (data:gsub('.', function(x)
            if (x == '=') then return '' end
            local r, f = '', (b:find(x) - 1)
            for i = 6, 1, -1 do r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0') end
            return r;
        end):gsub('%d%d%d%d%d%d%d%d', function(x)
            if (#x < 8) then return '' end
            local c = 0
            for i = 1, 8 do c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0) end
            return string.char(c)
        end))
    end
end

-- Глобальные переменные и состояние
local main_window_state = imgui.new.bool(false)
local script_version = "1.2.1"
local config_path = getFolderPath(0x1A) .. '\\moonloader\\config\\TaimerPRO.json'
local state_path = getFolderPath(0x1A) .. '\\moonloader\\config\\TaimerPRO.state.json'
local W, H = getScreenResolution()

local message_queue = effil.queue()
local active_timers = {}
local last_telegram_update_id = 0

-- Переменные для UI
local inputs = {
    profile_name = imgui.new.char[128](''),
    hours = imgui.new.int(0),
    minutes = imgui.new.int(5),
    seconds = imgui.new.int(0),
    message = imgui.new.char[256](''),
    font_name = imgui.new.char[128]('Arial'),
    font_size = imgui.new.int(20),
    font_flags = imgui.new.int(5),
    tg_token = imgui.new.char[256](''),
    tg_chat_id = imgui.new.char[128]('')
}
local editing_profile_id = nil
local show_profile_editor = false
local customFont = nil

--- Вспомогательные функции для ImGui (идея из Flooder.lua) ---
function imgui.CenterText(text)
    local width = imgui.GetWindowWidth()
    local text_width = imgui.CalcTextSize(text).x
    imgui.SetCursorPosX((width - text_width) / 2)
    imgui.Text(text)
end

function imgui.Hint(text)
    if imgui.IsItemHovered() then
        imgui.SetTooltip(text)
    end
end
-------------------------------------------------------------

-- Настройки по умолчанию
local defaultConfig = {
    settings = {
        theme = "Dark",
        hud = {
            mode = 1, -- 1: ImGui окно, 2: Нативный текст
            imgui_pos = { x = 10, y = 200 },
            native_pos = { x = W / 2, y = 10 },
            font_name = "Arial",
            font_size = 20,
            font_flags = 5,
            font_color = "FFFFFF",
            show_shadow = true
        },
        hotkeysEnabled = true,
        telegram = {
            enabled = false,
            allow_commands = false,
            token_b64 = "",
            chat_id = ""
        }
    },
    profiles = {},
    autostarts = {},
    chains = {},
    window = {
        pos = { x = 500, y = 300 },
        size = { x = 600, y = 400 }
    }
}
local config = {}

-----------------------------------------------------
--[[ 1. ОСНОВА И УПРАВЛЕНИЕ КОНФИГУРАЦИЕЙ ⚙️ ]]--
-----------------------------------------------------

local function merge_tables(default, custom)
    local result = {}
    for k, v in pairs(default) do
        if type(v) == 'table' and custom and custom[k] and type(custom[k]) == 'table' then
            result[k] = merge_tables(v, custom[k])
        elseif custom and custom[k] ~= nil then
            result[k] = custom[k]
        else
            result[k] = v
        end
    end
    return result
end

function saveAllSettings()
    config.settings.hud.font_name = u8.decode(inputs.font_name)
    config.settings.hud.font_size = inputs.font_size[0]
    config.settings.hud.font_flags = inputs.font_flags[0]
    config.settings.telegram.token_b64 = b64.encode(u8.decode(inputs.tg_token))
    config.settings.telegram.chat_id = u8.decode(inputs.tg_chat_id)

    local file = io.open(config_path, 'w')
    if file then
        file:write(dkjson.encode(config, { indent = true }))
        file:close()
    end
end

function fontUpdate()
    local s = config.settings.hud
    customFont = renderCreateFont(s.font_name, s.font_size, s.font_flags)
end

function load_settings_on_startup()
    local file = io.open(config_path, 'r')
    local loaded_config
    if file then
        local content = file:read('*a')
        file:close()
        pcall(function() loaded_config = dkjson.decode(content) end)
    end
    config = merge_tables(defaultConfig, loaded_config or {})

    imgui.Strncpy(inputs.font_name, u8.encode(config.settings.hud.font_name), 128)
    inputs.font_size[0] = config.settings.hud.font_size
    inputs.font_flags[0] = config.settings.hud.font_flags

    if config.settings.telegram.token_b64 and config.settings.telegram.token_b64 ~= "" then
        pcall(function()
            local decoded_token = b64.decode(config.settings.telegram.token_b64)
            imgui.Strncpy(inputs.tg_token, decoded_token, 256)
        end)
    end
    if config.settings.telegram.chat_id then
        imgui.Strncpy(inputs.tg_chat_id, u8.encode(config.settings.telegram.chat_id), 128)
    end

    fontUpdate()
end

-----------------------------------------------------
--[[ 2. ЛОГИКА ТАЙМЕРОВ И МНОГОПОТОЧНОСТЬ ⏱️ ]]--
-----------------------------------------------------

function timer_thread(id, duration, message, profileName, settings)
    local start_time = os.clock()
    local time_slept = 0
    local paused = false
    local remaining_on_pause = duration

    while time_slept < duration do
        local command = message_queue:get(id, 0)
        if command == 'stop' then
            message_queue:push({ type = 'timer_stopped', id = id, silent = true })
            return
        elseif command == 'pause' then
            paused = true
            remaining_on_pause = duration - time_slept
        elseif command == 'resume' then
            paused = false
            duration = remaining_on_pause
            start_time = os.clock()
            time_slept = 0
        end

        if not paused then
            effil.sleep(100)
            time_slept = os.clock() - start_time
        else
            effil.sleep(200)
        end
    end

    message_queue:push({ type = 'samp', text = ('{00BFFF}[TaimerPRO]{FFFFFF} Таймер "%s" завершен!'):format(profileName) })
    if message and message ~= "" then
        message_queue:push({ type = 'samp', text = ('{00BFFF}Сообщение:{FFFFFF} %s'):format(message) })
    end
    if settings.telegram.enabled then
        message_queue:push({ type = 'telegram_send', text = ('Таймер "%s" завершен!'):format(profileName) })
    end
    message_queue:push({ type = 'timer_finished', id = id })
end

function startNewTimer(profile)
    -- Проверяем, не запущен ли уже таймер для этого профиля
    for _, timer in pairs(active_timers) do
        if timer.profile_id == profile.id then
            sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Таймер для профиля '%s' уже запущен."):format(profile.name), 0xFF0000)
            return
        end
    end

    local id = os.time() + math.random(1000)
    local duration = (profile.h or 0) * 3600 + (profile.m or 0) * 60 + (profile.s or 0)

    if duration <= 0 then
        sampAddChatMessage('{00BFFF}[TaimerPRO]{FFFFFF} Ошибка: Длительность таймера должна быть больше нуля.', 0xFF0000)
        return
    end

    local thread = effil.thread(timer_thread)(id, duration, profile.message, profile.name, config.settings)

    active_timers[id] = {
        thread = thread,
        profile_id = profile.id,
        name = profile.name,
        start_time = os.time(),
        duration = duration,
        paused = false,
        remaining_on_pause = duration
    }
    sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Таймер '%s' запущен на %dч %dм %dс."):format(profile.name, profile.h, profile.m, profile.s), 0x2ECC71)
end

function stopTimer(id)
    if active_timers[id] then
        message_queue:push(id, 'stop')
    end
end

function togglePause(id)
    local timer = active_timers[id]
    if not timer then return end

    timer.paused = not timer.paused
    if timer.paused then
        message_queue:push(id, 'pause')
        local elapsed = os.time() - timer.start_time
        timer.remaining_on_pause = timer.duration - elapsed
    else
        message_queue:push(id, 'resume')
        timer.start_time = os.time()
        timer.duration = timer.remaining_on_pause
    end
end

function save_timers_state()
    local state_to_save = {}
    for id, timer in pairs(active_timers) do
        local remaining
        if timer.paused then
            remaining = timer.remaining_on_pause
        else
            remaining = timer.duration - (os.time() - timer.start_time)
        end

        if remaining > 0 then
            table.insert(state_to_save, {
                profile_id = timer.profile_id,
                remaining = remaining
            })
        end
    end

    if #state_to_save > 0 then
        local file = io.open(state_path, 'w')
        if file then
            file:write(dkjson.encode(state_to_save))
            file:close()
        end
    end
end

function resume_persistent_timers()
    local file = io.open(state_path, 'r')
    if not file then return end

    local content = file:read('*a')
    file:close()
    pcall(os.remove, state_path)

    local resumed_timers
    pcall(function() resumed_timers = dkjson.decode(content) end)

    if resumed_timers and type(resumed_timers) == 'table' then
        for _, t in ipairs(resumed_timers) do
            local profile = config.profiles[t.profile_id]
            if profile then
                local restored_profile = {
                    id = profile.id,
                    name = profile.name,
                    h = math.floor(t.remaining / 3600),
                    m = math.floor((t.remaining % 3600) / 60),
                    s = t.remaining % 60,
                    message = profile.message
                }
                startNewTimer(restored_profile)
            end
        end
    end
end

---------------------------------------------------
--[[ 3. СИСТЕМА ПРОФИЛЕЙ И ЦЕПОЧЕК 📂 ]]--
---------------------------------------------------

function openProfileEditor(id)
    if id then
        local profile = config.profiles[id]
        if profile then
            editing_profile_id = id
            imgui.Strncpy(inputs.profile_name, u8.encode(profile.name), 128)
            inputs.hours[0] = profile.h or 0
            inputs.minutes[0] = profile.m or 0
            inputs.seconds[0] = profile.s or 0
            imgui.Strncpy(inputs.message, u8.encode(profile.message or ""), 256)
        end
    else
        editing_profile_id = nil
        inputs.profile_name:fill(0)
        inputs.hours[0], inputs.minutes[0], inputs.seconds[0] = 0, 5, 0
        inputs.message:fill(0)
    end
    show_profile_editor = true
end

function saveOrUpdateProfile()
    local name = u8.decode(inputs.profile_name)
    if name == "" then return end

    local id = editing_profile_id or tostring(os.time() + math.random(1000))
    config.profiles[id] = {
        id = id,
        name = name,
        h = inputs.hours[0],
        m = inputs.minutes[0],
        s = inputs.seconds[0],
        message = u8.decode(inputs.message)
    }
    saveAllSettings()
    show_profile_editor = false
end

function deleteProfile(id)
    if not id then return end
    config.profiles[id] = nil
    saveAllSettings()
    show_profile_editor = false
end

---------------------------------------------------
--[[ 4. ГРАФИЧЕСКИЙ ИНТЕРФЕЙС (GUI) 🎨 ]]--
---------------------------------------------------

local ThemeManager = {
    themes = {
        Dark = function() imgui.SwitchContext() imgui.StyleColorsDark() end,
        Light = function() imgui.SwitchContext() imgui.StyleColorsLight() end,
        Classic = function() imgui.SwitchContext() imgui.StyleColorsClassic() end,
    },
    apply = function(themeName)
        if ThemeManager.themes[themeName] then
            ThemeManager.themes[themeName]()
        end
    end
}

function render_native_hud()
    if not customFont or next(active_timers) == nil then return end

    local y_offset = config.settings.hud.native_pos.y
    local x_pos = config.settings.hud.native_pos.x
    local color = tonumber("0xFF" .. config.settings.hud.font_color)

    for id, timer in pairs(active_timers) do
        local remaining
        if timer.paused then
            remaining = timer.remaining_on_pause
        else
            remaining = timer.duration - (os.time() - timer.start_time)
        end
        if remaining < 0 then remaining = 0 end

        local time_str = string.format("%02d:%02d:%02d", math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), remaining % 60)
        local text = u8.encode(("%s: %s %s"):format(timer.name, time_str, timer.paused and "[P]" or ""))
        
        local text_w = renderGetFontDrawTextWidth(customFont, text, false)
        local x = x_pos - (text_w / 2)

        if config.settings.hud.show_shadow then
            renderFontDrawText(customFont, text, x + 1, y_offset + 1, 0xFF000000)
        end
        renderFontDrawText(customFont, text, x, y_offset, color)
        
        y_offset = y_offset + config.settings.hud.font_size
    end
end

function render_imgui_hud()
    local s = config.settings.hud
    imgui.SetNextWindowPos(imgui.ImVec2(s.imgui_pos.x, s.imgui_pos.y), imgui.Cond.FirstUseEver)
    imgui.SetNextWindowSize(imgui.ImVec2(250, 0), imgui.Cond.Always)
    imgui.SetNextWindowBgAlpha(0.5)

    if imgui.Begin('Active Timers HUD', nil, imgui.WindowFlags.NoTitleBar + imgui.WindowFlags.NoCollapse) then
        s.imgui_pos.x, s.imgui_pos.y = imgui.GetWindowPos()
        for id, timer in pairs(active_timers) do
            local remaining
            if timer.paused then
                remaining = timer.remaining_on_pause
            else
                remaining = timer.duration - (os.time() - timer.start_time)
            end
            if remaining < 0 then remaining = 0 end

            local time_str = string.format("%02d:%02d:%02d", math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), remaining % 60)
            local elapsed = timer.duration - remaining

            imgui.Text(u8.encode(timer.name))
            imgui.SameLine()
            imgui.Text(time_str)
            imgui.ProgressBar(elapsed / timer.duration, imgui.ImVec2(-1, 0), timer.paused and "PAUSED" or "")
        end
        imgui.End()
    end
end

function render_profile_editor_popup()
    if show_profile_editor then
        imgui.OpenPopup('Редактор профиля')
    end

    imgui.SetNextWindowSize(imgui.ImVec2(400, 350), imgui.Cond.FirstUseEver)
    if imgui.BeginPopupModal('Редактор профиля', true, imgui.WindowFlags.NoResize) then
        imgui.Text("Название:")
        imgui.InputText('##ProfileName', inputs.profile_name, 128)
        imgui.Separator()
        imgui.Text("Длительность (Часы:Минуты:Секунды):")
        imgui.PushItemWidth(80)
        imgui.InputInt('##h', inputs.hours); imgui.SameLine()
        imgui.InputInt('##m', inputs.minutes); imgui.SameLine()
        imgui.InputInt('##s', inputs.seconds)
        imgui.PopItemWidth()
        imgui.Separator()
        imgui.Text("Сообщение по окончании (необязательно):")
        imgui.InputTextMultiline('##Message', inputs.message, 256, imgui.ImVec2(-1, 80))
        imgui.Separator()

        if imgui.Button('Сохранить', imgui.ImVec2(120, 0)) then saveOrUpdateProfile() end
        imgui.SameLine()
        if editing_profile_id and imgui.Button('Удалить', imgui.ImVec2(120, 0)) then deleteProfile(editing_profile_id) end
        
        imgui.SameLine(imgui.GetWindowWidth() - 80)
        if imgui.Button('Закрыть') then
            show_profile_editor = false
            imgui.CloseCurrentPopup()
        end

        imgui.EndPopup()
    end
end

imgui.OnFrame(
    function() return main_window_state[0] end,
    function(self)
        ThemeManager.apply(config.settings.theme)
        
        imgui.SetNextWindowPos(imgui.ImVec2(config.window.pos.x, config.window.pos.y), imgui.Cond.FirstUseEver)
        imgui.SetNextWindowSize(imgui.ImVec2(config.window.size.x, config.window.size.y), imgui.Cond.FirstUseEver)

        if imgui.Begin('TaimerPRO v1.2.1 by Arizona RP Хакер', main_window_state) then
            config.window.pos.x, config.window.pos.y = imgui.GetWindowPos()
            config.window.size.x, config.window.size.y = imgui.GetWindowSize()
            
            if imgui.BeginTabBar("MainTabBar") then
                if imgui.BeginTabItem('Запуск') then
                    if imgui.Button('Создать новый профиль') then openProfileEditor() end
                    imgui.Separator()
                    
                    imgui.Columns(2, "ProfileColumns", false)
                    imgui.SetColumnWidth(0, 250)
                    imgui.Text("Профиль"); imgui.NextColumn()
                    imgui.Text("Действия"); imgui.NextColumn()
                    imgui.Separator()

                    for id, profile in pairs(config.profiles) do
                        imgui.Text(u8.encode(profile.name)); imgui.NextColumn()
                        
                        local active_id = nil
                        for a_id, active in pairs(active_timers) do
                            if active.profile_id == id then active_id = a_id; break; end
                        end
                        
                        if active_id then
                            if imgui.Button('Стоп##'..id) then stopTimer(active_id) end
                        else
                            if imgui.Button('Старт##'..id) then startNewTimer(profile) end
                        end

                        imgui.SameLine()
                        if imgui.Button('Ред.##'..id) then openProfileEditor(id) end
                        imgui.NextColumn()
                    end
                    imgui.Columns(1)
                    imgui.EndTabItem()
                end

                if imgui.BeginTabItem('Настройки') then
                    imgui.Text("Настройки HUD"); imgui.Separator()
                    if imgui.RadioButton("Режим: Окно ImGui", config.settings.hud.mode == 1) then config.settings.hud.mode = 1 end
                    imgui.Hint("Классический HUD в виде полупрозрачного окна. Можно перетаскивать.")
                    if imgui.RadioButton("Режим: Нативный текст", config.settings.hud.mode == 2) then config.settings.hud.mode = 2 end
                    imgui.Hint("Минималистичный HUD в виде простого текста на экране.")
                    
                    if config.settings.hud.mode == 2 then
                        imgui.Separator()
                        imgui.Text("Настройки нативного текста:")
                        imgui.InputText("Шрифт##fontname", inputs.font_name, 128)
                        imgui.SliderInt("Размер##fontsize", inputs.font_size, 8, 72)
                        imgui.InputInt("Стиль (флаги)##fontflags", inputs.font_flags)
                        imgui.Hint("0-обычный, 1-жирный, 2-наклон, 4-подчеркнутый, 8-перечеркнутый. Можно суммировать (напр. 1+4=5 для жирного+подчеркнутого)")
                        if imgui.Checkbox("Тень у текста", imgui.new.bool(config.settings.hud.show_shadow)) then
                            config.settings.hud.show_shadow = not config.settings.hud.show_shadow
                        end
                        if imgui.Button("Применить шрифт") then fontUpdate(); saveAllSettings() end
                    end
                    
                    imgui.Separator()
                    imgui.Text("Интеграция с Telegram"); imgui.Separator()
                    if imgui.Checkbox("Включить уведомления", imgui.new.bool(config.settings.telegram.enabled)) then
                        config.settings.telegram.enabled = not config.settings.telegram.enabled
                    end
                    if imgui.Checkbox("Разрешить команды из Telegram", imgui.new.bool(config.settings.telegram.allow_commands)) then
                        config.settings.telegram.allow_commands = not config.settings.telegram.allow_commands
                    end
                    imgui.Text("Токен бота:"); imgui.InputText("##TGToken", inputs.tg_token, 256)
                    imgui.Text("Chat ID:"); imgui.InputText("##TGChatID", inputs.tg_chat_id, 128)
                    if imgui.Button("Сохранить все настройки") then saveAllSettings() end
                    imgui.EndTabItem()
                end

                imgui.EndTabBar()
            end
            
            render_profile_editor_popup()
            imgui.End()
        end
    end
)

---------------------------------------------------
--[[ 5. ДОПОЛНИТЕЛЬНЫЕ МОДУЛИ И ФУНКЦИИ ⭐ ]]--
---------------------------------------------------

function sendTelegramNotification(text)
    if not config.settings.telegram.enabled or not config.settings.telegram.token_b64 or config.settings.telegram.chat_id == "" then return end
    
    local token = b64.decode(config.settings.telegram.token_b64)
    local chat_id = config.settings.telegram.chat_id
    text = text:gsub(" ", "%%20"):gsub("\n", "%%0A")
    
    local url = string.format("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s", token, chat_id, text)
    
    effil.thread(function(request_url)
        pcall(https.request, request_url)
    end)(url)
end

function telegram_polling_thread()
    local token = b64.decode(config.settings.telegram.token_b64)
    if not token or token == "" then return end

    local initial_url = string.format("https://api.telegram.org/bot%s/getUpdates?offset=-1", token)
    local _, res = pcall(https.request, initial_url)
    if res then
        local data, _ = pcall(dkjson.decode, res)
        if data and data.ok and #data.result > 0 then
            last_telegram_update_id = data.result[#data.result].update_id
        end
    end

    while true do
        effil.sleep(2000)
        if config.settings.telegram.enabled and config.settings.telegram.allow_commands then
            local url = string.format("https://api.telegram.org/bot%s/getUpdates?offset=%d", token, last_telegram_update_id + 1)
            local _, response = pcall(https.request, url)

            if response then
                local data, _ = pcall(dkjson.decode, response)
                if data and data.ok and #data.result > 0 then
                    for _, update in ipairs(data.result) do
                        last_telegram_update_id = update.update_id
                        if update.message and update.message.text then
                            local text = u8.decode(update.message.text)
                            message_queue:push({ type = 'tg_command', text = text })
                        end
                    end
                end
            end
        else
            effil.sleep(5000)
        end
    end
end

sampev.onServerMessage = function(color, text)
    if not config.autostarts or next(config.autostarts) == nil then return end
    
    for id, autostart in pairs(config.autostarts) do
        if text:find(autostart.keyword) then
            local profile = config.profiles[autostart.profile_id]
            if profile then
                sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Обнаружен триггер '%s'. Запуск таймера '%s'."):format(autostart.keyword, profile.name), 0xFFA500)
                startNewTimer(profile)
            end
        end
    end
end

function onScriptTerminate()
    save_timers_state()
    saveAllSettings()
end

---------------------------------------------------
--[[ 6. ГЛАВНЫЙ ЦИКЛ И ИНИЦИАЛИЗАЦИЯ ]]--
---------------------------------------------------
function main()
    if not isSampLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end

    load_settings_on_startup()
    resume_persistent_timers()

    sampRegisterChatCommand('taimer', function()
        main_window_state[0] = not main_window_state[0]
        imgui.Process = main_window_state[0]
    end)
    
    if config.settings.telegram.enabled and config.settings.telegram.token_b64 ~= "" then
        effil.thread(telegram_polling_thread)()
    end

    sampAddChatMessage('{00BFFF}[TaimerPRO]{FFFFFF} Загружен. Версия ' .. script_version .. '. Активация: /taimer', 0x2ECC71)

    while true do
        wait(0)

        if next(active_timers) ~= nil then
            if config.settings.hud.mode == 1 then
                render_imgui_hud()
            elseif config.settings.hud.mode == 2 then
                render_native_hud()
            end
        end
        
        while true do
            local msg = message_queue:pop(0)
            if not msg then break end

            if msg.type == 'samp' then
                sampAddChatMessage(u8.encode(msg.text), 0xFFFFFF)

            elseif msg.type == 'telegram_send' then
                sendTelegramNotification(msg.text)

            elseif msg.type == 'timer_finished' or msg.type == 'timer_stopped' then
                if active_timers[msg.id] then
                    if active_timers[msg.id].thread:status() ~= 'dead' then
                       active_timers[msg.id].thread:detach()
                    end
                    active_timers[msg.id] = nil
                end

            elseif msg.type == 'tg_command' then
                local cmd, arg = msg.text:match("^/(%S+)%s*(.*)$")
                if cmd then
                    cmd = cmd:lower()
                    if cmd == 'status' then
                        local response = "Активные таймеры:\n"
                        if next(active_timers) == nil then
                            response = "Нет активных таймеров."
                        else
                            for id, timer in pairs(active_timers) do
                                local remaining = timer.duration - (os.time() - timer.start_time)
                                local time_str = string.format("%02d:%02d:%02d", math.floor(remaining/3600), math.floor(remaining/60)%60, remaining%60)
                                response = response .. string.format("- %s: осталось %s\n", timer.name, time_str)
                            end
                        end
                        message_queue:push({ type = 'telegram_send', text = response })

                    elseif cmd == 'profiles' then
                        local response = "Доступные профили:\n"
                        if next(config.profiles) == nil then
                           response = "У вас нет сохраненных профилей."
                        else
                           for _, p in pairs(config.profiles) do response = response .. "- " .. p.name .. "\n" end
                        end
                        message_queue:push({ type = 'telegram_send', text = response })
                    
                    elseif cmd == 'start' and arg ~= "" then
                        local found_profile = nil
                        for _, p in pairs(config.profiles) do
                            if p.name:lower() == arg:lower() then
                                found_profile = p
                                break
                            end
                        end
                        if found_profile then
                            startNewTimer(found_profile)
                            message_queue:push({ type = 'telegram_send', text = "Таймер '"..found_profile.name.."' запущен." })
                        else
                            message_queue:push({ type = 'telegram_send', text = "Профиль '"..arg.."' не найден." })
                        end

                    elseif cmd == 'stop' and arg ~= "" then
                         local found_timer_id = nil
                         for id, timer in pairs(active_timers) do
                            if timer.name:lower() == arg:lower() then
                                found_timer_id = id
                                break
                            end
                         end
                         if found_timer_id then
                            stopTimer(found_timer_id)
                            message_queue:push({ type = 'telegram_send', text = "Таймер '"..arg.."' остановлен." })
                         else
                            message_queue:push({ type = 'telegram_send', text = "Активный таймер '"..arg.."' не найден." })
                         end
                    end
                end
            end
        end
    end
end
 

chromiusj

модерирую шмодерирую
Модератор
5,983
4,296
нет никакой очереди в еффиле, перед подачей промпта нейросети можно и спросить вежливо, чтоб она проверила актуальность библиотеки