179 lines
4.9 KiB
Lua
179 lines
4.9 KiB
Lua
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;
|