Compare commits
16 Commits
47a03898cc
...
#27-rss-fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfb2765f39 | ||
|
|
33680209ba | ||
|
|
f70ea4229a | ||
|
|
acadc9faee | ||
|
|
f725ec88f9 | ||
|
|
8d34b93527 | ||
|
|
918cfe06e4 | ||
|
|
e2f276e65b | ||
| 52353ec1fb | |||
|
|
750877db1a | ||
|
|
a18337d0f8 | ||
|
|
6f45a60030 | ||
|
|
1fb4728b0a | ||
|
|
ebf48c7618 | ||
|
|
11b3504f28 | ||
|
|
14171a9c13 |
@@ -4,13 +4,13 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
# TODO - REMOVE AFTER TESTING
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- "*"
|
|
||||||
# schedule:
|
# schedule:
|
||||||
# - cron: '0 0 * * 1' # Every Monday at midnight (UTC)
|
# - cron: '0 0 * * 1' # Every Monday at midnight (UTC)
|
||||||
|
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_ENV: development
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-wiki:
|
update-wiki:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -35,12 +35,22 @@ jobs:
|
|||||||
repository: logan/drb-server.wiki # Replace with your wiki repository
|
repository: logan/drb-server.wiki # Replace with your wiki repository
|
||||||
path: wiki
|
path: wiki
|
||||||
|
|
||||||
|
- name: Output Generated Documentation
|
||||||
|
run: |
|
||||||
|
cat Home.md
|
||||||
|
ls
|
||||||
|
|
||||||
- name: Update wiki
|
- name: Update wiki
|
||||||
run: |
|
run: |
|
||||||
cp -r home.md wiki/
|
cp -rf Home.md wiki/Home.md
|
||||||
cd wiki
|
cd wiki
|
||||||
git config user.name "gitea-actions"
|
git config user.name "gitea-actions"
|
||||||
git config user.email "gitea-actions@cusano.net"
|
git config user.email "gitea-actions@cusano.net"
|
||||||
git add .
|
git add .
|
||||||
git commit -m "Update wiki from JSDoc"
|
# Check if there are any changes to commit
|
||||||
git push
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes to commit."
|
||||||
|
else
|
||||||
|
git commit -m "Update wiki from JSDoc"
|
||||||
|
git push
|
||||||
|
fi
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
{
|
{
|
||||||
"source": {
|
"source": {
|
||||||
"includePattern": ".+\\.([mc]?js(doc|x)?)$"
|
"includePattern": ".+([mc]?js(doc|x)?)$"
|
||||||
|
},
|
||||||
|
"plugins": ["node_modules/jsdoc-babel"],
|
||||||
|
"babel": {
|
||||||
|
"presets": [ "es2015" ],
|
||||||
|
"plugins": [ "transform-async-to-generator" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1327
package-lock.json
generated
1327
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs": "jsdoc2md -c jsdoc.conf src/**/*.mjs >> home.md",
|
"docs": "jsdoc2md -c jsdoc.conf src/*js > Home.md",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint --fix .",
|
"lint:fix": "eslint --fix .",
|
||||||
"test": "mocha --timeout 5000",
|
"test": "mocha --timeout 5000",
|
||||||
@@ -14,11 +14,14 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||||
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"eslint-plugin-unused-imports": "^4.1.3",
|
"eslint-plugin-unused-imports": "^4.1.3",
|
||||||
|
"jsdoc-babel": "^0.5.0",
|
||||||
"jsdoc-to-markdown": "^8.0.3",
|
"jsdoc-to-markdown": "^8.0.3",
|
||||||
"mocha": "^10.4.0",
|
"mocha": "^10.4.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
|||||||
@@ -1,232 +1,94 @@
|
|||||||
import { DebugBuilder } from "../../modules/debugger.mjs";
|
import { DebugBuilder } from "../../modules/debugger.mjs";
|
||||||
import { requestNodeJoinSystem } from "../../modules/socketServerWrappers.mjs";
|
const log = new DebugBuilder("server", "discordBot.modules.gptHandler");
|
||||||
import { getSystemByName } from "../../modules/mongo-wrappers/mongoSystemsWrappers.mjs";
|
|
||||||
import { getConfig } from "../../modules/mongo-wrappers/mongoConfigWrappers.mjs";
|
|
||||||
import { getAvailableTokensInGuild } from "../modules/wrappers.mjs";
|
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
import { OpenAI } from "openai";
|
import { OpenAI } from "openai";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
// Initialize environment variables
|
const openai = new OpenAI(process.env.OPENAI_API_KEY);
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const log = new DebugBuilder("server", "discordBot.modules.gptHandler");
|
const assistant = await openai.beta.assistants.create({
|
||||||
|
name: "Emmelia",
|
||||||
|
instructions: process.env.DRB_SERVER_INITIAL_PROMPT,
|
||||||
|
model: "gpt-4o",
|
||||||
|
});
|
||||||
|
|
||||||
let assistant;
|
class EventHandler extends EventEmitter {
|
||||||
let eventHandler;
|
constructor(client) {
|
||||||
|
super();
|
||||||
(async () => {
|
this.client = client;
|
||||||
try {
|
|
||||||
const openai = new OpenAI(process.env.OPENAI_API_KEY);
|
|
||||||
|
|
||||||
assistant = await openai.beta.assistants.create({
|
|
||||||
name: "Emmelia",
|
|
||||||
instructions: (await getConfig("emmeliaInitialPrompt")) || "",
|
|
||||||
model: "gpt-4o-mini",
|
|
||||||
tools: [
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "checkUserVoiceChannel",
|
|
||||||
description: "Check if the user is in a voice channel",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
userId: {
|
|
||||||
type: "string",
|
|
||||||
description: "The ID of the user",
|
|
||||||
},
|
|
||||||
guildId: {
|
|
||||||
type: "string",
|
|
||||||
description: "The ID of the guild",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["userId", "guildId"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "getSelectedSystem",
|
|
||||||
description: "Retrieve the selected system details",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
systemName: {
|
|
||||||
type: "string",
|
|
||||||
description: "The name of the system",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["systemName"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "joinSelectedSystem",
|
|
||||||
description: "Join the selected system in the user's voice channel",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
userId: {
|
|
||||||
type: "string",
|
|
||||||
description: "The ID of the user",
|
|
||||||
},
|
|
||||||
guildId: {
|
|
||||||
type: "string",
|
|
||||||
description: "The ID of the guild",
|
|
||||||
},
|
|
||||||
systemName: {
|
|
||||||
type: "string",
|
|
||||||
description: "The name of the system",
|
|
||||||
},
|
|
||||||
channelId: {
|
|
||||||
type: "string",
|
|
||||||
description: "The ID of the voice channel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["userId", "guildId", "systemName", "channelId"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
class EventHandler extends EventEmitter {
|
|
||||||
constructor(client) {
|
|
||||||
super();
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onEvent(event) {
|
|
||||||
try {
|
|
||||||
console.log(event);
|
|
||||||
if (event.event === "thread.run.requires_action") {
|
|
||||||
await this.handleRequiresAction(
|
|
||||||
event.data,
|
|
||||||
event.data.id,
|
|
||||||
event.data.thread_id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error handling event:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleRequiresAction(data, runId, threadId) {
|
|
||||||
try {
|
|
||||||
const toolOutputs = await Promise.all(
|
|
||||||
data.required_action.submit_tool_outputs.tool_calls.map(
|
|
||||||
async (toolCall) => {
|
|
||||||
switch (toolCall.function.name) {
|
|
||||||
case "checkUserVoiceChannel":
|
|
||||||
return await this.checkUserVoiceChannel(toolCall);
|
|
||||||
case "getSelectedSystem":
|
|
||||||
return await this.getSelectedSystem(toolCall);
|
|
||||||
case "joinSelectedSystem":
|
|
||||||
return await this.joinSelectedSystem(toolCall);
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unknown function: ${toolCall.function.name}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await this.submitToolOutputs(toolOutputs, runId, threadId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error processing required action:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkUserVoiceChannel(toolCall) {
|
|
||||||
const { userId, guildId } = JSON.parse(toolCall.function.arguments);
|
|
||||||
const guild = await this.client.guilds.get(guildId);
|
|
||||||
const member = await guild.members.get(userId);
|
|
||||||
const isInVoiceChannel = !!member.voice.channel;
|
|
||||||
|
|
||||||
return {
|
|
||||||
tool_call_id: toolCall.id,
|
|
||||||
output: JSON.stringify({
|
|
||||||
isInVoiceChannel,
|
|
||||||
channelId: member.voice.channelId,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSelectedSystem(toolCall) {
|
|
||||||
const { systemName } = JSON.parse(toolCall.function.arguments);
|
|
||||||
const system = await getSystemByName(systemName);
|
|
||||||
|
|
||||||
return {
|
|
||||||
tool_call_id: toolCall.id,
|
|
||||||
output: JSON.stringify(system),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async joinSelectedSystem(toolCall) {
|
|
||||||
const { userId, guildId, systemName, channelId } = JSON.parse(
|
|
||||||
toolCall.function.arguments,
|
|
||||||
);
|
|
||||||
const system = await getSystemByName(systemName);
|
|
||||||
const guild = await this.client.guilds.fetch(guildId);
|
|
||||||
const discordToken = await getAvailableTokensInGuild(guildId);
|
|
||||||
|
|
||||||
if (discordToken) {
|
|
||||||
const result = await requestNodeJoinSystem(
|
|
||||||
system,
|
|
||||||
channelId,
|
|
||||||
discordToken,
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
tool_call_id: toolCall.id,
|
|
||||||
output: JSON.stringify({ success: true, result }),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
tool_call_id: toolCall.id,
|
|
||||||
output: JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
message: "No available bots.",
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async submitToolOutputs(toolOutputs, runId, threadId) {
|
|
||||||
try {
|
|
||||||
const stream =
|
|
||||||
this.client.beta.threads.runs.submitToolOutputsStream(
|
|
||||||
threadId,
|
|
||||||
runId,
|
|
||||||
{ tool_outputs: toolOutputs },
|
|
||||||
);
|
|
||||||
for await (const event of stream) {
|
|
||||||
this.emit("event", event);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error submitting tool outputs:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventHandler = new EventHandler(openai);
|
|
||||||
eventHandler.on("event", eventHandler.onEvent.bind(eventHandler));
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Initialization error:", error);
|
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
async onEvent(event) {
|
||||||
|
try {
|
||||||
|
console.log(event);
|
||||||
|
// Retrieve events that are denoted with 'requires_action'
|
||||||
|
// since these will have our tool_calls
|
||||||
|
if (event.event === "thread.run.requires_action") {
|
||||||
|
await this.handleRequiresAction(
|
||||||
|
event.data,
|
||||||
|
event.data.id,
|
||||||
|
event.data.thread_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling event:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleRequiresAction(data, runId, threadId) {
|
||||||
|
try {
|
||||||
|
const toolOutputs =
|
||||||
|
data.required_action.submit_tool_outputs.tool_calls.map((toolCall) => {
|
||||||
|
// Call the function
|
||||||
|
switch (toolCall.function.name) {
|
||||||
|
case "getCurrentTemperature":
|
||||||
|
return {
|
||||||
|
tool_call_id: toolCall.id,
|
||||||
|
output: "57",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Submit all the tool outputs at the same time
|
||||||
|
await this.submitToolOutputs(toolOutputs, runId, threadId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing required action:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitToolOutputs(toolOutputs, runId, threadId) {
|
||||||
|
try {
|
||||||
|
// Use the submitToolOutputsStream helper
|
||||||
|
const stream = this.client.beta.threads.runs.submitToolOutputsStream(
|
||||||
|
threadId,
|
||||||
|
runId,
|
||||||
|
{ tool_outputs: toolOutputs },
|
||||||
|
);
|
||||||
|
for await (const event of stream) {
|
||||||
|
this.emit("event", event);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error submitting tool outputs:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventHandler = new EventHandler(openai);
|
||||||
|
eventHandler.on("event", eventHandler.onEvent.bind(eventHandler));
|
||||||
|
|
||||||
export const gptHandler = async (additionalMessages) => {
|
export const gptHandler = async (additionalMessages) => {
|
||||||
|
const thread = await openai.beta.threads.create();
|
||||||
|
|
||||||
|
// Add the additional messages to the conversation
|
||||||
|
for (const msgObj of additionalMessages) {
|
||||||
|
await openai.beta.threads.messages.create(thread.id, msgObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.DEBUG("AI Conversation:", thread);
|
||||||
|
|
||||||
|
// Run the thread to get a response
|
||||||
try {
|
try {
|
||||||
const thread = await openai.beta.threads.create();
|
|
||||||
|
|
||||||
for (const msgObj of additionalMessages) {
|
|
||||||
await openai.beta.threads.messages.create(thread.id, msgObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stream = await openai.beta.threads.runs.stream(
|
const stream = await openai.beta.threads.runs.stream(
|
||||||
thread.id,
|
thread.id,
|
||||||
{ assistant_id: assistant.id },
|
{ assistant_id: assistant.id },
|
||||||
@@ -237,8 +99,9 @@ export const gptHandler = async (additionalMessages) => {
|
|||||||
eventHandler.emit("event", event);
|
eventHandler.emit("event", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let response;
|
||||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||||
const response = messages.data[0].content[0].text.value;
|
response = messages.data[0].content[0].text.value;
|
||||||
|
|
||||||
log.DEBUG("AI Response:", response);
|
log.DEBUG("AI Response:", response);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import {
|
|||||||
getConfig,
|
getConfig,
|
||||||
setConfig,
|
setConfig,
|
||||||
} from "../../modules/mongo-wrappers/mongoConfigWrappers.mjs";
|
} from "../../modules/mongo-wrappers/mongoConfigWrappers.mjs";
|
||||||
import { ActivityType, PresenceUpdateStatus, Client } from "discord.js";
|
import { ActivityType, PresenceUpdateStatus } from "discord.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control the presence or activity of the discord bot.
|
||||||
|
*/
|
||||||
class PresenceManager {
|
class PresenceManager {
|
||||||
/**
|
/**
|
||||||
* Creates an instance of PresenceManager.
|
* Creates an instance of PresenceManager.
|
||||||
|
|||||||
@@ -71,6 +71,22 @@ export const updateFeedByLink = async (link, updatedFields) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wrapper for deactivating a feed by link
|
||||||
|
export const deactivateFeedByLink = async (link) => {
|
||||||
|
try {
|
||||||
|
const updatedCount = await updateDocumentByField(
|
||||||
|
feedCollectionName,
|
||||||
|
"link",
|
||||||
|
link,
|
||||||
|
[{'active':false}]
|
||||||
|
);
|
||||||
|
return updatedCount;
|
||||||
|
} catch (error) {
|
||||||
|
log.ERROR("Error deleting feed by link:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Wrapper for deleting a feed by link
|
// Wrapper for deleting a feed by link
|
||||||
export const deleteFeedByLink = async (link) => {
|
export const deleteFeedByLink = async (link) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ export const updateFeeds = async (client) => {
|
|||||||
|
|
||||||
const sourcePromiseArray = records.map(async (source) => {
|
const sourcePromiseArray = records.map(async (source) => {
|
||||||
log.DEBUG("Processing source:", source.title);
|
log.DEBUG("Processing source:", source.title);
|
||||||
|
// Check if the feed is active
|
||||||
|
if (!source.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedFeed = await parser.parseURL(source.link);
|
const parsedFeed = await parser.parseURL(source.link);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const log = new DebugBuilder("server", "sourceManager");
|
|||||||
import {
|
import {
|
||||||
createFeed,
|
createFeed,
|
||||||
getFeedByLink,
|
getFeedByLink,
|
||||||
deleteFeedByLink,
|
deactivateFeedByLink,
|
||||||
} from "../modules/mongo-wrappers/mongoFeedsWrappers.mjs";
|
} from "../modules/mongo-wrappers/mongoFeedsWrappers.mjs";
|
||||||
|
|
||||||
class SourceManager {
|
class SourceManager {
|
||||||
@@ -48,7 +48,7 @@ class SourceManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await deleteFeedByLink(sourceURL);
|
const results = await deactivateFeedByLink(sourceURL);
|
||||||
if (!results) {
|
if (!results) {
|
||||||
log.WARN(`Failed to remove source: ${sourceURL}`);
|
log.WARN(`Failed to remove source: ${sourceURL}`);
|
||||||
return;
|
return;
|
||||||
@@ -70,6 +70,7 @@ class SourceManager {
|
|||||||
category,
|
category,
|
||||||
guild_id: guildId,
|
guild_id: guildId,
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
|
active: true
|
||||||
};
|
};
|
||||||
const record = await createFeed(feed);
|
const record = await createFeed(feed);
|
||||||
log.DEBUG("Source added:", record);
|
log.DEBUG("Source added:", record);
|
||||||
|
|||||||
Reference in New Issue
Block a user