11 Commits

Author SHA1 Message Date
Logan Cusano
961a7cc2bd Implement early AI integration #18
All checks were successful
release-tag / release-image (push) Successful in 4m19s
DRB Tests / drb_mocha_tests (push) Successful in 1m16s
- Added new event to catch messageCreate events
- @ messages to the server will use ChatGPT to respond to the message with an indepth prompt about the server
- Implement module to interact with chatGPT repeatably
- Add linkcop with GPT integration #12
- Added environment variable for inital prompt for GPT integration
2024-06-02 19:16:01 -04:00
Logan Cusano
2c3cc18474 Update dependencies and add openai 2024-06-02 19:10:29 -04:00
Logan Cusano
424d5ae749 #16 Fix bug in rss remove
All checks were successful
release-tag / release-image (push) Successful in 4m5s
DRB Tests / drb_mocha_tests (push) Successful in 43s
- A dependency of remove was missing the log object
- Updated discord output for all RSS commands
2024-05-26 22:42:54 -04:00
Logan Cusano
5c86185ef5 Update port in dockerfile
All checks were successful
release-tag / release-image (push) Successful in 3m42s
DRB Tests / drb_mocha_tests (push) Successful in 55s
2024-05-26 21:45:08 -04:00
Logan Cusano
e6de0f4453 Undo repo change
All checks were successful
release-tag / release-image (push) Successful in 4m24s
DRB Tests / drb_mocha_tests (push) Successful in 36s
- Does not add to the repo, changes the name
2024-05-26 21:30:47 -04:00
Logan Cusano
e8cfca1d8d Update build action
All checks were successful
release-tag / release-image (push) Successful in 5m15s
DRB Tests / drb_mocha_tests (push) Successful in 1m0s
- Attempt to place the package in the repo instead of my profile
2024-05-26 21:23:19 -04:00
Logan Cusano
dce0086fdb Update build action
All checks were successful
release-tag / release-image (push) Successful in 4m52s
DRB Tests / drb_mocha_tests (push) Successful in 41s
- Update repo name with proper vars
2024-05-26 21:12:57 -04:00
Logan Cusano
ad45d8f0ea Update repo name
Some checks failed
DRB Tests / drb_mocha_tests (push) Successful in 49s
release-tag / release-image (push) Failing after 4m1s
2024-05-26 20:49:32 -04:00
Logan Cusano
2c5cf3dac0 update all local IPs to public hostname
Some checks failed
release-tag / release-image (push) Failing after 22s
DRB Tests / drb_mocha_tests (push) Successful in 32s
2024-05-26 20:47:31 -04:00
Logan Cusano
a3223b716e update server build action
Some checks failed
release-tag / release-image (push) Failing after 20s
DRB Tests / drb_mocha_tests (push) Successful in 34s
- try public hostname instead of IP for docker login (to use HTTPS)
2024-05-26 20:45:16 -04:00
Logan Cusano
7a246f9e2a Update build action to explicitly set gitea as http
Some checks failed
release-tag / release-image (push) Failing after 16s
DRB Tests / drb_mocha_tests (push) Successful in 31s
2024-05-26 20:43:19 -04:00
13 changed files with 792 additions and 290 deletions

View File

@@ -25,14 +25,14 @@ jobs:
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
with: # replace it with your local IP with: # replace it with your local IP
config-inline: | config-inline: |
[registry."${{ secrets.LOCAL_GITEA_IP}}:3000"] [registry."git.vpn.cusano.net"]
http = true http = false
insecure = true insecure = false
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ${{ secrets.LOCAL_GITEA_IP}}:3000 # replace it with your local IP registry: git.vpn.cusano.net # replace it with your local IP
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
@@ -52,5 +52,5 @@ jobs:
linux/arm64 linux/arm64
push: true push: true
tags: | # replace it with your local IP and tags tags: | # replace it with your local IP and tags
${{ secrets.LOCAL_GITEA_IP}}:3000/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }} git.vpn.cusano.net/${{ vars.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
${{ secrets.LOCAL_GITEA_IP}}:3000/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }} git.vpn.cusano.net/${{ vars.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}

View File

@@ -15,7 +15,7 @@ RUN npm install
COPY . . COPY . .
# Expose the port on which your Node.js application will run # Expose the port on which your Node.js application will run
EXPOSE 3000 EXPOSE 3420
# Command to run the Node.js application # Command to run the Node.js application
CMD ["node", "."] CMD ["node", "."]

View File

@@ -0,0 +1,53 @@
import { DebugBuilder } from "../../modules/debugger.mjs";
const log = new DebugBuilder("server", "discordBot.addons.gptInteraction");
import { gptHandler } from "../modules/gptHandler.mjs";
export const gptInteraction = async (nodeIo, message) => {
let conversation = [];
let prevMessages = await message.channel.messages.fetch({ limit: 10 });
prevMessages.reverse();
prevMessages.forEach((msg) => {
// Check if the message was sent within the last 24 hours
if (new Date().getTime() - msg.createdTimestamp > (24 * 60 * 60 * 1000)) {
return;
}
// Check if it's from a bot other than the server
if (msg.author.bot && msg.author.id !== nodeIo.serverClient.user.id) return;
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
if (msg.author.id === nodeIo.serverClient.user.id) {
conversation.push({
role: 'assistant',
name: msg.author.id,
content: msg.content,
});
return;
}
conversation.push({
role: 'user',
name: msg.author.id,
content: msg.content.replace(`<@${nodeIo.serverClient.user.id}>`, ''),
});
});
const response = await gptHandler(conversation);
if (response) {
const responseMessage = response.choices[0].message.content;
const chunkSize = 2500;
for (let i = 0; i < responseMessage.length; i += chunkSize) {
const chunk = responseMessage.substring(i, i + chunkSize);
log.DEBUG("Sending message chunk:", chunk);
await message.reply(chunk);
}
} else {
message.channel.send('Sorry, I encountered an error while processing your request.');
}
}

View File

@@ -0,0 +1,83 @@
import { DebugBuilder } from "../../modules/debugger.mjs";
const log = new DebugBuilder("server", "discordBot.addons.linkCop");
import { gptHandler } from "../modules/gptHandler.mjs";
import dotenv from 'dotenv';
dotenv.config();
const approvedLinksChannel = "767303243285790721";
const restrictedChannelIds = process.env.LINKCOP_RESTRICTED_CHANNELS.split(',');
const linkRegExp = /(?:http[s]?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/g
export const linkCop = async (nodeIo, message) => {
if (message.channel.id == approvedLinksChannel || !restrictedChannelIds.includes(message.channel.id)) return false;
const urls = String(message.content).matchAll(linkRegExp);
if (!urls || urls.length === 0) return false;
log.DEBUG("Found URLs: ", urls);
let conversation = [];
let prevMessages = await message.channel.messages.fetch({ limit: 2 });
prevMessages.reverse();
prevMessages.forEach((msg) => {
// Check if the message was sent within the last 5 minutes
if (new Date().getTime() - msg.createdTimestamp > (5 * 60 * 1000)) {
return;
}
// Check if it's from a bot other than the server
if (msg.author.bot && msg.author.id !== nodeIo.serverClient.user.id) return;
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
if (msg.author.id === nodeIo.serverClient.user.id) {
conversation.push({
role: 'assistant',
name: msg.author.id,
content: msg.content,
});
return;
}
conversation.push({
role: 'user',
name: msg.author.id,
content: msg.content.replace(`<@${nodeIo.serverClient.user.id}>`, ''),
});
});
conversation.push({
role: 'system',
content: `There has been a link posted to a channel that links are not allowed in. The above messages are from the channel that links are not allowed including the message with the link. The message with the link is going to be deleted and moved to the '#links' channels. You should let the user know.`
});
const response = await gptHandler(conversation);
if (response) {
const responseMessage = response.choices[0].message.content;
const chunkSize = 2000;
for (let i = 0; i < responseMessage.length; i += chunkSize) {
const chunk = responseMessage.substring(i, i + chunkSize);
log.DEBUG("Sending message chunk:", chunk);
await message.reply(chunk);
}
const messageContent = {
'author': message.author,
'content': `<@${message.author.id}> - ${String(message.content)}`,
'channelId': message.channelId,
'links': urls
}
await message.delete();
log.DEBUG("Message content: ", messageContent);
message.client.channels.cache.get(approvedLinksChannel).send(messageContent);
}
}

View File

@@ -54,18 +54,19 @@ export const execute = async (nodeIo, interaction) => {
var category = interaction.options.getString('category'); var category = interaction.options.getString('category');
if (!category) category = "ALL"; if (!category) category = "ALL";
await interaction.reply(`Adding ${title} to the list of RSS sources, please wait...`);
await addSource(title, link, category, interaction.guildId, interaction.channelId, (err, result) => { await addSource(title, link, category, interaction.guildId, interaction.channelId, (err, result) => {
log.DEBUG("Result from adding entry", result); log.DEBUG("Result from adding entry", result);
if (result) { if (result) {
interaction.reply(`Successfully added ${title} to the list of RSS sources`); interaction.editReply(`Successfully added ${title} to the list of RSS sources`);
} else { } else {
interaction.reply(`${title} already exists in the list of RSS sources`); interaction.editReply(`${title} already exists in the list of RSS sources`);
} }
}); });
} catch (err) { } catch (err) {
log.ERROR(err) log.ERROR(err)
await interaction.reply(err.toString()); await interaction.editReply(err.toString());
} }
} }

View File

@@ -42,17 +42,17 @@ export async function autocomplete(nodeIo, interaction) {
export const execute = async (nodeIo, interaction) => { export const execute = async (nodeIo, interaction) => {
try { try {
var title = interaction.options.getString('title'); var title = interaction.options.getString('title');
interaction.reply(`Removing ${title} from the list of RSS sources, please wait...`); await interaction.reply(`Removing ${title} from the list of RSS sources, please wait...`);
const results = await deleteFeedByTitle(title); const results = await deleteFeedByTitle(title);
if (!results) { if (!results) {
log.WARN(`Failed to remove source: ${title}`); log.WARN(`Failed to remove source: ${title}`);
interaction.editReply(`Failed to remove source: '${title}'`); await interaction.editReply(`Failed to remove source: '${title}'`);
return; return;
} }
interaction.editReply(`${title} was successfully removed from the RSS sources.`) await interaction.editReply(`${title} was successfully removed from the RSS sources.`)
} catch (err) { } catch (err) {
log.ERROR(err) log.ERROR(err)
interaction.editReply(err.toString()); await interaction.editReply(err.toString());
} }
} }

View File

@@ -38,6 +38,7 @@ export const execute = async (nodeIo, interaction) => {
//await interaction.reply(`**Online Sockets: '${sockets}'**`); //await interaction.reply(`**Online Sockets: '${sockets}'**`);
await interaction.reply('Triggering RSS update'); await interaction.reply('Triggering RSS update');
await updateFeeds(interaction.client); await updateFeeds(interaction.client);
await interaction.editReply('RSS Update Completed');
//await interaction.channel.send('**Pong.**'); //await interaction.channel.send('**Pong.**');
} catch (err) { } catch (err) {
console.error(err); console.error(err);

View File

@@ -79,7 +79,7 @@ export function addEnabledEventListeners(serverClient, _eventsPath = "./events")
} }
// The discord client // The discord client
export const serverClient = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates] }); export const serverClient = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
// Run when the bot is ready // Run when the bot is ready
serverClient.on('ready', async () => { serverClient.on('ready', async () => {

View File

@@ -0,0 +1,29 @@
import { DebugBuilder } from "../../modules/debugger.mjs";
const log = new DebugBuilder("server", "discordBot.events.messageCreate");
import dotenv from 'dotenv';
dotenv.config();
import { Events } from 'discord.js';
import { gptInteraction } from '../addons/gptInteraction.mjs';
import { linkCop } from '../addons/linkCop.mjs';
const IGNORED_CHANNELS = process.env.IGNORED_CHANNELS.split(',');
export const name = Events.MessageCreate;
export async function execute(nodeIo, message) {
// Ignore ignored channels
if (IGNORED_CHANNELS.includes(message.channel.id)) return;
// Ignore messages from a bot
if (message.author.bot) return;
log.INFO("Message create", message);
// Check if the message mentions the bot
if (message.mentions.users.has(nodeIo.serverClient.user.id)) {
return await gptInteraction(nodeIo, message);
}
// Check if the message contains a link in a channel it shouldn't
if (await linkCop(nodeIo, message)) return;
}

View File

@@ -0,0 +1,39 @@
import { DebugBuilder } from "../../modules/debugger.mjs";
const log = new DebugBuilder("server", "discordBot.modules.gptHandler");
import dotenv from 'dotenv';
dotenv.config();
import { OpenAI } from 'openai';
const openai = new OpenAI(process.env.OPENAI_API_KEY);
let conversation = [];
conversation.push({
role: 'system',
content: process.env.DRB_SERVER_INITIAL_PROMPT
});
export const gptHandler = async (additionalMessages) => {
// Add the additional messages to the conversation
conversation = conversation.concat(additionalMessages);
log.DEBUG("AI Conversation:", conversation);
try {
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: conversation,
}).catch((error) => log.ERROR("OpenAI Error: ", error));
log.DEBUG("AI Response:", response);
if (!response) {
return false;
}
return response
} catch (error) {
console.error('Error generating response:', error);
return false;
}
}

829
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,19 +11,20 @@
"license": "ISC", "license": "ISC",
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"chai": "^5.1.0", "chai": "^5.1.1",
"mocha": "^10.4.0", "mocha": "^10.4.0",
"socket.io-client": "^4.7.5" "socket.io-client": "^4.7.5"
}, },
"dependencies": { "dependencies": {
"discord.js": "^14.14.1", "discord.js": "^14.15.2",
"dotenv": "^16.3.1", "dotenv": "^16.4.5",
"express": "^4.18.2", "express": "^4.19.2",
"mongodb": "^6.3.0", "mongodb": "^6.7.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"node-html-parser": "^6.1.13", "node-html-parser": "^6.1.13",
"openai": "^4.47.3",
"rss-parser": "^3.13.0", "rss-parser": "^3.13.0",
"socket.io": "^4.7.2", "socket.io": "^4.7.5",
"user-agents": "^1.1.208" "user-agents": "^1.1.222"
} }
} }

View File

@@ -1,3 +1,5 @@
import { DebugBuilder } from "../modules/debugger.mjs";
const log = new DebugBuilder("server", "sourceManager");
import { createFeed, getFeedByLink, deleteFeedByLink } from '../modules/mongo-wrappers/mongoFeedsWrappers.mjs'; import { createFeed, getFeedByLink, deleteFeedByLink } from '../modules/mongo-wrappers/mongoFeedsWrappers.mjs';
class SourceManager { class SourceManager {