'use strict'; const config = require('config'); const bhttp = require('bhttp'); const style = require('../utils/style'); const promptRegex = new RegExp(`^${config.usernamePrefix}?${config.user.username}[:,\\s]`, 'ui'); const history = new Map(); const settings = config.chat; function setHistory(value, context) { if (!value) { context.sendMessage(`Chat history is set to ${style.bold(settings.history)}`, context.room.id, { label: false }); return; } const newHistory = Number(value); if (!Number.isNaN(newHistory)) { settings.history = newHistory; context.logger.info(`Chat history set to ${newHistory} by ${context.user.username}`); context.sendMessage(`Chat history set to ${style.bold(newHistory)} by ${context.user.prefixedUsername}`, context.room.id, { label: false }); return; } context.logger.info(`Chat history must be a valid number, ${context.user.prefixedUsername}`); } function setModel(model, context) { if (!model) { context.sendMessage(`Chat model is set to ${style.bold(settings.model)}`, context.room.id, { label: false }); return; } if (config.chat.validModels.includes(model)) { settings.model = model; context.logger.info(`Chat model set to '${model}' by ${context.user.username}`); context.sendMessage(`Chat model set to '${style.bold(model)}' by ${context.user.prefixedUsername}`, context.room.id, { label: false }); return; } context.logger.info(`Model '${model}' is not supported right now, ${context.user.prefixedUsername}`); } function setRule(rule, context) { if (!rule) { context.sendMessage(`Chat is ${style.bold(settings.rule)}`, context.room.id, { label: false }); return; } if (rule === 'reset') { settings.rule = config.chat.rule; context.logger.info(`Chat reset by ${context.user.username} to be ${config.chat.rule}`); context.sendMessage(`Chat reset by ${context.user.prefixedUsername} to be ${style.bold(config.chat.rule)}`, context.room.id, { label: false }); return; } if (rule.length > 3) { settings.rule = `${rule.replace(/\.+$/, '')}`; context.logger.info(`Chat set by ${context.user.username} to be ${rule}`); context.sendMessage(`Chat set by ${context.user.prefixedUsername} to be ${style.bold(rule)}`, context.room.id, { label: false }); return; } context.sendMessage(`Chat rule must be at least 3 characters long, ${context.user.prefixedUsername}`, context.room.id, { label: false }); } async function onCommand(args, context) { if (context.subcommand === 'history' && config.operators.includes(context.user.username)) { setHistory(args[0], context); return; } if (context.subcommand === 'model' && config.operators.includes(context.user.username)) { setModel(args[0], context); return; } if (['rule', 'is', 'reset'].includes(context.subcommand) && (config.chat.rulePublic || config.operators.includes(context.user.username))) { setRule(context.subcommand === 'reset' ? 'reset' : args.join(' '), context); return; } const prompt = args.join(' '); try { const message = { role: 'user', content: `Answer as if you're ${settings.rule}. ${prompt}`, }; const userHistory = (history.get(context.user.username) || []).concat(message); context.logger.info(`${context.user.username} asked OpenAI '${config.chat.model}' (${userHistory.length}): ${message.content}`); const res = await bhttp.post('https://api.openai.com/v1/chat/completions', JSON.stringify({ model: settings.model, messages: userHistory, }), { headers: { Authorization: `Bearer ${config.chat.apiKey}`, 'Content-Type': 'application/json', }, }); const reply = res.body.choices?.[0].message; context.logger.info(`OpenAI ${config.chat.model} replied to ${context.user.username} (${res.body.usage.total_tokens} tokens): ${reply.content}`); context.sendMessage(`${context.user.prefixedUsername}: ${reply.content}`, context.room.id, { label: false }); if (config.chat.history > 0) { history.set(context.user.username, userHistory.concat(reply).slice(-settings.history)); } } catch (error) { context.logger.error(error.response ? `${error.response.status}: ${JSON.stringify(error.response.data)}` : error.message); context.sendMessage('Sorry, I can\'t think right now', context.room.id, { label: false }); } } function onMessage(message, context) { if (promptRegex.test(message.body)) { onCommand([message.body.replace(promptRegex, '').trim()], context); } } module.exports = { onCommand, onMessage, };