swapped to a new db locally hosted
This commit is contained in:
@@ -2,12 +2,25 @@ const { Client, GatewayIntentBits, Collection } = require('discord.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const deployCommands = require('./deploy-commands');
|
||||
const { readDb } = require('../backend/db');
|
||||
// legacy local db is available as a fallback in some commands via require('../../backend/db')
|
||||
const api = require('./api');
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] });
|
||||
|
||||
client.commands = new Collection();
|
||||
|
||||
// In-memory cache of server settings to allow backend to push updates and make toggles immediate
|
||||
const guildSettingsCache = new Map();
|
||||
|
||||
function setGuildSettings(guildId, settings) {
|
||||
if (!guildId) return;
|
||||
guildSettingsCache.set(guildId, settings || {});
|
||||
}
|
||||
|
||||
function getGuildSettingsFromCache(guildId) {
|
||||
return guildSettingsCache.get(guildId) || null;
|
||||
}
|
||||
|
||||
const commandHandler = require('./handlers/command-handler');
|
||||
const eventHandler = require('./handlers/event-handler');
|
||||
|
||||
@@ -20,12 +33,16 @@ client.on('interactionCreate', async interaction => {
|
||||
const id = interaction.customId || '';
|
||||
if (id.startsWith('copy_inv_')) {
|
||||
const code = id.replace('copy_inv_', '');
|
||||
const db = readDb();
|
||||
const invites = (db[interaction.guildId] && db[interaction.guildId].invites) ? db[interaction.guildId].invites : [];
|
||||
const inv = invites.find(i => i.code === code);
|
||||
if (inv) {
|
||||
await interaction.reply({ content: `Invite: ${inv.url}`, ephemeral: true });
|
||||
} else {
|
||||
try {
|
||||
const invites = await api.listInvites(interaction.guildId);
|
||||
const inv = (invites || []).find(i => i.code === code);
|
||||
if (inv) {
|
||||
await interaction.reply({ content: `Invite: ${inv.url}`, ephemeral: true });
|
||||
} else {
|
||||
await interaction.reply({ content: 'Invite not found.', ephemeral: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error fetching invites via API:', e);
|
||||
await interaction.reply({ content: 'Invite not found.', ephemeral: true });
|
||||
}
|
||||
} else if (id.startsWith('delete_inv_')) {
|
||||
@@ -37,12 +54,13 @@ client.on('interactionCreate', async interaction => {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// call backend delete endpoint
|
||||
const fetch = require('node-fetch');
|
||||
const backendBase = process.env.BACKEND_BASE || `http://${process.env.HOST || '127.0.0.1'}:${process.env.PORT || 3002}`;
|
||||
const url = `${backendBase}/api/servers/${interaction.guildId}/invites/${code}`;
|
||||
await fetch(url, { method: 'DELETE' });
|
||||
await interaction.reply({ content: 'Invite deleted.', ephemeral: true });
|
||||
// call backend delete endpoint via helper
|
||||
const ok = await api.deleteInvite(interaction.guildId, code);
|
||||
if (ok) {
|
||||
await interaction.reply({ content: 'Invite deleted.', ephemeral: true });
|
||||
} else {
|
||||
await interaction.reply({ content: 'Failed to delete invite via API.', ephemeral: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error deleting invite via API:', e);
|
||||
await interaction.reply({ content: 'Failed to delete invite.', ephemeral: true });
|
||||
@@ -57,11 +75,28 @@ client.on('interactionCreate', async interaction => {
|
||||
|
||||
if (!command) return;
|
||||
|
||||
// Check per-guild toggles
|
||||
// Check per-guild toggles via Postgres (directly) for lower latency and reliability
|
||||
try {
|
||||
const db = readDb();
|
||||
const guildSettings = db[interaction.guildId] || {};
|
||||
const toggles = guildSettings.commandToggles || {};
|
||||
// authoritative path: always try the backend HTTP API first so separate processes stay in sync
|
||||
let guildSettings = await api.getServerSettings(interaction.guildId) || {};
|
||||
// if API didn't return anything useful, try in-memory cache then direct DB as fallbacks
|
||||
if (!guildSettings || Object.keys(guildSettings).length === 0) {
|
||||
guildSettings = getGuildSettingsFromCache(interaction.guildId) || {};
|
||||
if (!guildSettings || Object.keys(guildSettings).length === 0) {
|
||||
try {
|
||||
const pg = require('../backend/pg');
|
||||
guildSettings = (await pg.getServerSettings(interaction.guildId)) || {};
|
||||
} catch (pgErr) {
|
||||
// leave guildSettings empty
|
||||
}
|
||||
}
|
||||
}
|
||||
// Normalize legacy flags into commandToggles for backward compatibility
|
||||
const toggles = { ...(guildSettings.commandToggles || {}) };
|
||||
// Example legacy flag mapping: pingCommand -> commandToggles.ping
|
||||
if (typeof guildSettings.pingCommand !== 'undefined') {
|
||||
toggles.ping = !!guildSettings.pingCommand;
|
||||
}
|
||||
const protectedCommands = ['manage-commands', 'help'];
|
||||
|
||||
// If command is protected, always allow
|
||||
@@ -97,4 +132,82 @@ const login = () => {
|
||||
client.login(process.env.DISCORD_BOT_TOKEN);
|
||||
}
|
||||
|
||||
module.exports = { login, client };
|
||||
// Allow backend to trigger a live announcement for debugging
|
||||
async function announceLive(guildId, stream) {
|
||||
try {
|
||||
const guild = await client.guilds.fetch(guildId).catch(() => null);
|
||||
if (!guild) return { success: false, message: 'Guild not found' };
|
||||
const settings = await (require('../backend/pg')).getServerSettings(guildId).catch(() => null) || await (require('./api')).getServerSettings(guildId).catch(() => ({}));
|
||||
const liveSettings = (settings && settings.liveNotifications) || {};
|
||||
if (!liveSettings.enabled) return { success: false, message: 'Live notifications disabled' };
|
||||
const channelId = liveSettings.channelId;
|
||||
if (!channelId) return { success: false, message: 'No channel configured' };
|
||||
const channel = await guild.channels.fetch(channelId).catch(() => null);
|
||||
if (!channel) return { success: false, message: 'Channel not found' };
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x9146FF)
|
||||
.setTitle(stream.title || `${stream.user_name} is live`)
|
||||
.setURL(stream.url)
|
||||
.setAuthor({ name: stream.user_name, iconURL: stream.profile_image_url || undefined, url: stream.url })
|
||||
.setThumbnail(stream.thumbnail_url || stream.profile_image_url || undefined)
|
||||
.addFields(
|
||||
{ name: 'Category', value: stream.game_name || 'Unknown', inline: true },
|
||||
{ name: 'Viewers', value: String(stream.viewer_count || 0), inline: true }
|
||||
)
|
||||
.setDescription(stream.description || '')
|
||||
.setFooter({ text: `ehchadservices • Started: ${stream.started_at ? new Date(stream.started_at).toLocaleString() : 'unknown'}` });
|
||||
await channel.send({ embeds: [embed] });
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
console.error('announceLive failed:', e && e.message ? e.message : e);
|
||||
return { success: false, message: e && e.message ? e.message : 'unknown error' };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { login, client, setGuildSettings, getGuildSettingsFromCache, announceLive };
|
||||
|
||||
// Start twitch watcher when client is ready (use 'clientReady' as the event name)
|
||||
try {
|
||||
const watcher = require('./twitch-watcher');
|
||||
// discord.js renamed the ready event to clientReady; the event loader registers
|
||||
// handlers based on event.name so we listen for the same 'clientReady' here.
|
||||
client.once('clientReady', () => {
|
||||
// start polling in background
|
||||
watcher.poll(client).catch(err => console.error('Twitch watcher failed to start:', err));
|
||||
});
|
||||
process.on('exit', () => { watcher.stop(); });
|
||||
process.on('SIGINT', () => { watcher.stop(); process.exit(); });
|
||||
} catch (e) {
|
||||
// ignore if watcher not available
|
||||
}
|
||||
|
||||
// --- Optional push receiver (so backend can notify a remote bot process) ---
|
||||
try {
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const botPort = process.env.BOT_PUSH_PORT || process.env.BOT_PORT || null;
|
||||
const botSecret = process.env.BOT_SECRET || null;
|
||||
if (botPort) {
|
||||
const app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.post('/internal/set-settings', (req, res) => {
|
||||
try {
|
||||
if (botSecret) {
|
||||
const provided = req.headers['x-bot-secret'];
|
||||
if (!provided || provided !== botSecret) return res.status(401).json({ success: false, message: 'Unauthorized' });
|
||||
}
|
||||
const { guildId, settings } = req.body || {};
|
||||
if (!guildId) return res.status(400).json({ success: false, message: 'Missing guildId' });
|
||||
setGuildSettings(guildId, settings || {});
|
||||
return res.json({ success: true });
|
||||
} catch (e) {
|
||||
console.error('Error in bot push receiver:', e);
|
||||
return res.status(500).json({ success: false });
|
||||
}
|
||||
});
|
||||
app.listen(botPort, () => console.log(`Bot push receiver listening on port ${botPort}`));
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore if express isn't available in this environment
|
||||
}
|
||||
Reference in New Issue
Block a user