linted
This commit is contained in:
parent
8231b5a105
commit
601e5a703f
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
node_modules/
|
||||||
|
logs/
|
||||||
|
images/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
|
*.min.js
|
||||||
67
.eslintrc.json
Normal file
67
.eslintrc.json
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es2022": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:import/recommended"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"import"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"node": {
|
||||||
|
"extensions": [
|
||||||
|
".js",
|
||||||
|
".mjs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
// Error prevention
|
||||||
|
"no-const-assign": "error",
|
||||||
|
"no-dupe-args": "error",
|
||||||
|
"no-dupe-keys": "error",
|
||||||
|
"no-duplicate-case": "error",
|
||||||
|
"no-unreachable": "error",
|
||||||
|
"valid-typeof": "error",
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"eqeqeq": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
||||||
|
"no-var": "error",
|
||||||
|
"prefer-const": "error",
|
||||||
|
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||||
|
|
||||||
|
// Style
|
||||||
|
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"quotes": ["error", "single"],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 1 }],
|
||||||
|
"no-trailing-spaces": "error",
|
||||||
|
"eol-last": "error",
|
||||||
|
"no-mixed-spaces-and-tabs": "error",
|
||||||
|
|
||||||
|
// Object and array formatting
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"array-bracket-spacing": ["error", "never"],
|
||||||
|
"comma-dangle": ["error", "never"],
|
||||||
|
|
||||||
|
// Import/Export
|
||||||
|
"import/no-duplicates": "error",
|
||||||
|
"import/order": ["error", {
|
||||||
|
"groups": ["builtin", "external", "internal", "parent", "sibling", "index"],
|
||||||
|
"newlines-between": "always",
|
||||||
|
"alphabetize": { "order": "asc" }
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
|
|
||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
|
|
||||||
import { CODES } from '../_src/ansiColors.js';
|
import { CODES } from '../_src/ansiColors.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } from 'discord.js';
|
|
||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } from 'discord.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* botUtils module - provides administrative bot control commands
|
* botUtils module - provides administrative bot control commands
|
||||||
@ -53,7 +53,7 @@ export const commands = [
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Slash command `/status` (Administrator only):
|
* Slash command `/status` (Administrator only):
|
||||||
* Shows this bot client’s status including CPU, memory, environment,
|
* Shows this bot client's status including CPU, memory, environment,
|
||||||
* uptime, module list, and entity counts. Optionally displays Git info
|
* uptime, module list, and entity counts. Optionally displays Git info
|
||||||
* (Git Reference and Git Status) when the gitUtils module is loaded.
|
* (Git Reference and Git Status) when the gitUtils module is loaded.
|
||||||
* @param {import('discord.js').CommandInteraction} interaction
|
* @param {import('discord.js').CommandInteraction} interaction
|
||||||
@ -158,6 +158,10 @@ export const commands = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Module loaded logging
|
// Module loaded logging
|
||||||
export async function init(client, clientConfig) {
|
export async function init(_client, _clientConfig) {
|
||||||
client.logger.info('[module:botUtils] Module loaded');
|
_client.logger.info('[module:botUtils] Module loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleInteractionCreate(_client, _clientConfig, _interaction) {
|
||||||
|
// ... existing code ...
|
||||||
}
|
}
|
||||||
@ -78,7 +78,6 @@ export const init = async (client, config) => {
|
|||||||
// Used as a prefix before any line that runs within a loop.
|
// Used as a prefix before any line that runs within a loop.
|
||||||
const bullet = '>';
|
const bullet = '>';
|
||||||
|
|
||||||
|
|
||||||
// === OpenAI Interaction ===
|
// === OpenAI Interaction ===
|
||||||
// Chat completion via OpenAI with provided instructions.
|
// Chat completion via OpenAI with provided instructions.
|
||||||
async function ai(prompt = '') {
|
async function ai(prompt = '') {
|
||||||
@ -86,17 +85,17 @@ export const init = async (client, config) => {
|
|||||||
debug(`**AI Prompt**: ${prompt}`);
|
debug(`**AI Prompt**: ${prompt}`);
|
||||||
|
|
||||||
// Read instructions.
|
// Read instructions.
|
||||||
let openAIInstructions = fs.readFileSync(openAIInstructionsFile, 'utf8');
|
const openAIInstructions = fs.readFileSync(openAIInstructionsFile, 'utf8');
|
||||||
const unmention = /<@(\w+)>/g;
|
const unmention = /<@(\w+)>/g;
|
||||||
const completion = await openai.chat.completions.create({
|
const completion = await openai.chat.completions.create({
|
||||||
model: 'gpt-4o-mini',
|
model: 'gpt-4o-mini',
|
||||||
messages: [
|
messages: [
|
||||||
{ role: 'user', content: `${prompt.replace(unmention, '$1')}` },
|
{ role: 'user', content: `${prompt.replace(unmention, '$1')}` },
|
||||||
{role: 'system', content: `${openAIInstructions}`},
|
{ role: 'system', content: `${openAIInstructions}` }
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
let chunk = completion.choices[0]?.message?.content;
|
const chunk = completion.choices[0]?.message?.content;
|
||||||
if (chunk != '') {
|
if (chunk !== '') {
|
||||||
for (const line of chunk.split(/\n\s*\n/).filter(Boolean)) {
|
for (const line of chunk.split(/\n\s*\n/).filter(Boolean)) {
|
||||||
debug(`${bullet} ${line}`);
|
debug(`${bullet} ${line}`);
|
||||||
openAIWebhookClient.send(line);
|
openAIWebhookClient.send(line);
|
||||||
@ -142,7 +141,7 @@ export const init = async (client, config) => {
|
|||||||
|
|
||||||
// === Message Fetching Helpers ===
|
// === Message Fetching Helpers ===
|
||||||
// Retrieve recent messages from every text channel since a given timestamp.
|
// Retrieve recent messages from every text channel since a given timestamp.
|
||||||
async function fetchRecentMessages(since) {
|
async function _fetchRecentMessages(since) {
|
||||||
const allMessages = new Collection();
|
const allMessages = new Collection();
|
||||||
|
|
||||||
// Get all text channels in the guild
|
// Get all text channels in the guild
|
||||||
@ -181,7 +180,7 @@ export const init = async (client, config) => {
|
|||||||
debug(`**Incident Cycle #${incidentCounter++}**`);
|
debug(`**Incident Cycle #${incidentCounter++}**`);
|
||||||
|
|
||||||
// Rebuild the list of current index cases, if any.
|
// Rebuild the list of current index cases, if any.
|
||||||
let indexesList = guild.members.cache.filter(member => member.roles.cache.has(indexRole.id));
|
const indexesList = guild.members.cache.filter(member => member.roles.cache.has(indexRole.id));
|
||||||
debug(`${bullet} Index Cases: **${indexesList.size}**`);
|
debug(`${bullet} Index Cases: **${indexesList.size}**`);
|
||||||
|
|
||||||
// Build the victimsList using whitelisted roles.
|
// Build the victimsList using whitelisted roles.
|
||||||
@ -206,7 +205,7 @@ export const init = async (client, config) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Conditions for potentially starting an incident.
|
// Conditions for potentially starting an incident.
|
||||||
if (indexesList.size == 0 && victimsList.size > 0) {
|
if (indexesList.size === 0 && victimsList.size > 0) {
|
||||||
if ((Math.floor(Math.random() * incidenceDenominator) + 1) === 1) {
|
if ((Math.floor(Math.random() * incidenceDenominator) + 1) === 1) {
|
||||||
debug(`${bullet} Incidence Check: **Success**`);
|
debug(`${bullet} Incidence Check: **Success**`);
|
||||||
const newIndex = victimsList.random();
|
const newIndex = victimsList.random();
|
||||||
@ -240,7 +239,7 @@ export const init = async (client, config) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the next cycle.
|
// Prepare the next cycle.
|
||||||
let interval = cycleInterval + Math.floor(Math.random() * (2 * cycleIntervalRange + 1)) - cycleIntervalRange;
|
const interval = cycleInterval + Math.floor(Math.random() * (2 * cycleIntervalRange + 1)) - cycleIntervalRange;
|
||||||
setTimeout(cycleIncidents, interval);
|
setTimeout(cycleIncidents, interval);
|
||||||
debug(`${bullet} Cycle #${incidentCounter} **<t:${Math.floor((Date.now() + interval) / 1000)}:R>** at **<t:${Math.floor((Date.now() + interval) / 1000)}:t>**`);
|
debug(`${bullet} Cycle #${incidentCounter} **<t:${Math.floor((Date.now() + interval) / 1000)}:R>** at **<t:${Math.floor((Date.now() + interval) / 1000)}:t>**`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -291,7 +290,6 @@ export const init = async (client, config) => {
|
|||||||
if (message.webhookId) return;
|
if (message.webhookId) return;
|
||||||
guild = client.guilds.cache.get(guildID);
|
guild = client.guilds.cache.get(guildID);
|
||||||
|
|
||||||
|
|
||||||
// Someone mentioned us - respond if openAI is enabled, the message was in the webhook channel, and a trigger was used.
|
// Someone mentioned us - respond if openAI is enabled, the message was in the webhook channel, and a trigger was used.
|
||||||
if (openAI === true && openAIWebhook.channel.id === message.channel.id && openAITriggers.some(word => message.content.replace(/[^\w\s]/gi, '').toLowerCase().includes(word.toLowerCase()))) {
|
if (openAI === true && openAIWebhook.channel.id === message.channel.id && openAITriggers.some(word => message.content.replace(/[^\w\s]/gi, '').toLowerCase().includes(word.toLowerCase()))) {
|
||||||
// Also check if an active incident is required to respond.
|
// Also check if an active incident is required to respond.
|
||||||
@ -334,7 +332,7 @@ export const init = async (client, config) => {
|
|||||||
let percentage = Math.min(infections / prox.size * 100, probabilityLimit);
|
let percentage = Math.min(infections / prox.size * 100, probabilityLimit);
|
||||||
|
|
||||||
// Reduce base probability by ${antiViralEffectiveness}% for those with ${antiViralRole}
|
// Reduce base probability by ${antiViralEffectiveness}% for those with ${antiViralRole}
|
||||||
if (message.member.roles.cache.has(antiViralRole.id)) {
|
if (message.member.roles.cache.has(antiViralRole.id) && Math.random() * 100 === antiViralEffectiveness) {
|
||||||
percentage = Math.round(percentage - (antiViralEffectiveness * (percentage / 100)));
|
percentage = Math.round(percentage - (antiViralEffectiveness * (percentage / 100)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { MessageFlags } from 'discord-api-types/v10';
|
|
||||||
import { execFile } from 'child_process';
|
import { execFile } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
|
||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
// Use execFile to avoid shell interpretation of arguments
|
// Use execFile to avoid shell interpretation of arguments
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { onMessageQueueEvent } from './pbUtils.js';
|
|||||||
/**
|
/**
|
||||||
* Example module that listens for 'test' messages in the message_queue collection.
|
* Example module that listens for 'test' messages in the message_queue collection.
|
||||||
*/
|
*/
|
||||||
export const init = async (client, config) => {
|
export async function init(client, _config) {
|
||||||
client.logger.info('[module:messageQueueExample] Initializing Message Queue Example module');
|
client.logger.info('[module:messageQueueExample] Message Queue Example module initialized');
|
||||||
onMessageQueueEvent(client, async (action, record) => {
|
onMessageQueueEvent(client, async (action, record) => {
|
||||||
// Only process newly created records
|
// Only process newly created records
|
||||||
if (action !== 'create') return;
|
if (action !== 'create') return;
|
||||||
@ -25,4 +25,4 @@ export const init = async (client, config) => {
|
|||||||
client.logger.error(`[module:messageQueueExample] Failed to delete message_queue record ${record.id}: ${err.message}`);
|
client.logger.error(`[module:messageQueueExample] Failed to delete message_queue record ${record.id}: ${err.message}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// _opt/pbutils.js
|
// _opt/pbutils.js
|
||||||
// Polyfill global EventSource for PocketBase realtime in Node.js (using CommonJS require)
|
// Polyfill global EventSource for PocketBase realtime in Node.js (using CommonJS require)
|
||||||
import { createRequire } from 'module';
|
import { createRequire } from 'module';
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const { EventSource } = require('eventsource');
|
const { EventSource } = require('eventsource');
|
||||||
if (typeof global.EventSource === 'undefined') {
|
if (typeof global.EventSource === 'undefined') {
|
||||||
@ -16,7 +17,7 @@ if (typeof global.EventSource === 'undefined') {
|
|||||||
* @param {Object} client - Discord client with attached PocketBase instance
|
* @param {Object} client - Discord client with attached PocketBase instance
|
||||||
* @param {Object} config - Client configuration
|
* @param {Object} config - Client configuration
|
||||||
*/
|
*/
|
||||||
export const init = async (client, config) => {
|
export async function init(client, _config) {
|
||||||
const { pb, logger } = client;
|
const { pb, logger } = client;
|
||||||
|
|
||||||
logger.info('[module:pbUtils] Initializing PocketBase utilities module');
|
logger.info('[module:pbUtils] Initializing PocketBase utilities module');
|
||||||
@ -41,7 +42,7 @@ export const init = async (client, config) => {
|
|||||||
// end of init()
|
// end of init()
|
||||||
|
|
||||||
logger.info('PocketBase utilities module initialized');
|
logger.info('PocketBase utilities module initialized');
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a handler for incoming message_queue pub/sub events.
|
* Register a handler for incoming message_queue pub/sub events.
|
||||||
@ -215,9 +216,9 @@ const extendPocketBase = (client, pb, logger) => {
|
|||||||
const records = [];
|
const records = [];
|
||||||
const pageSize = options.pageSize || 200;
|
const pageSize = options.pageSize || 200;
|
||||||
let page = 1;
|
let page = 1;
|
||||||
|
const isRunning = true;
|
||||||
|
while (isRunning) {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
|
||||||
const result = await pb.collection(collection).getList(page, pageSize, options);
|
const result = await pb.collection(collection).getList(page, pageSize, options);
|
||||||
records.push(...result.items);
|
records.push(...result.items);
|
||||||
|
|
||||||
@ -226,13 +227,13 @@ const extendPocketBase = (client, pb, logger) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
page++;
|
page++;
|
||||||
}
|
|
||||||
|
|
||||||
return records;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to get all records from ${collection}: ${error.message}`);
|
logger.error(`Failed to get all records from ${collection}: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,7 +362,6 @@ const extendPocketBase = (client, pb, logger) => {
|
|||||||
return await pb.deleteOne('message_queue', id);
|
return await pb.deleteOne('message_queue', id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ===== CACHE MANAGEMENT =====
|
// ===== CACHE MANAGEMENT =====
|
||||||
|
|
||||||
// Simple in-memory cache
|
// Simple in-memory cache
|
||||||
|
|||||||
@ -4,13 +4,15 @@
|
|||||||
* and handles text or image (function_call) outputs.
|
* and handles text or image (function_call) outputs.
|
||||||
*/
|
*/
|
||||||
// Removed local file fallback; prompt now comes exclusively from PocketBase via responsesPrompt module
|
// Removed local file fallback; prompt now comes exclusively from PocketBase via responsesPrompt module
|
||||||
import { OpenAI } from 'openai';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { AttachmentBuilder, PermissionFlagsBits } from 'discord.js';
|
|
||||||
import { expandTemplate } from '../_src/template.js';
|
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { AttachmentBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
|
import { OpenAI } from 'openai';
|
||||||
|
|
||||||
|
import { expandTemplate } from '../_src/template.js';
|
||||||
|
|
||||||
// Discord message max length
|
// Discord message max length
|
||||||
const MAX_DISCORD_MSG_LENGTH = 2000;
|
const MAX_DISCORD_MSG_LENGTH = 2000;
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ function splitMessage(text, maxLength = MAX_DISCORD_MSG_LENGTH) {
|
|||||||
let chunk = '';
|
let chunk = '';
|
||||||
let codeBlockOpen = false;
|
let codeBlockOpen = false;
|
||||||
let codeBlockFence = '```';
|
let codeBlockFence = '```';
|
||||||
for (let line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
const isFenceLine = trimmed.startsWith('```');
|
const isFenceLine = trimmed.startsWith('```');
|
||||||
if (isFenceLine) {
|
if (isFenceLine) {
|
||||||
@ -72,7 +74,6 @@ function splitMessage(text, maxLength = MAX_DISCORD_MSG_LENGTH) {
|
|||||||
return chunks.map(c => c.endsWith('\n') ? c.slice(0, -1) : c);
|
return chunks.map(c => c.endsWith('\n') ? c.slice(0, -1) : c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the bot should respond to a message.
|
* Determine whether the bot should respond to a message.
|
||||||
* Controlled by enableMentions and enableReplies in config.
|
* Controlled by enableMentions and enableReplies in config.
|
||||||
@ -143,7 +144,7 @@ async function handleImage(client, message, resp, cfg) {
|
|||||||
const promptText = args.prompt;
|
const promptText = args.prompt;
|
||||||
// Determine number of images (1-10); DALL·E-3 only supports 1
|
// Determine number of images (1-10); DALL·E-3 only supports 1
|
||||||
let count = 1;
|
let count = 1;
|
||||||
if (args.n != null) {
|
if (args.n !== null) {
|
||||||
const nVal = typeof args.n === 'number' ? args.n : parseInt(args.n, 10);
|
const nVal = typeof args.n === 'number' ? args.n : parseInt(args.n, 10);
|
||||||
if (!Number.isNaN(nVal)) count = nVal;
|
if (!Number.isNaN(nVal)) count = nVal;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
import { _fs } from 'fs';
|
||||||
|
import { _path } from 'path';
|
||||||
|
|
||||||
|
import { _MessageFlags } from 'discord-api-types/v10';
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
|
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
// Placeholder info for template variables
|
// Placeholder info for template variables
|
||||||
const TEMPLATE_KEYS_INFO = 'Available keys: userName, userId, locationName, locationId, date, time, datetime, clientId';
|
const TEMPLATE_KEYS_INFO = 'Available keys: userName, userId, locationName, locationId, date, time, datetime, clientId';
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ export const commands = [
|
|||||||
.setAutocomplete(true)
|
.setAutocomplete(true)
|
||||||
),
|
),
|
||||||
async execute(interaction, client) {
|
async execute(interaction, client) {
|
||||||
const clientId = client.config.id;
|
const _clientId = client.config.id;
|
||||||
const versionId = interaction.options.getString('version');
|
const versionId = interaction.options.getString('version');
|
||||||
// Fetch prompt: live latest or selected historic
|
// Fetch prompt: live latest or selected historic
|
||||||
let promptText = client.responsesPrompt || '';
|
let promptText = client.responsesPrompt || '';
|
||||||
@ -87,12 +89,12 @@ export const commands = [
|
|||||||
const _clients = [];
|
const _clients = [];
|
||||||
|
|
||||||
export async function init(client, clientConfig) {
|
export async function init(client, clientConfig) {
|
||||||
const clientId = clientConfig.id;
|
const _clientId = client.config.id;
|
||||||
client.logger.info('[module:responsesPrompt] initialized');
|
client.logger.info('[module:responsesPrompt] initialized');
|
||||||
// Load live prompt (latest version)
|
// Load live prompt (latest version)
|
||||||
try {
|
try {
|
||||||
const { items } = await client.pb.collection('responses_prompts')
|
const { items } = await client.pb.collection('responses_prompts')
|
||||||
.getList(1, 1, { filter: `clientId="${clientId}"`, sort: '-created' });
|
.getList(1, 1, { filter: `clientId="${_clientId}"`, sort: '-created' });
|
||||||
client.responsesPrompt = items[0]?.prompt || '';
|
client.responsesPrompt = items[0]?.prompt || '';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`Error loading current prompt: ${err.message}`);
|
client.logger.error(`Error loading current prompt: ${err.message}`);
|
||||||
@ -106,7 +108,7 @@ export async function init(client, clientConfig) {
|
|||||||
if (focused.name === 'version') {
|
if (focused.name === 'version') {
|
||||||
try {
|
try {
|
||||||
const { items } = await client.pb.collection('responses_prompts')
|
const { items } = await client.pb.collection('responses_prompts')
|
||||||
.getList(1, 25, { filter: `clientId="${clientId}"`, sort: '-created' });
|
.getList(1, 25, { filter: `clientId="${_clientId}"`, sort: '-created' });
|
||||||
const choices = items.map(r => ({ name: new Date(r.created).toLocaleString(), value: r.id }));
|
const choices = items.map(r => ({ name: new Date(r.created).toLocaleString(), value: r.id }));
|
||||||
await interaction.respond(choices);
|
await interaction.respond(choices);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -129,9 +131,9 @@ export async function init(client, clientConfig) {
|
|||||||
}
|
}
|
||||||
const newPrompt = parts.join('\n');
|
const newPrompt = parts.join('\n');
|
||||||
// Persist new version
|
// Persist new version
|
||||||
let newRec;
|
let _newRec;
|
||||||
try {
|
try {
|
||||||
newRec = await client.pb.createOne('responses_prompts', { clientId, prompt: newPrompt, updatedBy: interaction.user.id });
|
_newRec = await client.pb.createOne('responses_prompts', { clientId: _clientId, prompt: newPrompt, updatedBy: interaction.user.id });
|
||||||
client.responsesPrompt = newPrompt;
|
client.responsesPrompt = newPrompt;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`Failed to save prompt: ${err.message}`);
|
client.logger.error(`Failed to save prompt: ${err.message}`);
|
||||||
@ -140,7 +142,7 @@ export async function init(client, clientConfig) {
|
|||||||
// Prune older versions beyond the 10 most recent
|
// Prune older versions beyond the 10 most recent
|
||||||
try {
|
try {
|
||||||
const { items } = await client.pb.collection('responses_prompts')
|
const { items } = await client.pb.collection('responses_prompts')
|
||||||
.getList(1, 100, { filter: `clientId="${clientId}"`, sort: '-created' });
|
.getList(1, 100, { filter: `clientId="${_clientId}"`, sort: '-created' });
|
||||||
const toDelete = items.map(r => r.id).slice(10);
|
const toDelete = items.map(r => r.id).slice(10);
|
||||||
for (const id of toDelete) {
|
for (const id of toDelete) {
|
||||||
await client.pb.deleteOne('responses_prompts', id);
|
await client.pb.deleteOne('responses_prompts', id);
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import fs from 'fs/promises';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
/**
|
/**
|
||||||
* Slash command module for '/query'.
|
* Slash command module for '/query'.
|
||||||
@ -5,10 +9,8 @@ import { MessageFlags } from 'discord-api-types/v10';
|
|||||||
* including optional image generation function calls.
|
* including optional image generation function calls.
|
||||||
*/
|
*/
|
||||||
import { SlashCommandBuilder, AttachmentBuilder, PermissionFlagsBits } from 'discord.js';
|
import { SlashCommandBuilder, AttachmentBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
|
|
||||||
import { expandTemplate } from '../_src/template.js';
|
import { expandTemplate } from '../_src/template.js';
|
||||||
import fs from 'fs/promises';
|
|
||||||
import path from 'path';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split long text into chunks safe for Discord messaging.
|
* Split long text into chunks safe for Discord messaging.
|
||||||
@ -58,7 +60,7 @@ async function handleImageInteraction(client, interaction, resp, cfg, ephemeral)
|
|||||||
const promptText = args.prompt;
|
const promptText = args.prompt;
|
||||||
// Determine number of images (1-10); DALL·E-3 only supports 1
|
// Determine number of images (1-10); DALL·E-3 only supports 1
|
||||||
let count = 1;
|
let count = 1;
|
||||||
if (args.n != null) {
|
if (args.n !== null) {
|
||||||
const nVal = typeof args.n === 'number' ? args.n : parseInt(args.n, 10);
|
const nVal = typeof args.n === 'number' ? args.n : parseInt(args.n, 10);
|
||||||
if (!Number.isNaN(nVal)) count = nVal;
|
if (!Number.isNaN(nVal)) count = nVal;
|
||||||
}
|
}
|
||||||
@ -298,7 +300,7 @@ export const commands = [
|
|||||||
required,
|
required,
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
},
|
},
|
||||||
strict: true,
|
strict: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cfg.tools?.webSearch) {
|
if (cfg.tools?.webSearch) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { _MessageFlags } from 'discord-api-types/v10';
|
||||||
// _opt/schangar.js
|
// _opt/schangar.js
|
||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
|
|
||||||
@ -100,12 +100,12 @@ export const commands = [
|
|||||||
if (typeof client.pb.updateOne === 'function') {
|
if (typeof client.pb.updateOne === 'function') {
|
||||||
await client.pb.updateOne('command_hangarsync', record.id, {
|
await client.pb.updateOne('command_hangarsync', record.id, {
|
||||||
userId: `${interaction.user.id}`,
|
userId: `${interaction.user.id}`,
|
||||||
epoch: `${syncEpoch}`,
|
epoch: `${syncEpoch}`
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await client.pb.collection('command_hangarsync').update(record.id, {
|
await client.pb.collection('command_hangarsync').update(record.id, {
|
||||||
userId: `${interaction.user.id}`,
|
userId: `${interaction.user.id}`,
|
||||||
epoch: `${syncEpoch}`,
|
epoch: `${syncEpoch}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
client.logger.info(`[cmd:hangarsync] Updated hangarsync for guild ${interaction.guildId} by user ${interaction.user.id}`);
|
client.logger.info(`[cmd:hangarsync] Updated hangarsync for guild ${interaction.guildId} by user ${interaction.user.id}`);
|
||||||
@ -115,13 +115,13 @@ export const commands = [
|
|||||||
await client.pb.createOne('command_hangarsync', {
|
await client.pb.createOne('command_hangarsync', {
|
||||||
guildId: `${interaction.guildId}`,
|
guildId: `${interaction.guildId}`,
|
||||||
userId: `${interaction.user.id}`,
|
userId: `${interaction.user.id}`,
|
||||||
epoch: `${syncEpoch}`,
|
epoch: `${syncEpoch}`
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await client.pb.collection('command_hangarsync').create({
|
await client.pb.collection('command_hangarsync').create({
|
||||||
guildId: `${interaction.guildId}`,
|
guildId: `${interaction.guildId}`,
|
||||||
userId: `${interaction.user.id}`,
|
userId: `${interaction.user.id}`,
|
||||||
epoch: `${syncEpoch}`,
|
epoch: `${syncEpoch}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
client.logger.info(`[cmd:hangarsync] Created new hangarsync for guild ${interaction.guildId} by user ${interaction.user.id}`);
|
client.logger.info(`[cmd:hangarsync] Created new hangarsync for guild ${interaction.guildId} by user ${interaction.user.id}`);
|
||||||
@ -131,7 +131,7 @@ export const commands = [
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
client.logger.error(`[cmd:hangarsync] Error: ${error.message}`);
|
client.logger.error(`[cmd:hangarsync] Error: ${error.message}`);
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: `Error syncing hangar status. Please try again later.`,
|
content: 'Error syncing hangar status. Please try again later.',
|
||||||
ephemeral: true
|
ephemeral: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -212,8 +212,8 @@ export const commands = [
|
|||||||
|
|
||||||
// Key positions in the cycle
|
// Key positions in the cycle
|
||||||
const allOffDuration = 5;
|
const allOffDuration = 5;
|
||||||
const turningGreenDuration = 5 * 24;
|
const _turningGreenDuration = 5 * 24 * 1000;
|
||||||
const turningOffDuration = 5 * 12;
|
const turningOffDuration = 5 * 12 * 1000;
|
||||||
|
|
||||||
// Calculate how much time has passed since the epoch
|
// Calculate how much time has passed since the epoch
|
||||||
const timeSinceEpoch = (currentTime - hangarSync.epoch) / (60 * 1000);
|
const timeSinceEpoch = (currentTime - hangarSync.epoch) / (60 * 1000);
|
||||||
@ -222,26 +222,26 @@ export const commands = [
|
|||||||
const cyclePosition = ((timeSinceEpoch % cycleDuration) + cycleDuration) % cycleDuration;
|
const cyclePosition = ((timeSinceEpoch % cycleDuration) + cycleDuration) % cycleDuration;
|
||||||
|
|
||||||
// Initialize stuff and things
|
// Initialize stuff and things
|
||||||
const lights = [":black_circle:", ":black_circle:", ":black_circle:", ":black_circle:", ":black_circle:"];
|
const lights = [':black_circle:', ':black_circle:', ':black_circle:', ':black_circle:', ':black_circle:'];
|
||||||
let minutesUntilNextPhase = 0;
|
let minutesUntilNextPhase = 0;
|
||||||
let currentPhase = "";
|
let currentPhase = '';
|
||||||
|
|
||||||
// If the epoch is now, we should be at the all-green position.
|
// If the epoch is now, we should be at the all-green position.
|
||||||
// From there, we need to determine where we are in the cycle.
|
// From there, we need to determine where we are in the cycle.
|
||||||
|
|
||||||
// Case 1: We're in the unlocked phase, right after epoch
|
// Case 1: We're in the unlocked phase, right after epoch
|
||||||
if (cyclePosition < turningOffDuration) {
|
if (cyclePosition < turningOffDuration) {
|
||||||
currentPhase = "Unlocked";
|
currentPhase = 'Unlocked';
|
||||||
|
|
||||||
// All lights start as green
|
// All lights start as green
|
||||||
lights.fill(":green_circle:");
|
lights.fill(':green_circle:');
|
||||||
|
|
||||||
// Calculate how many lights have turned off
|
// Calculate how many lights have turned off
|
||||||
const offLights = Math.floor(cyclePosition / 12);
|
const offLights = Math.floor(cyclePosition / 12);
|
||||||
|
|
||||||
// Set the appropriate number of lights to off
|
// Set the appropriate number of lights to off
|
||||||
for (let i = 0; i < offLights; i++) {
|
for (let i = 0; i < offLights; i++) {
|
||||||
lights[i] = ":black_circle:";
|
lights[i] = ':black_circle:';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate time until next light turns off
|
// Calculate time until next light turns off
|
||||||
@ -251,7 +251,7 @@ export const commands = [
|
|||||||
|
|
||||||
// Case 2: We're in the reset phase
|
// Case 2: We're in the reset phase
|
||||||
else if (cyclePosition < turningOffDuration + allOffDuration) {
|
else if (cyclePosition < turningOffDuration + allOffDuration) {
|
||||||
currentPhase = "Resetting";
|
currentPhase = 'Resetting';
|
||||||
|
|
||||||
// Lights are initialized "off", so do nothing with them
|
// Lights are initialized "off", so do nothing with them
|
||||||
|
|
||||||
@ -262,10 +262,10 @@ export const commands = [
|
|||||||
|
|
||||||
// Case 3: We're in the locked phase
|
// Case 3: We're in the locked phase
|
||||||
else {
|
else {
|
||||||
currentPhase = "Locked";
|
currentPhase = 'Locked';
|
||||||
|
|
||||||
// All lights start as red
|
// All lights start as red
|
||||||
lights.fill(":red_circle:");
|
lights.fill(':red_circle:');
|
||||||
|
|
||||||
// Calculate how many lights have turned green
|
// Calculate how many lights have turned green
|
||||||
const timeIntoPhase = cyclePosition - (turningOffDuration + allOffDuration);
|
const timeIntoPhase = cyclePosition - (turningOffDuration + allOffDuration);
|
||||||
@ -273,7 +273,7 @@ export const commands = [
|
|||||||
|
|
||||||
// Set the appropriate number of lights to green
|
// Set the appropriate number of lights to green
|
||||||
for (let i = 0; i < greenLights; i++) {
|
for (let i = 0; i < greenLights; i++) {
|
||||||
lights[i] = ":green_circle:";
|
lights[i] = ':green_circle:';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate time until next light turns green
|
// Calculate time until next light turns green
|
||||||
@ -323,7 +323,7 @@ export const commands = [
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
client.logger.error(`Error in hangarstatus command: ${error.message}`);
|
client.logger.error(`Error in hangarstatus command: ${error.message}`);
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: `Error retrieving hangar status. Please try again later.`,
|
content: 'Error retrieving hangar status. Please try again later.',
|
||||||
ephemeral: true
|
ephemeral: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ function isPocketBaseConnected(client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize module
|
// Initialize module
|
||||||
export const init = async (client, config) => {
|
export async function init(client, _config) {
|
||||||
client.logger.info('Initializing Star Citizen Hangar Status module');
|
client.logger.info('Initializing Star Citizen Hangar Status module');
|
||||||
|
|
||||||
// Check PocketBase connection
|
// Check PocketBase connection
|
||||||
@ -361,4 +361,4 @@ export const init = async (client, config) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client.logger.info('Star Citizen Hangar Status module initialized');
|
client.logger.info('Star Citizen Hangar Status module initialized');
|
||||||
};
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// Example of another module using scorekeeper
|
// Example of another module using scorekeeper
|
||||||
export const init = async (client, config) => {
|
export async function init(client, _config) {
|
||||||
// Set up message listener that adds input points when users chat
|
// Set up message listener that adds input points when users chat
|
||||||
client.on('messageCreate', async (message) => {
|
client.on('messageCreate', async (message) => {
|
||||||
if (message.author.bot) return;
|
if (message.author.bot) return;
|
||||||
@ -76,7 +76,7 @@ export const init = async (client, config) => {
|
|||||||
// If someone joined or left a channel, update tracking for everyone in that channel
|
// If someone joined or left a channel, update tracking for everyone in that channel
|
||||||
updateChannelUserTracking(client, oldState, newState);
|
updateChannelUserTracking(client, oldState, newState);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process when a user leaves a voice channel
|
* Process when a user leaves a voice channel
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
// opt/scorekeeper.js
|
// opt/scorekeeper.js
|
||||||
import cron from 'node-cron';
|
|
||||||
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } from 'discord.js';
|
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
|
import cron from 'node-cron';
|
||||||
|
|
||||||
// Module state container
|
// Module state container
|
||||||
const moduleState = {
|
const moduleState = {
|
||||||
cronJobs: new Map(), // Store cron jobs by client ID
|
cronJobs: new Map() // Store cron jobs by client ID
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -435,7 +435,8 @@ async function runDecay(client, guildId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.logger.info(`Decay completed for guild ${guildId}: ${updatedCount} records updated`);
|
const _reason = 'Automated decay';
|
||||||
|
client.logger.info(`[module:scorekeeper] Decayed ${updatedCount} records by ${client.config.scorekeeper.decay}% (${_reason})`);
|
||||||
return updatedCount;
|
return updatedCount;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
client.logger.error(`Error running decay: ${error.message}`);
|
client.logger.error(`Error running decay: ${error.message}`);
|
||||||
@ -609,7 +610,7 @@ export const commands = [
|
|||||||
{ name: 'Commendation Value', value: `-# ${commendationValue}`, inline: true },
|
{ name: 'Commendation Value', value: `-# ${commendationValue}`, inline: true },
|
||||||
{ name: 'Citation Value', value: `-# ${citationValue}`, inline: true },
|
{ name: 'Citation Value', value: `-# ${citationValue}`, inline: true },
|
||||||
{ name: 'Multiplier Formula', value: `-# 1 + (${scoreData.commendations} * ${commendationValue}) - (${scoreData.citations} * ${citationValue}) = ${multiplierValue.toFixed(2)}`, inline: false },
|
{ name: 'Multiplier Formula', value: `-# 1 + (${scoreData.commendations} * ${commendationValue}) - (${scoreData.citations} * ${citationValue}) = ${multiplierValue.toFixed(2)}`, inline: false },
|
||||||
{ name: 'Priority Score Formula', value: `-# ${multiplierValue.toFixed(2)} × ${scoreData.input} / (${scoreData.output} + ${baseOutput}) = ${scoreData.totalScore.toFixed(2)}`, inline: false },
|
{ name: 'Priority Score Formula', value: `-# ${multiplierValue.toFixed(2)} × ${scoreData.input} / (${scoreData.output} + ${baseOutput}) = ${scoreData.totalScore.toFixed(2)}`, inline: false }
|
||||||
)
|
)
|
||||||
.setFooter({ text: 'Last decay: ' + new Date(scoreData.lastDecay).toLocaleDateString() })
|
.setFooter({ text: 'Last decay: ' + new Date(scoreData.lastDecay).toLocaleDateString() })
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
@ -721,7 +722,7 @@ export const commands = [
|
|||||||
const cooldown = client.config.scorekeeper.cooldown || 0;
|
const cooldown = client.config.scorekeeper.cooldown || 0;
|
||||||
if (cooldown > 0) {
|
if (cooldown > 0) {
|
||||||
const recent = await client.pb.collection('scorekeeper_events').getList(1, 1, {
|
const recent = await client.pb.collection('scorekeeper_events').getList(1, 1, {
|
||||||
filter: `guildId = \"${guildId}\" && userId = \"${targetUser.id}\" && type = \"commendation\" && categoryId = \"${categoryId}\"`,
|
filter: `guildId = "${guildId}" && userId = "${targetUser.id}" && type = "commendation" && categoryId = "${categoryId}"`,
|
||||||
sort: '-created'
|
sort: '-created'
|
||||||
});
|
});
|
||||||
const lastItem = recent.items?.[0];
|
const lastItem = recent.items?.[0];
|
||||||
@ -806,12 +807,13 @@ export const commands = [
|
|||||||
}
|
}
|
||||||
const targetUser = interaction.options.getUser('user');
|
const targetUser = interaction.options.getUser('user');
|
||||||
const categoryId = interaction.options.getString('category');
|
const categoryId = interaction.options.getString('category');
|
||||||
|
const reason = interaction.options.getString('reason');
|
||||||
const amount = 1;
|
const amount = 1;
|
||||||
// Enforce per-category cooldown
|
// Enforce per-category cooldown
|
||||||
const cooldown = client.config.scorekeeper.cooldown || 0;
|
const cooldown = client.config.scorekeeper.cooldown || 0;
|
||||||
if (cooldown > 0) {
|
if (cooldown > 0) {
|
||||||
const recent = await client.pb.collection('scorekeeper_events').getList(1, 1, {
|
const recent = await client.pb.collection('scorekeeper_events').getList(1, 1, {
|
||||||
filter: `guildId = \"${guildId}\" && userId = \"${targetUser.id}\" && type = \"citation\" && categoryId = \"${categoryId}\"`,
|
filter: `guildId = "${guildId}" && userId = "${targetUser.id}" && type = "citation" && categoryId = "${categoryId}"`,
|
||||||
sort: '-created'
|
sort: '-created'
|
||||||
});
|
});
|
||||||
const lastItem = recent.items?.[0];
|
const lastItem = recent.items?.[0];
|
||||||
@ -834,7 +836,6 @@ export const commands = [
|
|||||||
try {
|
try {
|
||||||
await client.scorekeeper.addCitation(interaction.guildId, targetUser.id, amount);
|
await client.scorekeeper.addCitation(interaction.guildId, targetUser.id, amount);
|
||||||
// Log event
|
// Log event
|
||||||
// Log event (timestamp managed by PocketBase "created" field)
|
|
||||||
await client.pb.collection('scorekeeper_events').create({
|
await client.pb.collection('scorekeeper_events').create({
|
||||||
guildId: interaction.guildId,
|
guildId: interaction.guildId,
|
||||||
userId: targetUser.id,
|
userId: targetUser.id,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { SlashCommandBuilder, PermissionFlagsBits, ChannelType, EmbedBuilder } from 'discord.js';
|
|
||||||
import { MessageFlags } from 'discord-api-types/v10';
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
import { SlashCommandBuilder, PermissionFlagsBits, ChannelType, EmbedBuilder } from 'discord.js';
|
||||||
// Init function to handle autocomplete for /vc invite
|
// Init function to handle autocomplete for /vc invite
|
||||||
/**
|
/**
|
||||||
* tempvc module: temporary voice channel manager
|
* tempvc module: temporary voice channel manager
|
||||||
@ -266,7 +266,7 @@ export const commands = [
|
|||||||
} catch {}
|
} catch {}
|
||||||
await interaction.reply({ content: `Kicked <@${u.id}>.`, flags: MessageFlags.Ephemeral });
|
await interaction.reply({ content: `Kicked <@${u.id}>.`, flags: MessageFlags.Ephemeral });
|
||||||
} else if (sub === 'limit') {
|
} else if (sub === 'limit') {
|
||||||
let num = interaction.options.getInteger('number', true);
|
const num = interaction.options.getInteger('number', true);
|
||||||
// enforce range 0-99
|
// enforce range 0-99
|
||||||
if (num < 0 || num > 99) {
|
if (num < 0 || num > 99) {
|
||||||
return interaction.reply({ content: 'User limit must be between 0 (no limit) and 99.', flags: MessageFlags.Ephemeral });
|
return interaction.reply({ content: 'User limit must be between 0 (no limit) and 99.', flags: MessageFlags.Ephemeral });
|
||||||
@ -571,21 +571,21 @@ export async function init(client) {
|
|||||||
'• /vc kick <user> — Kick a user from this channel\n' +
|
'• /vc kick <user> — Kick a user from this channel\n' +
|
||||||
'• /vc role <role> — Set a role to allow/deny access\n' +
|
'• /vc role <role> — Set a role to allow/deny access\n' +
|
||||||
'• /vc mode <whitelist|blacklist> — Switch role mode\n' +
|
'• /vc mode <whitelist|blacklist> — Switch role mode\n' +
|
||||||
'• /vc limit <number> — Set user limit (0–99)',
|
'• /vc limit <number> — Set user limit (0–99)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Presets',
|
name: 'Presets',
|
||||||
value:
|
value:
|
||||||
'• /vc save <name> — Save current settings as a preset\n' +
|
'• /vc save <name> — Save current settings as a preset\n' +
|
||||||
'• /vc restore <name> — Restore settings from a preset\n' +
|
'• /vc restore <name> — Restore settings from a preset\n' +
|
||||||
'• /vc reset — Reset channel to default settings',
|
'• /vc reset — Reset channel to default settings'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Utilities',
|
name: 'Utilities',
|
||||||
value:
|
value:
|
||||||
'• /vc rename <new_name> — Rename this channel\n' +
|
'• /vc rename <new_name> — Rename this channel\n' +
|
||||||
'• /vc info — Show channel info\n' +
|
'• /vc info — Show channel info\n' +
|
||||||
'• /vc delete — Delete this channel',
|
'• /vc delete — Delete this channel'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await ch.send({ embeds: [helpEmbed] });
|
await ch.send({ embeds: [helpEmbed] });
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import winston from 'winston';
|
|
||||||
import 'winston-daily-rotate-file';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
import winston from 'winston';
|
||||||
|
import 'winston-daily-rotate-file';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const rootDir = path.dirname(__dirname);
|
const rootDir = path.dirname(__dirname);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
/**
|
/**
|
||||||
* expandTemplate: simple variable substitution in {{key}} placeholders.
|
* expandTemplate: simple variable substitution in {{key}} placeholders.
|
||||||
* @param {string} template - The template string with {{key}} tokens.
|
* @param {string} template - The template string with {{key}} tokens.
|
||||||
|
|||||||
208
config.js
208
config.js
@ -1,6 +1,38 @@
|
|||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
const logging = {
|
||||||
|
console: {
|
||||||
|
enabled: true,
|
||||||
|
colorize: true,
|
||||||
|
level: 'silly'
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
dateFormat: 'YYYY-MM-DD',
|
||||||
|
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
combined: {
|
||||||
|
enabled: true,
|
||||||
|
level: 'silly',
|
||||||
|
location: 'logs',
|
||||||
|
maxSize: '12m',
|
||||||
|
maxFiles: '30d'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
enabled: true,
|
||||||
|
level: 'error',
|
||||||
|
location: 'logs',
|
||||||
|
maxSize: '12m',
|
||||||
|
maxFiles: '365d'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const pocketbase = {
|
||||||
|
url: process.env.SHARED_POCKETBASE_URL,
|
||||||
|
username: process.env.SHARED_POCKETBASE_USERNAME,
|
||||||
|
password: process.env.SHARED_POCKETBASE_PASSWORD
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
clients: [
|
clients: [
|
||||||
@ -14,37 +46,9 @@ export default {
|
|||||||
token: process.env.SYSAI_DISCORD_TOKEN
|
token: process.env.SYSAI_DISCORD_TOKEN
|
||||||
},
|
},
|
||||||
|
|
||||||
logging: {
|
logging: { ...logging },
|
||||||
console: {
|
|
||||||
enabled: true,
|
|
||||||
colorize: true,
|
|
||||||
level: 'silly',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
dateFormat: 'YYYY-MM-DD',
|
|
||||||
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
combined: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'silly',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '30d',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'error',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '365d',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
pocketbase: {
|
pocketbase: { ...pocketbase },
|
||||||
url: process.env.SHARED_POCKETBASE_URL,
|
|
||||||
username: process.env.SHARED_POCKETBASE_USERNAME,
|
|
||||||
password: process.env.SHARED_POCKETBASE_PASSWORD
|
|
||||||
},
|
|
||||||
|
|
||||||
responses: {
|
responses: {
|
||||||
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
||||||
@ -58,7 +62,7 @@ export default {
|
|||||||
tools: {
|
tools: {
|
||||||
webSearch: true,
|
webSearch: true,
|
||||||
fileSearch: false,
|
fileSearch: false,
|
||||||
imageGeneration: true,
|
imageGeneration: true
|
||||||
},
|
},
|
||||||
imageGeneration: {
|
imageGeneration: {
|
||||||
defaultModel: 'gpt-image-1',
|
defaultModel: 'gpt-image-1',
|
||||||
@ -90,31 +94,7 @@ export default {
|
|||||||
token: process.env.ASOP_DISCORD_TOKEN
|
token: process.env.ASOP_DISCORD_TOKEN
|
||||||
},
|
},
|
||||||
|
|
||||||
logging: {
|
logging: { ...logging },
|
||||||
console: {
|
|
||||||
enabled: true,
|
|
||||||
colorize: true,
|
|
||||||
level: 'silly',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
dateFormat: 'YYYY-MM-DD',
|
|
||||||
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
combined: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'silly',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '30d',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'error',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '365d',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
condimentX: {
|
condimentX: {
|
||||||
dryRun: false,
|
dryRun: false,
|
||||||
@ -163,11 +143,7 @@ export default {
|
|||||||
openAIToken: process.env.SHARED_OPENAI_API_KEY
|
openAIToken: process.env.SHARED_OPENAI_API_KEY
|
||||||
},
|
},
|
||||||
|
|
||||||
pocketbase: {
|
pocketbase: { ...pocketbase },
|
||||||
url: process.env.SHARED_POCKETBASE_URL,
|
|
||||||
username: process.env.SHARED_POCKETBASE_USERNAME,
|
|
||||||
password: process.env.SHARED_POCKETBASE_PASSWORD
|
|
||||||
},
|
|
||||||
|
|
||||||
responses: {
|
responses: {
|
||||||
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
||||||
@ -181,7 +157,7 @@ export default {
|
|||||||
tools: {
|
tools: {
|
||||||
webSearch: false,
|
webSearch: false,
|
||||||
fileSearch: false,
|
fileSearch: false,
|
||||||
imageGeneration: true,
|
imageGeneration: true
|
||||||
},
|
},
|
||||||
imageGeneration: {
|
imageGeneration: {
|
||||||
defaultModel: 'gpt-image-1',
|
defaultModel: 'gpt-image-1',
|
||||||
@ -225,37 +201,9 @@ export default {
|
|||||||
token: process.env.CROWLEY_DISCORD_TOKEN
|
token: process.env.CROWLEY_DISCORD_TOKEN
|
||||||
},
|
},
|
||||||
|
|
||||||
logging: {
|
logging: { ...logging },
|
||||||
console: {
|
|
||||||
enabled: true,
|
|
||||||
colorize: true,
|
|
||||||
level: 'silly',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
dateFormat: 'YYYY-MM-DD',
|
|
||||||
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
combined: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'silly',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '30d',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'error',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '365d',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
pocketbase: {
|
pocketbase: { ...pocketbase },
|
||||||
url: process.env.SHARED_POCKETBASE_URL,
|
|
||||||
username: process.env.SHARED_POCKETBASE_USERNAME,
|
|
||||||
password: process.env.SHARED_POCKETBASE_PASSWORD
|
|
||||||
},
|
|
||||||
|
|
||||||
responses: {
|
responses: {
|
||||||
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
||||||
@ -269,7 +217,7 @@ export default {
|
|||||||
tools: {
|
tools: {
|
||||||
webSearch: false,
|
webSearch: false,
|
||||||
fileSearch: false,
|
fileSearch: false,
|
||||||
imageGeneration: false,
|
imageGeneration: false
|
||||||
},
|
},
|
||||||
imageGeneration: {
|
imageGeneration: {
|
||||||
defaultModel: 'gpt-image-1',
|
defaultModel: 'gpt-image-1',
|
||||||
@ -298,37 +246,9 @@ export default {
|
|||||||
token: process.env.GRANDPA_DISCORD_TOKEN
|
token: process.env.GRANDPA_DISCORD_TOKEN
|
||||||
},
|
},
|
||||||
|
|
||||||
logging: {
|
logging: { ...logging },
|
||||||
console: {
|
|
||||||
enabled: true,
|
|
||||||
colorize: true,
|
|
||||||
level: 'silly',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
dateFormat: 'YYYY-MM-DD',
|
|
||||||
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
combined: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'silly',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '30d',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'error',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '365d',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
pocketbase: {
|
pocketbase: { ...pocketbase },
|
||||||
url: process.env.SHARED_POCKETBASE_URL,
|
|
||||||
username: process.env.SHARED_POCKETBASE_USERNAME,
|
|
||||||
password: process.env.SHARED_POCKETBASE_PASSWORD
|
|
||||||
},
|
|
||||||
|
|
||||||
responses: {
|
responses: {
|
||||||
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
||||||
@ -342,7 +262,7 @@ export default {
|
|||||||
tools: {
|
tools: {
|
||||||
webSearch: false,
|
webSearch: false,
|
||||||
fileSearch: false,
|
fileSearch: false,
|
||||||
imageGeneration: false,
|
imageGeneration: false
|
||||||
},
|
},
|
||||||
imageGeneration: {
|
imageGeneration: {
|
||||||
defaultModel: 'gpt-image-1',
|
defaultModel: 'gpt-image-1',
|
||||||
@ -352,7 +272,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
responsesRandomizer: {
|
responsesRandomizer: {
|
||||||
chance: 0.01,
|
chance: 0.01
|
||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
'botUtils',
|
'botUtils',
|
||||||
@ -374,37 +294,9 @@ export default {
|
|||||||
token: process.env.SMUUUSH_DISCORD_TOKEN
|
token: process.env.SMUUUSH_DISCORD_TOKEN
|
||||||
},
|
},
|
||||||
|
|
||||||
logging: {
|
logging: { ...logging },
|
||||||
console: {
|
|
||||||
enabled: true,
|
|
||||||
colorize: true,
|
|
||||||
level: 'silly',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
dateFormat: 'YYYY-MM-DD',
|
|
||||||
timestampFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
combined: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'silly',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '30d',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
enabled: true,
|
|
||||||
level: 'error',
|
|
||||||
location: 'logs',
|
|
||||||
maxSize: '12m',
|
|
||||||
maxFiles: '365d',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
pocketbase: {
|
pocketbase: { ...pocketbase },
|
||||||
url: process.env.SHARED_POCKETBASE_URL,
|
|
||||||
username: process.env.SHARED_POCKETBASE_USERNAME,
|
|
||||||
password: process.env.SHARED_POCKETBASE_PASSWORD
|
|
||||||
},
|
|
||||||
|
|
||||||
responses: {
|
responses: {
|
||||||
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
apiKey: process.env.SHARED_OPENAI_API_KEY,
|
||||||
@ -418,7 +310,7 @@ export default {
|
|||||||
tools: {
|
tools: {
|
||||||
webSearch: false,
|
webSearch: false,
|
||||||
fileSearch: false,
|
fileSearch: false,
|
||||||
imageGeneration: true,
|
imageGeneration: true
|
||||||
},
|
},
|
||||||
imageGeneration: {
|
imageGeneration: {
|
||||||
defaultModel: 'gpt-image-1',
|
defaultModel: 'gpt-image-1',
|
||||||
@ -433,8 +325,8 @@ export default {
|
|||||||
'responses',
|
'responses',
|
||||||
'responsesPrompt',
|
'responsesPrompt',
|
||||||
'responsesQuery'
|
'responsesQuery'
|
||||||
],
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|||||||
6
index.js
6
index.js
@ -1,10 +1,11 @@
|
|||||||
import { Client, Collection, GatewayIntentBits } from 'discord.js';
|
import { Client, Collection, GatewayIntentBits } from 'discord.js';
|
||||||
|
|
||||||
|
import { ansi, wrapAnsi } from './_src/ansiColors.js';
|
||||||
|
import { loadModules } from './_src/loader.js';
|
||||||
import { createLogger } from './_src/logger.js';
|
import { createLogger } from './_src/logger.js';
|
||||||
import { initializePocketbase } from './_src/pocketbase.js';
|
import { initializePocketbase } from './_src/pocketbase.js';
|
||||||
import { loadModules } from './_src/loader.js';
|
|
||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
// For deprecated ephemeral option: convert to flags
|
// For deprecated ephemeral option: convert to flags
|
||||||
import { ansi, wrapAnsi } from './_src/ansiColors.js';
|
|
||||||
|
|
||||||
// Initialize Discord client
|
// Initialize Discord client
|
||||||
const initializeClient = async (clientConfig) => {
|
const initializeClient = async (clientConfig) => {
|
||||||
@ -45,7 +46,6 @@ const initializeClient = async (clientConfig) => {
|
|||||||
client.on('interactionCreate', async (interaction) => {
|
client.on('interactionCreate', async (interaction) => {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
|
||||||
const commandName = interaction.commandName;
|
const commandName = interaction.commandName;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
2883
package-lock.json
generated
2883
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,9 +11,12 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js",
|
"start": "node index.js",
|
||||||
"registry": "node registry.js"
|
"registry": "node registry.js",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@discordjs/rest": "^2.2.0",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.8.4",
|
||||||
"discord-api-types": "^0.37.120",
|
"discord-api-types": "^0.37.120",
|
||||||
"discord.js": "^14.18.0",
|
"discord.js": "^14.18.0",
|
||||||
@ -24,5 +27,9 @@
|
|||||||
"pocketbase": "^0.25.2",
|
"pocketbase": "^0.25.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0"
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-import": "^2.29.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
// registry.js
|
// registry.js
|
||||||
import { REST } from '@discordjs/rest';
|
|
||||||
import { Routes } from 'discord-api-types/v10';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
import { REST } from '@discordjs/rest'; // eslint-disable-line import/no-unresolved
|
||||||
|
import { Routes } from 'discord-api-types/v10';
|
||||||
|
|
||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
|
|
||||||
// Get directory name in ES module
|
// Get directory name in ES module
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user