Adding Groq access
This commit is contained in:
parent
335dcda79d
commit
317ef0dd5f
241
backend-server/groq-backend.js
Normal file
241
backend-server/groq-backend.js
Normal file
@ -0,0 +1,241 @@
|
||||
import 'dotenv/config';
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import cmd from 'cmd-promise';
|
||||
import cors from 'cors';
|
||||
import cheerio from 'cheerio';
|
||||
import llamaTokenizer from 'llama-tokenizer-js';
|
||||
import googleIt from 'google-it';
|
||||
import Groq from 'groq-sdk';
|
||||
|
||||
// Constants and initialization
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
const prompt = process.env.PROMPT;
|
||||
|
||||
const groq = new Groq({ apiKey: process.env.GROQ });
|
||||
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
allowedHeaders: ['Content-Type', 'x-forwarded-for-id', 'x-forwarded-for-name']
|
||||
}));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
let isProcessing = false;
|
||||
let conversationHistory = {};
|
||||
|
||||
// Helper function to get current timestamp
|
||||
const getTimestamp = () => {
|
||||
const now = new Date();
|
||||
const date = now.toLocaleDateString('en-US');
|
||||
const time = now.toLocaleTimeString('en-US');
|
||||
return `${date} [${time}]`;
|
||||
};
|
||||
|
||||
// Middleware to track conversation history by CF-Connecting-IP
|
||||
app.use((req, res, next) => {
|
||||
const ip = req.headers['x-forwarded-for-id'] || req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.ip;
|
||||
const name = req.headers['x-forwarded-for-name'];
|
||||
const guild = req.headers['x-forwarded-for-guild'];
|
||||
|
||||
req.clientIp = ip; // Store the IP in a request property
|
||||
|
||||
if (!conversationHistory[req.clientIp]) {
|
||||
console.log(`${getTimestamp()} [INFO] Initializing conversation history for: ${req.clientIp}`);
|
||||
conversationHistory[req.clientIp] = [{
|
||||
role: 'system',
|
||||
content: `My name is: ${name || 'Unknown'}, my Discord ID is: ${req.clientIp}.` + (guild ? ` We are chatting inside ${guild} a Discord Server.` : '') + prompt
|
||||
}];
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
function countLlamaTokens(messages) {
|
||||
let totalTokens = 0;
|
||||
for (const message of messages) {
|
||||
if (message.role === 'user' || message.role === 'assistant') {
|
||||
const encodedTokens = llamaTokenizer.encode(message.content);
|
||||
totalTokens += encodedTokens.length;
|
||||
}
|
||||
}
|
||||
return totalTokens;
|
||||
}
|
||||
|
||||
function trimConversationHistory(ip, maxLength = 14000, tolerance = 25) {
|
||||
const messages = conversationHistory[ip];
|
||||
let totalTokens = countLlamaTokens(messages);
|
||||
|
||||
while (totalTokens > maxLength + tolerance && messages.length > 1) {
|
||||
messages.shift(); // Remove the oldest messages first
|
||||
totalTokens = countLlamaTokens(messages);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to scrape web page
|
||||
async function scrapeWebPage(url, length = 2000) {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const html = await res.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const pageTitle = $('head title').text().trim();
|
||||
const pageDescription = $('head meta[name="description"]').attr('content');
|
||||
let plainTextContent = $('body').text().trim().replace(/[\r\n\t]+/g, ' ');
|
||||
|
||||
if (plainTextContent.length > length) {
|
||||
plainTextContent = plainTextContent.substring(0, length) + '...';
|
||||
}
|
||||
|
||||
return `Title: ${pageTitle}\nDescription: ${pageDescription || 'N/A'}\nContent: ${plainTextContent}\nURL: ${url}`;
|
||||
} catch (err) {
|
||||
console.error(`${getTimestamp()} [ERROR] Failed to scrape URL: ${url}`, err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle IP plugin
|
||||
async function handleIPPlugin(ipAddr, ip, conversationHistory) {
|
||||
try {
|
||||
const url = new URL('https://api.abuseipdb.com/api/v2/check');
|
||||
url.searchParams.append('ipAddress', ipAddr);
|
||||
url.searchParams.append('maxAgeInDays', '90');
|
||||
url.searchParams.append('verbose', '');
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Key': process.env.ABUSE_KEY,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
|
||||
let abuseResponse = `IP: ${ipAddr}\n`;
|
||||
abuseResponse += `Abuse Score: ${data.data.abuseConfidenceScore}\n`;
|
||||
abuseResponse += `Country: ${data.data.countryCode}\n`;
|
||||
abuseResponse += `Usage Type: ${data.data.usageType}\n`;
|
||||
abuseResponse += `ISP: ${data.data.isp}\n`;
|
||||
abuseResponse += `Domain: ${data.data.domain}\n`;
|
||||
|
||||
if (data.data.totalReports) {
|
||||
abuseResponse += `Total Reports: ${data.data.totalReports}\n`;
|
||||
abuseResponse += `Last Reported: ${data.data.lastReportedAt}\n`;
|
||||
}
|
||||
|
||||
const lastMessageIndex = conversationHistory[ip].length - 1;
|
||||
if (lastMessageIndex >= 0) {
|
||||
conversationHistory[ip][lastMessageIndex].content += "\n" + abuseResponse;
|
||||
console.log(`${getTimestamp()} [INFO] Processed IP address: ${ipAddr}, response: ${abuseResponse}`);
|
||||
} else {
|
||||
console.error(`${getTimestamp()} [ERROR] Conversation history is unexpectedly empty for: ${ip}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`${getTimestamp()} [ERROR] Failed to process IP address: ${ipAddr}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Main chat handler
|
||||
app.post('/api/v1/chat', async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
const ip = req.clientIp;
|
||||
|
||||
// if (isProcessing) {
|
||||
// return res.status(429).json({ message: "System is busy processing another request, try again later" });
|
||||
// }
|
||||
|
||||
isProcessing = true;
|
||||
|
||||
try {
|
||||
const userMessage = req.body.message + `\nDate/Time: ${getTimestamp()}`;
|
||||
conversationHistory[ip].push({ role: 'user', content: userMessage });
|
||||
trimConversationHistory(ip);
|
||||
|
||||
const pluginTasks = [];
|
||||
|
||||
// Check for IPs in user message and process them
|
||||
const ipRegex = /(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)/g;
|
||||
const ipAddresses = userMessage.match(ipRegex);
|
||||
if (ipAddresses) {
|
||||
for (const ipAddr of ipAddresses) {
|
||||
pluginTasks.push(handleIPPlugin(ipAddr, ip, conversationHistory));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for URLs and scrape them
|
||||
const urls = userMessage.match(/(https?:\/\/[^\s]+)/g);
|
||||
if (urls) {
|
||||
for (const url of urls) {
|
||||
pluginTasks.push(scrapeWebPage(url).then(content => {
|
||||
if (content) {
|
||||
conversationHistory[ip].push({ role: 'assistant', content });
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(pluginTasks);
|
||||
|
||||
const completion = await groq.chat.completions.create({
|
||||
messages: conversationHistory[ip],
|
||||
//model: "llama3-8b-8192"
|
||||
model: "llama-3.2-3b-preview"
|
||||
});
|
||||
|
||||
const assistantMessage = completion.choices[0].message.content;
|
||||
conversationHistory[ip].push({ role: 'assistant', content: assistantMessage });
|
||||
|
||||
res.json(assistantMessage);
|
||||
} catch (error) {
|
||||
console.error(`${getTimestamp()} [ERROR] An error occurred`, error);
|
||||
res.status(500).json({ message: "An error occurred", error: error.message });
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
const endTime = Date.now();
|
||||
const processingTime = ((endTime - startTime) / 1000).toFixed(2);
|
||||
console.log(`${getTimestamp()} [STATS] Processing Time: ${processingTime} seconds`);
|
||||
}
|
||||
});
|
||||
|
||||
// Endpoint to restart core service
|
||||
app.post('/api/v1/restart-core', (req, res) => {
|
||||
console.log(`${getTimestamp()} [INFO] Restarting core service`);
|
||||
cmd(`docker restart llama-gpu-server`).then(out => {
|
||||
console.log(`${getTimestamp()} [INFO] Core service restarted`);
|
||||
res.json(out.stdout);
|
||||
}).catch(err => {
|
||||
console.error(`${getTimestamp()} [ERROR] Failed to restart core service`, err);
|
||||
res.status(500).json({
|
||||
message: "An error occurred while restarting the core service",
|
||||
error: err.message
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Endpoint to reset conversation history
|
||||
app.post('/api/v1/reset-conversation', (req, res) => {
|
||||
const ip = req.clientIp;
|
||||
console.log(`${getTimestamp()} [INFO] Resetting conversation history for: ${ip}`);
|
||||
|
||||
conversationHistory[ip] = [{
|
||||
role: 'system',
|
||||
content: prompt
|
||||
}];
|
||||
|
||||
console.log(`${getTimestamp()} [INFO] Conversation history reset for: ${ip}`);
|
||||
res.json({ message: "Conversation history reset for: " + ip });
|
||||
});
|
||||
|
||||
// Get conversation history for debugging purposes
|
||||
app.get('/api/v1/conversation-history', (req, res) => {
|
||||
const ip = req.clientIp;
|
||||
console.log(`${getTimestamp()} [INFO] Fetching conversation history for: ${ip}`);
|
||||
res.json(conversationHistory[ip]);
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(port, () => {
|
||||
console.log(`${getTimestamp()} [INFO] Server running at http://localhost:${port}`);
|
||||
});
|
@ -17,6 +17,7 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"google-it": "^1.6.4",
|
||||
"groq-sdk": "^0.7.0",
|
||||
"llama-tokenizer-js": "^1.2.2"
|
||||
}
|
||||
}
|
||||
|
200
bot/discord-bot-groq.js
Normal file
200
bot/discord-bot-groq.js
Normal file
@ -0,0 +1,200 @@
|
||||
const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');
|
||||
const axios = require('axios');
|
||||
const he = require('he');
|
||||
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
|
||||
const { userResetMessages } = require('./assets/messages.js');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const channelIDs = process.env.CHANNEL_IDS.split(',');
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
|
||||
});
|
||||
|
||||
const MAX_CONTENT_LENGTH = process.env.MAX_CONTENT_LENGTH || 8000;
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
});
|
||||
|
||||
client.on('messageCreate', async message => {
|
||||
if (message.guildId == "1192181551526584380") return
|
||||
if (message.content.includes("[LOG]")) return
|
||||
console.log(`Received message: ${message.content}, from ${message.author.tag}, in channel: ${message.channel.name}`);
|
||||
console.log(message.guildId)
|
||||
|
||||
// Function to send a random message from any array
|
||||
async function sendRand(array) {
|
||||
const arrayChoice = array[Math.floor(Math.random() * array.length)];
|
||||
console.log(`Sending random response: ${arrayChoice}`);
|
||||
await message.channel.send(arrayChoice); // give a notification of reset using a human-like response.
|
||||
}
|
||||
|
||||
if (message.author.bot) return;
|
||||
|
||||
// Only respond in the specified channels
|
||||
if (!channelIDs.includes(message.channel.id)) {
|
||||
console.log(`Ignoring message from channel: ${message.channel.id}, not in the specified channels.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = message.content.trim();
|
||||
let additionalContent = '';
|
||||
|
||||
if (content === '!r' || content === '!reset') {
|
||||
console.log("Reset command received.");
|
||||
await resetConversation(message);
|
||||
// Handle conversation reset
|
||||
return await sendRand(userResetMessages);
|
||||
}
|
||||
if (content === '!restartCore') {
|
||||
console.log("Restart core command received.");
|
||||
// Handle core restart
|
||||
return await restartCore(message);
|
||||
}
|
||||
|
||||
console.log(`Handling user message: ${content}`);
|
||||
await handleUserMessage(message, content, additionalContent);
|
||||
});
|
||||
|
||||
|
||||
async function handleUserMessage(message, content, additionalContent) {
|
||||
const encodedMessage = he.encode(content + additionalContent);
|
||||
console.log(`Encoded message: ${encodedMessage}`);
|
||||
|
||||
const typingInterval = setInterval(() => {
|
||||
message.channel.sendTyping();
|
||||
}, 9000);
|
||||
message.channel.sendTyping(); // Initial typing indicator
|
||||
|
||||
try {
|
||||
console.log(`Sending message to API at: http://${process.env.ROOT_IP}:${process.env.ROOT_PORT}/api/v1/chat`);
|
||||
const response = await axios.post(`http://${process.env.ROOT_IP}:${process.env.ROOT_PORT}/api/v1/chat`, {
|
||||
message: encodedMessage,
|
||||
max_tokens: Number(process.env.MAX_TOKENS),
|
||||
repeat_penalty: Number(process.env.REPEAT_PENALTY)
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for-id': message.author.id,
|
||||
'x-forwarded-for-name': message.author.username,
|
||||
'x-forwarded-for-guild': message.guild.name
|
||||
}
|
||||
});
|
||||
|
||||
console.log("API response received:", response.data);
|
||||
|
||||
// If response.data is a string (no 'content' property), use it directly
|
||||
const responseText = typeof response.data === 'string' ? response.data : response.data.content;
|
||||
|
||||
if (!responseText) {
|
||||
console.error("No content found in API response.");
|
||||
return message.channel.send("Oops, something went wrong. No response content.");
|
||||
}
|
||||
|
||||
clearInterval(typingInterval); // Stop typing indicator
|
||||
await sendLongMessage(message, responseText);
|
||||
} catch (error) {
|
||||
console.error("Error during API call:", error.message);
|
||||
clearInterval(typingInterval); // Stop typing indicator
|
||||
if (error.response && error.response.status === 429) {
|
||||
console.log("API rate limited, trying to send DM.");
|
||||
try {
|
||||
await message.author.send('I am currently busy. Please try again later.');
|
||||
} catch (dmError) {
|
||||
console.error('Failed to send DM:', dmError);
|
||||
message.reply('I am currently busy. Please try again later.');
|
||||
}
|
||||
} else {
|
||||
message.reply('Error: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function resetConversation(message) {
|
||||
try {
|
||||
console.log("Resetting conversation...");
|
||||
const response = await axios.post(
|
||||
`${process.env.API_PATH}/reset-conversation`, {}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for-id': message.author.id,
|
||||
'x-forwarded-for-name': message.author.username,
|
||||
'x-forwarded-for-guild': message.guild.name
|
||||
}
|
||||
}
|
||||
);
|
||||
console.log(`Reset conversation status: ${response.status}`);
|
||||
} catch (error) {
|
||||
console.error("Error during reset conversation:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function restartCore(message) {
|
||||
try {
|
||||
console.log("Restarting core...");
|
||||
const response = await axios.post(`${process.env.API_PATH}/restart-core`);
|
||||
console.log(`Core restart status: ${response.status}`);
|
||||
} catch (error) {
|
||||
console.error("Error during core restart:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendLongMessage(message, responseText) {
|
||||
console.log(`Preparing to send response. Length: ${responseText ? responseText.length : 'undefined'}`);
|
||||
|
||||
const limit = 8096;
|
||||
|
||||
if (!responseText) {
|
||||
console.error("Response text is undefined or null.");
|
||||
return message.channel.send("Oops, I didn't get any response. Please try again.");
|
||||
}
|
||||
|
||||
if (responseText.length > limit) {
|
||||
console.log("Response too long, splitting into chunks...");
|
||||
const lines = responseText.split('\n');
|
||||
const chunks = [];
|
||||
let currentChunk = '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (currentChunk.length + line.length > limit) {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = '';
|
||||
}
|
||||
currentChunk += line + '\n';
|
||||
}
|
||||
|
||||
if (currentChunk.trim() !== '') {
|
||||
chunks.push(currentChunk.trim());
|
||||
}
|
||||
|
||||
if (chunks.length >= 80) return await message.channel.send("Response chunks too large. Try again");
|
||||
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
const chunk = chunks[i];
|
||||
const embed = new EmbedBuilder()
|
||||
.setDescription(chunk) // Wraps the chunk in a code block
|
||||
.setColor("#3498DB")
|
||||
.setTimestamp();
|
||||
console.log(`Sending chunk ${i + 1}/${chunks.length}`);
|
||||
setTimeout(() => {
|
||||
message.channel.send({
|
||||
embeds: [embed]
|
||||
});
|
||||
}, i * (process.env.OVERFLOW_DELAY || 3) * 1000);
|
||||
}
|
||||
} else {
|
||||
console.log("Response fits within limit, sending single message.");
|
||||
const embed = new EmbedBuilder()
|
||||
.setDescription(responseText) // Wraps the response in a code block
|
||||
.setColor("#3498DB")
|
||||
.setTimestamp();
|
||||
message.channel.send({
|
||||
embeds: [embed]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
client.login(process.env.THE_TOKEN);
|
302
bot/installableApp-groq.js
Normal file
302
bot/installableApp-groq.js
Normal file
@ -0,0 +1,302 @@
|
||||
const { Client, GatewayIntentBits, REST, Routes, EmbedBuilder, SlashCommandBuilder, AttachmentBuilder } = require('discord.js');
|
||||
const axios = require('axios');
|
||||
const he = require('he');
|
||||
const fs = require('fs');
|
||||
const PDFDocument = require('pdfkit');
|
||||
require('dotenv').config();
|
||||
const markdownIt = require('markdown-it');
|
||||
const puppeteer = require('puppeteer');
|
||||
const highlight = require('highlight.js');
|
||||
|
||||
const md = new markdownIt({
|
||||
highlight: function (str, lang) {
|
||||
if (lang && highlight.getLanguage(lang)) {
|
||||
try {
|
||||
return highlight.highlight(str, { language: lang }).value;
|
||||
} catch (__) {}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds]
|
||||
});
|
||||
|
||||
const userPrivacyFilePath = './userPrivacySettings.json';
|
||||
let userPrivacySettings = {};
|
||||
if (fs.existsSync(userPrivacyFilePath)) {
|
||||
userPrivacySettings = JSON.parse(fs.readFileSync(userPrivacyFilePath));
|
||||
}
|
||||
|
||||
function saveUserPrivacySettings() {
|
||||
fs.writeFileSync(userPrivacyFilePath, JSON.stringify(userPrivacySettings, null, 2));
|
||||
}
|
||||
|
||||
// Check if the user's responses should be ephemeral
|
||||
function isEphemeral(userId) {
|
||||
return userPrivacySettings[userId] || false; // Default to false (standard response) if not set
|
||||
}
|
||||
|
||||
let userLastInteraction = {};
|
||||
let conversationHistories = {};
|
||||
|
||||
async function resetConversationIfExpired(userId) {
|
||||
const now = Date.now();
|
||||
if (userLastInteraction[userId] && now - userLastInteraction[userId] >= 3600000) {
|
||||
try {
|
||||
const response = await axios.post(`${process.env.API_PATH}/reset-conversation`, {}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for-id': userId,
|
||||
}
|
||||
});
|
||||
console.log(`Conversation history reset for user ${userId} due to inactivity. Status: ${response.status}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to reset conversation for user ${userId}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateUserInteractionTime(userId) {
|
||||
userLastInteraction[userId] = Date.now();
|
||||
}
|
||||
|
||||
// Create the commands
|
||||
const commands = [
|
||||
new SlashCommandBuilder().setName('reset').setDescription('Reset the conversation'),
|
||||
new SlashCommandBuilder().setName('restartcore').setDescription('Restart the core service'),
|
||||
new SlashCommandBuilder().setName('chat').setDescription('Send a chat message')
|
||||
.addStringOption(option =>
|
||||
option.setName('message')
|
||||
.setDescription('Message to send')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder().setName('privacy').setDescription('Toggle between ephemeral and standard responses'),
|
||||
new SlashCommandBuilder().setName('pdf').setDescription('Download conversation history as a PDF')
|
||||
];
|
||||
|
||||
// Add integration types and contexts to each command
|
||||
const commandData = commands.map(command => {
|
||||
const commandJSON = command.toJSON();
|
||||
const extras = {
|
||||
"integration_types": [0, 1], // 0 for guilds, 1 for user apps
|
||||
"contexts": [0, 1, 2] // 0 for guilds, 1 for app DMs, 2 for group DMs
|
||||
};
|
||||
|
||||
// Merge extras into command JSON
|
||||
Object.assign(commandJSON, extras);
|
||||
return commandJSON;
|
||||
});
|
||||
|
||||
const rest = new REST({ version: '10' }).setToken(process.env.THE_TOKEN_2);
|
||||
|
||||
client.once('ready', async () => {
|
||||
try {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
|
||||
// Register the commands with Discord
|
||||
const data = await rest.put(
|
||||
Routes.applicationCommands(process.env.DISCORD_CLIENT_ID),
|
||||
{ body: commandData } // Send the array of commands with integration types and contexts
|
||||
);
|
||||
|
||||
console.log('Successfully registered application commands.');
|
||||
} catch (error) {
|
||||
console.error('Error registering commands: ', error);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (!interaction.isCommand()) return;
|
||||
|
||||
const { commandName, options } = interaction;
|
||||
const userId = interaction.user.id;
|
||||
|
||||
updateUserInteractionTime(userId);
|
||||
await resetConversationIfExpired(userId);
|
||||
|
||||
if (commandName === 'reset') {
|
||||
await resetConversation(interaction);
|
||||
} else if (commandName === 'restartcore') {
|
||||
await restartCore(interaction);
|
||||
} else if (commandName === 'chat') {
|
||||
const content = options.getString('message');
|
||||
await handleUserMessage(interaction, content);
|
||||
} else if (commandName === 'privacy') {
|
||||
await togglePrivacy(interaction);
|
||||
} else if (commandName === 'pdf') {
|
||||
try {
|
||||
await interaction.deferReply({ ephemeral: isEphemeral(interaction.user.id) });
|
||||
await generatePDF(interaction);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate PDF:', error);
|
||||
await interaction.editReply({ content: 'Failed to generate PDF.' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function handleUserMessage(interaction, content) {
|
||||
const encodedMessage = he.encode(content);
|
||||
|
||||
// Start typing indicator
|
||||
await interaction.deferReply({ ephemeral: isEphemeral(interaction.user.id) });
|
||||
|
||||
try {
|
||||
// Making the API call and expecting the plain text response
|
||||
const response = await axios.post(`http://${process.env.ROOT_IP}:${process.env.ROOT_PORT}/api/v1/chat`, {
|
||||
message: encodedMessage,
|
||||
max_tokens: Number(process.env.MAX_TOKENS),
|
||||
repeat_penalty: Number(process.env.REPEAT_PENALTY)
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for-id': interaction.user.id,
|
||||
'x-forwarded-for-name': interaction.user.username
|
||||
}
|
||||
});
|
||||
|
||||
// Since response is expected to be plain text
|
||||
const responseText = response.data || 'Oops, something went wrong. No content received.';
|
||||
|
||||
await sendLongMessage(interaction, responseText);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 429) {
|
||||
try {
|
||||
await interaction.editReply({ content: 'I am currently busy. Please try again later.', ephemeral: isEphemeral(interaction.user.id) });
|
||||
} catch (dmError) {
|
||||
console.error('Failed to send DM:', dmError);
|
||||
interaction.editReply({ content: 'I am currently busy. Please try again later.', ephemeral: isEphemeral(interaction.user.id) });
|
||||
}
|
||||
} else {
|
||||
interaction.editReply({ content: 'Error: ' + error.message, ephemeral: isEphemeral(interaction.user.id) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendLongMessage(interaction, responseText) {
|
||||
const limit = 8096;
|
||||
|
||||
if (responseText.length > limit) {
|
||||
const lines = responseText.split('\n');
|
||||
const chunks = [];
|
||||
let currentChunk = '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (currentChunk.length + line.length > limit) {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = '';
|
||||
}
|
||||
currentChunk += line + '\n';
|
||||
}
|
||||
|
||||
if (currentChunk.trim() !== '') {
|
||||
chunks.push(currentChunk.trim());
|
||||
}
|
||||
|
||||
if (chunks.length >= 80) return await interaction.reply({ content: "Response chunks too large. Try again", ephemeral: isEphemeral(interaction.user.id) });
|
||||
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
const chunk = chunks[i];
|
||||
const embed = new EmbedBuilder()
|
||||
.setDescription(chunk)
|
||||
.setColor("#3498DB")
|
||||
.setTimestamp();
|
||||
|
||||
setTimeout(() => {
|
||||
interaction.followUp({
|
||||
embeds: [embed],
|
||||
ephemeral: isEphemeral(interaction.user.id)
|
||||
});
|
||||
}, i * (process.env.OVERFLOW_DELAY || 3) * 1000);
|
||||
}
|
||||
} else {
|
||||
const embed = new EmbedBuilder()
|
||||
.setDescription(responseText)
|
||||
.setColor("#3498DB")
|
||||
.setTimestamp();
|
||||
|
||||
interaction.editReply({
|
||||
embeds: [embed],
|
||||
ephemeral: isEphemeral(interaction.user.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function resetConversation(interaction) {
|
||||
try {
|
||||
const userId = interaction.user.id;
|
||||
conversationHistories[userId] = []; // Reset conversation history for the user
|
||||
const response = await axios.post(`${process.env.API_PATH}/reset-conversation`, {}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for-id': interaction.user.id,
|
||||
'x-forwarded-for-name': interaction.user.username
|
||||
}
|
||||
});
|
||||
console.log(response.status);
|
||||
interaction.reply({ content: 'Conversation reset successfully.', ephemeral: isEphemeral(interaction.user.id) });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
interaction.reply({ content: 'Failed to reset the conversation.', ephemeral: isEphemeral(interaction.user.id) });
|
||||
}
|
||||
}
|
||||
|
||||
async function generatePDF(interaction) {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.ROOT_IP}:${process.env.ROOT_PORT}/api/v1/conversation-history`, {
|
||||
headers: {
|
||||
'x-forwarded-for-id': interaction.user.id
|
||||
}
|
||||
});
|
||||
|
||||
const conversationHistory = response.data;
|
||||
|
||||
const filteredHistory = conversationHistory.filter(message => message.role !== 'system');
|
||||
|
||||
let htmlContent = `
|
||||
<html>
|
||||
<head>
|
||||
<link id="highlightjs-style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/atom-one-dark.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; }
|
||||
pre { background-color: #f4f4f4; padding: 10px; border-radius: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body><h1>Conversation History</h1>`;
|
||||
|
||||
filteredHistory.forEach(message => {
|
||||
const role = message.role.charAt(0).toUpperCase() + message.role.slice(1);
|
||||
const content = he.decode(message.content);
|
||||
const renderedMarkdown = md.render(content);
|
||||
|
||||
htmlContent += `<h3>${role}:</h3>`;
|
||||
htmlContent += `<div>${renderedMarkdown}</div>`;
|
||||
});
|
||||
|
||||
htmlContent += `</body></html>`;
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setContent(htmlContent);
|
||||
const pdfPath = `./conversation_${interaction.user.id}.pdf`;
|
||||
await page.pdf({ path: pdfPath, format: 'A4' });
|
||||
|
||||
await browser.close();
|
||||
|
||||
const attachment = new AttachmentBuilder(pdfPath);
|
||||
await interaction.editReply({
|
||||
content: 'Here is your conversation history in PDF format:',
|
||||
files: [attachment],
|
||||
ephemeral: isEphemeral(interaction.user.id)
|
||||
});
|
||||
|
||||
fs.unlinkSync(pdfPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate PDF:', error);
|
||||
await interaction.reply({ content: 'Failed to generate PDF.', ephemeral: isEphemeral(interaction.user.id) });
|
||||
}
|
||||
}
|
||||
|
||||
client.login(process.env.THE_TOKEN_2);
|
Loading…
Reference in New Issue
Block a user