Random messages, logging refactor

This commit is contained in:
James Shiffer 2023-10-08 19:10:47 -07:00
parent 81b75d3546
commit 2809c29c9a
4 changed files with 61 additions and 36 deletions

View File

@ -13,7 +13,16 @@ import {
TextChannel, TextChannel,
User User
} from 'discord.js'; } 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({ const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions], intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions],
@ -21,11 +30,12 @@ const client = new Client({
}); });
client.once(Events.ClientReady, async () => { client.once(Events.ClientReady, async () => {
console.log('[bot] Ready.'); logInfo('[bot] Ready.');
for (let i = 0; i < reactionEmojis.length; ++i) 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) async function onMessageReactionChanged(reaction: MessageReaction | PartialMessageReaction, user: User)
{ {
// When a reaction is received, check if the structure is partial // When a reaction is received, check if the structure is partial
@ -34,7 +44,7 @@ async function onMessageReactionChanged(reaction: MessageReaction | PartialMessa
try { try {
await reaction.fetch(); await reaction.fetch();
} catch (error) { } 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 as `reaction.message.author` may be undefined/null
return; return;
} }
@ -44,14 +54,14 @@ async function onMessageReactionChanged(reaction: MessageReaction | PartialMessa
try { try {
await reaction.message.fetch(); await reaction.message.fetch();
} catch (error) { } 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 as `reaction.message.author` may be undefined/null
return; return;
} }
} }
// Now the message has been cached and is fully available // 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(<MessageReaction> reaction); await recordReaction(<MessageReaction> reaction);
} }
@ -70,13 +80,13 @@ async function scheduleRandomMessage(firstTime = false)
const channel = <TextChannel> await client.channels.fetch(process.env.MOTD_CHANNEL); const channel = <TextChannel> await client.channels.fetch(process.env.MOTD_CHANNEL);
const randomMessage = await fetchMotd(); const randomMessage = await fetchMotd();
await channel.send(randomMessage); await channel.send(randomMessage);
console.log(`[bot] Sent MOTD: ${randomMessage}`); logInfo(`[bot] Sent MOTD: ${randomMessage}`);
} }
// wait between 2-8 hours // wait between 2-8 hours
const timeoutMins = Math.random() * 360 + 120; const timeoutMins = Math.random() * 360 + 120;
const scheduledTime = new Date(); const scheduledTime = new Date();
scheduledTime.setMinutes(scheduledTime.getMinutes() + timeoutMins); scheduledTime.setMinutes(scheduledTime.getMinutes() + timeoutMins);
console.log(`[bot] Next MOTD: ${scheduledTime}`); logInfo(`[bot] Next MOTD: ${scheduledTime}`);
setTimeout(scheduleRandomMessage, timeoutMins * 60 * 1000); setTimeout(scheduleRandomMessage, timeoutMins * 60 * 1000);
} }
@ -84,12 +94,12 @@ client.on(Events.MessageReactionAdd, onMessageReactionChanged);
client.on(Events.MessageReactionRemove, onMessageReactionChanged); client.on(Events.MessageReactionRemove, onMessageReactionChanged);
async function startup() { async function startup() {
console.log("[db] Opening..."); logInfo("[db] Opening...");
await openDb(); await openDb();
console.log("[db] Migrating..."); logInfo("[db] Migrating...");
await db.migrate(); await db.migrate();
console.log("[db] Ready."); logInfo("[db] Ready.");
console.log("[bot] Logging in..."); logInfo("[bot] Logging in...");
await client.login(process.env.TOKEN); await client.login(process.env.TOKEN);
await sync(client.guilds); await sync(client.guilds);
if (process.env.ENABLE_MOTD) { if (process.env.ENABLE_MOTD) {

View File

@ -5,7 +5,7 @@
*/ */
import { Client, Events, GatewayIntentBits, IntentsBitField, Partials } from 'discord.js'; 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({ const client = new Client({
intents: [GatewayIntentBits.MessageContent, IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages], intents: [GatewayIntentBits.MessageContent, IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages],
@ -13,18 +13,18 @@ const client = new Client({
}); });
client.once(Events.ClientReady, async () => { client.once(Events.ClientReady, async () => {
console.log('[bot] Ready.'); logInfo('[bot] Ready.');
for (let i = 0; i < reactionEmojis.length; ++i) 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() { async function startup() {
console.log("[db] Opening..."); logInfo("[db] Opening...");
await openDb(); await openDb();
console.log("[db] Migrating..."); logInfo("[db] Migrating...");
await db.migrate(); await db.migrate();
console.log("[db] Ready."); logInfo("[db] Ready.");
console.log("[bot] Logging in..."); logInfo("[bot] Logging in...");
await client.login(process.env.TOKEN); await client.login(process.env.TOKEN);
await sync(client.guilds); await sync(client.guilds);
process.exit(0); process.exit(0);

View File

@ -15,6 +15,19 @@ import { ScoreboardMessageRow } from '../models';
const reactionEmojis: string[] = process.env.REACTIONS.split(','); const reactionEmojis: string[] = process.env.REACTIONS.split(',');
let db: Database = null; 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() { async function openDb() {
db = await open({ db = await open({
filename: 'db.sqlite', filename: 'db.sqlite',
@ -38,14 +51,14 @@ function userAvatarPath(user: User)
async function downloadUserAvatar(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)); const file = createWriteStream(userAvatarPath(user));
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
httpGet(user.displayAvatarURL(), res => { httpGet(user.displayAvatarURL(), res => {
res.pipe(file); res.pipe(file);
file.on('finish', () => { file.on('finish', () => {
file.close(); file.close();
console.log(`[bot] Finished downloading ${user.id}'s avatar.`); logInfo(`[bot] Finished downloading ${user.id}'s avatar.`);
resolve(); resolve();
}); });
}); });
@ -72,7 +85,7 @@ async function refreshUserReactionTotalCount(user: User, emoji_idx: number)
if (!existsSync(userAvatarPath(user))) { if (!existsSync(userAvatarPath(user))) {
await downloadUserAvatar(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) async function recordReaction(reaction: MessageReaction)
@ -95,9 +108,9 @@ async function recordReaction(reaction: MessageReaction)
reaction.message.id reaction.message.id
); );
await refreshUserReactionTotalCount(reaction.message.author, emojiIdx); 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) { } catch (error) {
console.error('[bot] Something went wrong when updating the database:', error); logError('[bot] Something went wrong when updating the database:', error);
return; return;
} }
} }
@ -105,14 +118,14 @@ async function recordReaction(reaction: MessageReaction)
async function sync(guilds: GuildManager) { async function sync(guilds: GuildManager) {
const guild = await guilds.fetch(process.env.GUILD); const guild = await guilds.fetch(process.env.GUILD);
if (!guild) { if (!guild) {
console.error(`[bot] FATAL: guild ${guild.id} not found!`); logError(`[bot] FATAL: guild ${guild.id} not found!`);
return 1; return 1;
} }
console.log(`[bot] Entered guild ${guild.id}`); logInfo(`[bot] Entered guild ${guild.id}`);
const channels = await guild.channels.fetch(); const channels = await guild.channels.fetch();
const textChannels = <Collection<string, GuildTextBasedChannel>> channels.filter(c => c && 'messages' in c && c.isTextBased); const textChannels = <Collection<string, GuildTextBasedChannel>> channels.filter(c => c && 'messages' in c && c.isTextBased);
for (const [id, textChannel] of textChannels) { for (const [id, textChannel] of textChannels) {
console.log(`[bot] Found text channel ${id}`); logInfo(`[bot] Found text channel ${id}`);
const oldestMsg = await db.get<ScoreboardMessageRow>( const oldestMsg = await db.get<ScoreboardMessageRow>(
'SELECT * FROM messages WHERE guild = ? AND channel = ? ORDER BY id ASC LIMIT 1', 'SELECT * FROM messages WHERE guild = ? AND channel = ? ORDER BY id ASC LIMIT 1',
guild.id, guild.id,
@ -136,15 +149,16 @@ async function sync(guilds: GuildManager) {
newMessagesAfter = await textChannel.messages.fetch({ after, limit: 100 }); newMessagesAfter = await textChannel.messages.fetch({ after, limit: 100 });
messagesCount += newMessagesAfter.size; 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<MessageReaction>(m => m.reactions.cache) const reactions = newMessagesBefore
.flatMap<MessageReaction>(m => m.reactions.cache)
.concat(newMessagesAfter.flatMap<MessageReaction>(m => m.reactions.cache)); .concat(newMessagesAfter.flatMap<MessageReaction>(m => m.reactions.cache));
for (const [_, reaction] of reactions) { for (const [_, reaction] of reactions) {
await recordReaction(reaction); await recordReaction(reaction);
} }
reactionsCount += reactions.size; reactionsCount += reactions.size;
console.log(`[bot] [${id}] Recorded ${reactionsCount} reactions (+${reactions.size}).`); logInfo(`[bot] [${id}] Recorded ${reactionsCount} reactions (+${reactions.size}).`);
if (newMessagesBefore.size > 0) { if (newMessagesBefore.size > 0) {
before = newMessagesBefore.last().id; before = newMessagesBefore.last().id;
@ -153,11 +167,11 @@ async function sync(guilds: GuildManager) {
after = newMessagesAfter.first().id; after = newMessagesAfter.first().id;
} }
} while (newMessagesBefore.size === 100 || newMessagesAfter.size === 100); } while (newMessagesBefore.size === 100 || newMessagesAfter.size === 100);
console.log(`[bot] [${id}] Done.`); logInfo(`[bot] [${id}] Done.`);
} catch (err) { } 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 };

View File

@ -8,6 +8,7 @@ import { Database, open } from 'sqlite';
import express = require('express'); import express = require('express');
import 'dotenv/config'; import 'dotenv/config';
import { ScoreboardMessageRow, ScoreboardUserRow } from './models'; import { ScoreboardMessageRow, ScoreboardUserRow } from './models';
import { logInfo } from './discord/util';
const app = express(); const app = express();
app.use(express.static('public')); app.use(express.static('public'));
@ -36,8 +37,8 @@ app.get('/', async (req, res) => {
}); });
app.listen(port, async () => { app.listen(port, async () => {
console.log('[web] Opening database...'); logInfo('[web] Opening database...');
db = await openDb(); db = await openDb();
console.log('[web] Database ready.'); logInfo('[web] Database ready.');
console.log(`[web] Listening on port ${port}`); logInfo(`[web] Listening on port ${port}`);
}); });