diff --git a/discord/bot.ts b/discord/bot.ts index 3f188f9..983f4d4 100644 --- a/discord/bot.ts +++ b/discord/bot.ts @@ -13,7 +13,16 @@ import { TextChannel, User } from 'discord.js'; -import { db, openDb, reactionEmojis, recordReaction, sync } from './util'; +import { + db, + logInfo, + logError, + openDb, + reactionEmojis, + recordReaction, + sync +} from './util'; + const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions], @@ -21,11 +30,12 @@ const client = new Client({ }); client.once(Events.ClientReady, async () => { - console.log('[bot] Ready.'); + logInfo('[bot] Ready.'); for (let i = 0; i < reactionEmojis.length; ++i) - console.log(`[bot] config: reaction_${i + 1} = ${reactionEmojis[i]}`); + logInfo(`[bot] config: reaction_${i + 1} = ${reactionEmojis[i]}`); }); + async function onMessageReactionChanged(reaction: MessageReaction | PartialMessageReaction, user: User) { // When a reaction is received, check if the structure is partial @@ -34,7 +44,7 @@ async function onMessageReactionChanged(reaction: MessageReaction | PartialMessa try { await reaction.fetch(); } catch (error) { - console.error('[bot] Something went wrong when fetching the reaction:', error); + logError('[bot] Something went wrong when fetching the reaction:', error); // Return as `reaction.message.author` may be undefined/null return; } @@ -44,14 +54,14 @@ async function onMessageReactionChanged(reaction: MessageReaction | PartialMessa try { await reaction.message.fetch(); } catch (error) { - console.error('[bot] Something went wrong when fetching the message:', error); + logError('[bot] Something went wrong when fetching the message:', error); // Return as `reaction.message.author` may be undefined/null return; } } // Now the message has been cached and is fully available - console.log(`[bot] ${reaction.message.author.id}'s message reaction count changed: ${reaction.emoji.name}x${reaction.count}`); + logInfo(`[bot] ${reaction.message.author.id}'s message reaction count changed: ${reaction.emoji.name}x${reaction.count}`); await recordReaction( reaction); } @@ -70,13 +80,13 @@ async function scheduleRandomMessage(firstTime = false) const channel = await client.channels.fetch(process.env.MOTD_CHANNEL); const randomMessage = await fetchMotd(); await channel.send(randomMessage); - console.log(`[bot] Sent MOTD: ${randomMessage}`); + logInfo(`[bot] Sent MOTD: ${randomMessage}`); } // wait between 2-8 hours const timeoutMins = Math.random() * 360 + 120; const scheduledTime = new Date(); scheduledTime.setMinutes(scheduledTime.getMinutes() + timeoutMins); - console.log(`[bot] Next MOTD: ${scheduledTime}`); + logInfo(`[bot] Next MOTD: ${scheduledTime}`); setTimeout(scheduleRandomMessage, timeoutMins * 60 * 1000); } @@ -84,12 +94,12 @@ client.on(Events.MessageReactionAdd, onMessageReactionChanged); client.on(Events.MessageReactionRemove, onMessageReactionChanged); async function startup() { - console.log("[db] Opening..."); + logInfo("[db] Opening..."); await openDb(); - console.log("[db] Migrating..."); + logInfo("[db] Migrating..."); await db.migrate(); - console.log("[db] Ready."); - console.log("[bot] Logging in..."); + logInfo("[db] Ready."); + logInfo("[bot] Logging in..."); await client.login(process.env.TOKEN); await sync(client.guilds); if (process.env.ENABLE_MOTD) { diff --git a/discord/sync.ts b/discord/sync.ts index 766400f..7dee347 100644 --- a/discord/sync.ts +++ b/discord/sync.ts @@ -5,7 +5,7 @@ */ import { Client, Events, GatewayIntentBits, IntentsBitField, Partials } from 'discord.js'; -import { db, openDb, reactionEmojis, sync } from './util'; +import { db, logInfo, openDb, reactionEmojis, sync } from './util'; const client = new Client({ intents: [GatewayIntentBits.MessageContent, IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages], @@ -13,18 +13,18 @@ const client = new Client({ }); client.once(Events.ClientReady, async () => { - console.log('[bot] Ready.'); + logInfo('[bot] Ready.'); for (let i = 0; i < reactionEmojis.length; ++i) - console.log(`[bot] config: reaction_${i + 1} = ${reactionEmojis[i]}`); + logInfo(`[bot] config: reaction_${i + 1} = ${reactionEmojis[i]}`); }); async function startup() { - console.log("[db] Opening..."); + logInfo("[db] Opening..."); await openDb(); - console.log("[db] Migrating..."); + logInfo("[db] Migrating..."); await db.migrate(); - console.log("[db] Ready."); - console.log("[bot] Logging in..."); + logInfo("[db] Ready."); + logInfo("[bot] Logging in..."); await client.login(process.env.TOKEN); await sync(client.guilds); process.exit(0); diff --git a/discord/util.ts b/discord/util.ts index 75ffae7..16d1388 100644 --- a/discord/util.ts +++ b/discord/util.ts @@ -15,6 +15,19 @@ import { ScoreboardMessageRow } from '../models'; const reactionEmojis: string[] = process.env.REACTIONS.split(','); let db: Database = null; + +function logInfo(...data) { + console.log(`[${new Date()}] ${data.join(' ')}`); +} + +function logWarn(...data) { + console.warn(`[${new Date()}] ${data.join(' ')}`); +} + +function logError(...data) { + console.error(`[${new Date()}] ${data.join(' ')}`); +} + async function openDb() { db = await open({ filename: 'db.sqlite', @@ -38,14 +51,14 @@ function userAvatarPath(user: User) async function downloadUserAvatar(user: User) { - console.log(`[bot] Downloading ${user.id}'s avatar...`); + logInfo(`[bot] Downloading ${user.id}'s avatar...`); const file = createWriteStream(userAvatarPath(user)); return new Promise(resolve => { httpGet(user.displayAvatarURL(), res => { res.pipe(file); file.on('finish', () => { file.close(); - console.log(`[bot] Finished downloading ${user.id}'s avatar.`); + logInfo(`[bot] Finished downloading ${user.id}'s avatar.`); resolve(); }); }); @@ -72,7 +85,7 @@ async function refreshUserReactionTotalCount(user: User, emoji_idx: number) if (!existsSync(userAvatarPath(user))) { await downloadUserAvatar(user); } - console.log(`[bot] Refreshed ${user.id}'s ${reactionEmojis[emoji_idx - 1]} count.`); + logInfo(`[bot] Refreshed ${user.id}'s ${reactionEmojis[emoji_idx - 1]} count.`); } async function recordReaction(reaction: MessageReaction) @@ -95,9 +108,9 @@ async function recordReaction(reaction: MessageReaction) reaction.message.id ); await refreshUserReactionTotalCount(reaction.message.author, emojiIdx); - console.log(`[bot] Recorded ${reaction.emoji.name}x${reaction.count} in database.`); + logInfo(`[bot] Recorded ${reaction.emoji.name}x${reaction.count} in database.`); } catch (error) { - console.error('[bot] Something went wrong when updating the database:', error); + logError('[bot] Something went wrong when updating the database:', error); return; } } @@ -105,14 +118,14 @@ async function recordReaction(reaction: MessageReaction) async function sync(guilds: GuildManager) { const guild = await guilds.fetch(process.env.GUILD); if (!guild) { - console.error(`[bot] FATAL: guild ${guild.id} not found!`); + logError(`[bot] FATAL: guild ${guild.id} not found!`); return 1; } - console.log(`[bot] Entered guild ${guild.id}`); + logInfo(`[bot] Entered guild ${guild.id}`); const channels = await guild.channels.fetch(); const textChannels = > channels.filter(c => c && 'messages' in c && c.isTextBased); for (const [id, textChannel] of textChannels) { - console.log(`[bot] Found text channel ${id}`); + logInfo(`[bot] Found text channel ${id}`); const oldestMsg = await db.get( 'SELECT * FROM messages WHERE guild = ? AND channel = ? ORDER BY id ASC LIMIT 1', guild.id, @@ -136,15 +149,16 @@ async function sync(guilds: GuildManager) { newMessagesAfter = await textChannel.messages.fetch({ after, limit: 100 }); messagesCount += newMessagesAfter.size; - console.log(`[bot] [${id}] Fetched ${messagesCount} messages (+${newMessagesBefore.size} older, ${newMessagesAfter.size} newer)`); + logInfo(`[bot] [${id}] Fetched ${messagesCount} messages (+${newMessagesBefore.size} older, ${newMessagesAfter.size} newer)`); - const reactions = newMessagesBefore.flatMap(m => m.reactions.cache) + const reactions = newMessagesBefore + .flatMap(m => m.reactions.cache) .concat(newMessagesAfter.flatMap(m => m.reactions.cache)); for (const [_, reaction] of reactions) { await recordReaction(reaction); } reactionsCount += reactions.size; - console.log(`[bot] [${id}] Recorded ${reactionsCount} reactions (+${reactions.size}).`); + logInfo(`[bot] [${id}] Recorded ${reactionsCount} reactions (+${reactions.size}).`); if (newMessagesBefore.size > 0) { before = newMessagesBefore.last().id; @@ -153,11 +167,11 @@ async function sync(guilds: GuildManager) { after = newMessagesAfter.first().id; } } while (newMessagesBefore.size === 100 || newMessagesAfter.size === 100); - console.log(`[bot] [${id}] Done.`); + logInfo(`[bot] [${id}] Done.`); } catch (err) { - console.warn(`[bot] [${id}] Failed to fetch messages and reactions: ${err}`); + logWarn(`[bot] [${id}] Failed to fetch messages and reactions: ${err}`); } } } -export { db, clearDb, openDb, reactionEmojis, recordReaction, sync }; +export { db, clearDb, logError, logInfo, logWarn, openDb, reactionEmojis, recordReaction, sync }; diff --git a/server.ts b/server.ts index b89c0e9..a773354 100644 --- a/server.ts +++ b/server.ts @@ -8,6 +8,7 @@ import { Database, open } from 'sqlite'; import express = require('express'); import 'dotenv/config'; import { ScoreboardMessageRow, ScoreboardUserRow } from './models'; +import { logInfo } from './discord/util'; const app = express(); app.use(express.static('public')); @@ -36,8 +37,8 @@ app.get('/', async (req, res) => { }); app.listen(port, async () => { - console.log('[web] Opening database...'); + logInfo('[web] Opening database...'); db = await openDb(); - console.log('[web] Database ready.'); - console.log(`[web] Listening on port ${port}`); + logInfo('[web] Database ready.'); + logInfo(`[web] Listening on port ${port}`); });