local DEFAULT_THROTTLE_SECONDS = 5; local DEFAULT_MAX_REPLY_CHARS = 160; local DEFAULT_PREFIX = 'TrapGPT'; local DEFAULT_SESSION_SETTING_KEY = 'trapgpt.opencc.session_id'; local SILENCE = 'SILENCE'; local function nowSeconds() if os.epoch then return os.epoch('utc') / 1000; end return os.clock(); end local function resolveNumber(value, defaultValue) local n = tonumber(value); if not n or n < 0 then return defaultValue; end return n; end local function trim(s) return tostring(s or ''):gsub('^%s+', ''):gsub('%s+$', ''); end local function truncate(s, maxChars) s = trim(s); if #s <= maxChars then return s; end if maxChars <= 3 then return string.sub(s, 1, maxChars); end return string.sub(s, 1, maxChars - 3) .. '...'; end local function formatChatLine(message) local at = message.at and ('@' .. tostring(math.floor(message.at)) .. ' ') or ''; return at .. tostring(message.username or '?') .. ': ' .. tostring(message.text or ''); end local function buildPrompt(messages, firstBatch, maxReplyChars) local lines = { 'Tu es TrapGPT dans le chat Minecraft.', 'Reponds seulement si utile.', 'Reponse tres concise: une phrase courte, maximum ' .. tostring(maxReplyChars) .. ' caracteres.', 'Pas de markdown. Ne repete pas l historique.', 'Si aucune reponse utile, reponds exactement: ' .. SILENCE, }; if firstBatch then lines[#lines + 1] = 'Contexte initial: voici les premiers messages recus.'; end lines[#lines + 1] = ''; lines[#lines + 1] = 'Nouveaux messages chat depuis le dernier envoi:'; for _, message in ipairs(messages) do lines[#lines + 1] = formatChatLine(message); end return table.concat(lines, '\n'); end local function createTrapGpt(opts) opts = opts or {}; local settingsLib = opts.settings or settings; local nowFunc = opts.now or nowSeconds; local sleepFunc = opts.sleep or sleep; local ai = opts.ai or require('/apis/libai')(); local chatBox = opts.chatBox; local log = opts.log or print; local api = {}; local history = {}; local sentIndex = 0; local firstBatch = true; local lastSendAt = 0; local active = false; local stopped = false; local function throttleSeconds() return resolveNumber(settingsLib.get('trapgpt.throttle_seconds'), DEFAULT_THROTTLE_SECONDS); end local function maxReplyChars() return math.max(1, resolveNumber(settingsLib.get('trapgpt.max_reply_chars'), DEFAULT_MAX_REPLY_CHARS)); end local function prefix() local value = settingsLib.get('trapgpt.prefix'); if type(value) ~= 'string' or value == '' then return DEFAULT_PREFIX; end return value; end local function queuedMessages() local messages = {}; for i = sentIndex + 1, #history do messages[#messages + 1] = history[i]; end return messages; end local function shouldIgnore(username, text, isHidden) if isHidden then return true; end if type(text) ~= 'string' or trim(text) == '' then return true; end if type(username) == 'string' and username == prefix() then return true; end return false; end function api.onChat(username, message, uuid, isHidden, messageUtf8) local text = messageUtf8 or message; if shouldIgnore(username, text, isHidden) then return false; end history[#history + 1] = { username = username, text = text, uuid = uuid, at = nowFunc(), }; return true; end function api.pendingCount() return #history - sentIndex; end function api.history() return history; end function api.buildPrompt(messages) return buildPrompt(messages, firstBatch, maxReplyChars()); end function api.processOnce() if active or api.pendingCount() <= 0 then return false; end local waitSeconds = throttleSeconds() - (nowFunc() - lastSendAt); if waitSeconds > 0 then sleepFunc(waitSeconds); end local startIndex = sentIndex + 1; local messages = queuedMessages(); if #messages == 0 then return false; end active = true; local ok, result = ai.ask(buildPrompt(messages, firstBatch, maxReplyChars()), { sessionSettingKey = DEFAULT_SESSION_SETTING_KEY, sessionTitle = 'trapgpt', }); active = false; lastSendAt = nowFunc(); if not ok then log('trapgpt ai error: ' .. tostring(result)); return false; end sentIndex = startIndex + #messages - 1; firstBatch = false; local reply = truncate(result.reply, maxReplyChars()); if reply == '' or reply == SILENCE then return true; end if chatBox then local sent, err = chatBox.sendMessage(reply, prefix()); if not sent then log('trapgpt chat error: ' .. tostring(err)); end end return true, reply; end function api.stop() stopped = true; end function api.run() while not stopped do if api.pendingCount() > 0 then api.processOnce(); else sleepFunc(0.25); end end end return api; end return createTrapGpt;