Skip to content

Commit

Permalink
#patch persistent messages update every 10 mins, cleaned up structure…
Browse files Browse the repository at this point in the history
…/formatting
  • Loading branch information
jgaribsin committed Mar 9, 2024
1 parent dace045 commit 5d69ec8
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 151 deletions.
10 changes: 2 additions & 8 deletions src/api-wrapper/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@ import {
WarInfo,
Status,
MergedPlanetData,
PlanetStatus,
GlobalEvent,
Faction,
MergedCampaignData,
MergedPlanetEventData,
} from './types';
import {
getFactionName,
getPlanetEventType,
getPlanetName,
getSectorName,
} from './mapping';
import {getFactionName, getPlanetEventType, getPlanetName} from './mapping';
import {existsSync, mkdirSync, writeFileSync} from 'fs';
import {compressFile, dayjs, logger, writeGzipJson} from '../handlers';
import {dayjs, logger} from '../handlers';
import path from 'path';
import {getAllPlanets} from './planets';
import axios from 'axios';
Expand Down
12 changes: 0 additions & 12 deletions src/commands/campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,7 @@ import {
EmbedBuilder,
SlashCommandBuilder,
} from 'discord.js';
import {
Faction,
MergedCampaignData,
MergedPlanetData,
MergedPlanetEventData,
getAllCampaigns,
getCampaignByPlanetName,
getUtcTime,
} from '../api-wrapper';
import {Command} from '../interfaces';
import {FACTION_COLOUR, FOOTER_MESSAGE} from './_components';
import {campaignEmbeds} from '../handlers';

const command: Command = {
Expand All @@ -38,7 +28,6 @@ const command: Command = {
)
),
run: async interaction => {
// TODO: perform any checks if needed (eg. perm checks)
const subcommand = interaction.options.data[0].name;

await subcmds[subcommand](interaction);
Expand All @@ -47,7 +36,6 @@ const command: Command = {

const subcmds: {[key: string]: (job: CommandInteraction) => Promise<void>} = {
// hashmap of subcommands
// TODO
list,
info,
};
Expand Down
6 changes: 1 addition & 5 deletions src/commands/discord.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
CommandInteraction,
EmbedBuilder,
SlashCommandBuilder,
} from 'discord.js';
import {EmbedBuilder, SlashCommandBuilder} from 'discord.js';
import {Command} from '../interfaces';
import {EMBED_COLOUR, FOOTER_MESSAGE} from './_components';
import {config} from '../config';
Expand Down
9 changes: 7 additions & 2 deletions src/commands/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import {
} from 'discord.js';
import {Command} from '../interfaces';
import {EMBED_COLOUR, FOOTER_MESSAGE} from './_components';
import {client, missingChannelPerms, sleep, warStatusEmbeds} from '../handlers';
import {
client,
missingChannelPerms,
sleep,
warStatusPersistentMessage,
} from '../handlers';
import {isProd} from '../config';
import {
db,
Expand Down Expand Up @@ -126,7 +131,7 @@ async function status(interaction: CommandInteraction) {
const discordMsg = await messageChannel.messages.fetch(message.id);
if (discordMsg)
await discordMsg.edit({
embeds: warStatusEmbeds(),
embeds: warStatusPersistentMessage(),
});
}

Expand Down
5 changes: 5 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const configObj: Record<string, string | number | undefined> = {
BOT_TOKEN: process.env.BOT_TOKEN,
BOT_OWNER: process.env.BOT_OWNER || '319226464786710539',

// Cron job intervals
PERSISTENT_MESSAGE_INTERVAL: '*/10 * * * *', // every 10 minutes
API_UPDATE_INTERVAL: '*/10 * * * * *', // every 10 seconds
STATUS_UPDATE_INTERVAL: '*/3 * * * * *', // every 3 seconds

// Database config
DATABASE_URL: process.env.DATABASE_URL,

Expand Down
1 change: 0 additions & 1 deletion src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import postgres from 'postgres';
import {config} from '../config';
import * as schema from './schema';
import {announcementChannels} from './schema';
import {eq} from 'drizzle-orm';

const {DATABASE_URL} = config;

Expand Down
106 changes: 10 additions & 96 deletions src/events/onReady.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import {
ActivityType,
ChannelType,
Client,
DiscordAPIError,
REST,
Routes,
} from 'discord.js';
import {ActivityType, Client, REST, Routes} from 'discord.js';
import {schedule} from 'node-cron';
import {commandHash, commandList, presenceCmds} from '../commands';
import {config, isProd} from '../config';
import {config} from '../config';
import {getData, mappedNames} from '../api-wrapper';
import {db, eq, persistentMessages} from '../db';
import {logger, warStatusEmbeds} from '../handlers';
import {and} from 'drizzle-orm';
import {logger, updateMessages} from '../handlers';

// bot client token, for use with discord API
const BOT_TOKEN = config.BOT_TOKEN;
const PERSISTENT_MESSAGE_INTERVAL = config.PERSISTENT_MESSAGE_INTERVAL;
const API_UPDATE_INTERVAL = config.API_UPDATE_INTERVAL;
const STATUS_UPDATE_INTERVAL = config.STATUS_UPDATE_INTERVAL;

const onReady = async (client: Client) => {
if (!client.user) throw Error('Client not initialised');
Expand Down Expand Up @@ -46,6 +40,7 @@ const onReady = async (client: Client) => {
type: 'startup',
});

start = Date.now();
// get api data on startup
await getData().then(data => {
mappedNames.planets = data[801].Planets.map(x => x.name);
Expand All @@ -59,99 +54,18 @@ const onReady = async (client: Client) => {
});

// cron schedule to update messages every hour
schedule('0 * * * *', async () => {
// measure time taken to update all persistent messages
start = Date.now();

const embeds = {
curr_war: warStatusEmbeds(),
};

const messages = await db.query.persistentMessages.findMany({
where: and(
eq(persistentMessages.deleted, false),
eq(persistentMessages.production, isProd)
),
});

const promises: Promise<any>[] = [];
for (const message of messages) {
const {messageId, channelId, type: messageType} = message;

try {
// try fetching the channel, may throw '50001', bot can't see channel
const messageChannel = await client.channels.fetch(channelId);
if (
messageChannel &&
(messageChannel.type === ChannelType.GuildText ||
messageChannel.type === ChannelType.PublicThread)
) {
// try fetching the message, may throw '10008', message doesn't exist (deleted?)
const discordMsg = await messageChannel.messages.fetch(messageId);
if (discordMsg)
switch (messageType) {
case 'war_status':
promises.push(
discordMsg.edit({
embeds: embeds.curr_war,
})
);
break;
}
}
} catch (err) {
const discordErr = err as DiscordAPIError;
// discord API error codes
// https://github.com/meew0/discord-api-docs-1/blob/master/docs/topics/RESPONSE_CODES.md#json-error-response
switch (discordErr.code) {
case 10003: // Unknown channel
promises.push(
db
.update(persistentMessages)
.set({deleted: true})
.where(eq(persistentMessages.messageId, messageId))
);
break;
case 10008: // Unknown message
promises.push(
db
.update(persistentMessages)
.set({deleted: true})
.where(eq(persistentMessages.messageId, messageId))
);
break;
case 50001: // Missing access
promises.push(
db
.update(persistentMessages)
.set({deleted: true})
.where(eq(persistentMessages.messageId, messageId))
);
break;
case 50005: // Cannot edit a message authored by another user
break;
}
}
}

// await all message edits completion
await Promise.all(promises);
time = `${Date.now() - start}ms`;
logger.info(`Updated ${messages.length} messages in ${time}`, {
type: 'info',
});
});
schedule(PERSISTENT_MESSAGE_INTERVAL, () => updateMessages());

// cron schedule to update api data every 10 seconds
schedule('*/10 * * * * *', () => {
schedule(API_UPDATE_INTERVAL, () => {
getData().then(data => {
mappedNames.planets = data[801].Planets.map(x => x.name);
mappedNames.campaignPlanets = data[801].Campaigns.map(x => x.planetName);
});
});

// cron schedule to update presence every 3 seconds
schedule('*/3 * * * * *', () => {
schedule(STATUS_UPDATE_INTERVAL, () => {
if (client.user) {
if (client.user.presence.activities[0]) {
const prev = client.user.presence.activities[0].name;
Expand Down
20 changes: 0 additions & 20 deletions src/handlers/custom.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,8 @@
import {CommandInteraction, EmbedBuilder, ColorResolvable} from 'discord.js';
import {getAllPlanets} from '../api-wrapper';
import {FOOTER_MESSAGE, EMBED_COLOUR} from '../commands/_components';

export function sleep(ms: number) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}

export function missingChannelPerms(interaction: CommandInteraction) {
return {
embeds: [
new EmbedBuilder()
.setAuthor({
name: interaction.user.tag,
iconURL: interaction.user.avatarURL() || undefined,
})
.setTitle(`Permission Denied`)
.setDescription(
'This command creates a public, persistent message. To avoid inconviencing other users, it requires moderator permissions. '
)
.setFooter({text: FOOTER_MESSAGE})
.setColor(EMBED_COLOUR as ColorResolvable)
.setTimestamp(),
],
};
}
31 changes: 24 additions & 7 deletions src/handlers/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
Faction,
MergedCampaignData,
MergedPlanetEventData,
getAllActivePlanets,
getAllCampaigns,
getAllPlayers,
getCampaignByPlanetName,
Expand All @@ -29,15 +28,34 @@ export function commandErrorEmbed(
name: client.user?.tag || '',
iconURL: client.user?.avatarURL() || undefined,
})
.setTitle(`Something Went Wrong )=`)
.setTitle('Something Went Wrong )=')
.setDescription(
`There was an issue trying to execute \`/${
interaction.isCommand()
? interaction.commandName
: interaction.customId
}\`! ` +
`The issue has been logged and will be looked into. Feel free to try again shortly. ` +
`If the problem persists, please let Major know`
'The issue has been logged and will be looked into. Feel free to try again shortly. ' +
'If the problem persists, please let Major know'
)
.setFooter({text: FOOTER_MESSAGE})
.setColor(EMBED_COLOUR as ColorResolvable)
.setTimestamp(),
],
};
}

export function missingChannelPerms(interaction: CommandInteraction) {
return {
embeds: [
new EmbedBuilder()
.setAuthor({
name: interaction.user.tag,
iconURL: interaction.user.avatarURL() || undefined,
})
.setTitle('Permission Denied')
.setDescription(
'This command creates a public, persistent message. To avoid inconviencing other users, it requires moderator permissions. '
)
.setFooter({text: FOOTER_MESSAGE})
.setColor(EMBED_COLOUR as ColorResolvable)
Expand All @@ -54,7 +72,7 @@ export function ownerCommandEmbed(interaction: CommandInteraction) {
name: interaction.user.tag,
iconURL: interaction.user.avatarURL() || undefined,
})
.setTitle(`Permission Denied`)
.setTitle('Permission Denied')
.setDescription('This command is only available to Owners!')
.setFooter({text: FOOTER_MESSAGE})
.setColor(EMBED_COLOUR as ColorResolvable)
Expand All @@ -71,7 +89,7 @@ export function adminCommandEmbed(interaction: CommandInteraction) {
name: interaction.user.tag,
iconURL: interaction.user.avatarURL() || undefined,
})
.setTitle(`Permission Denied`)
.setTitle('Permission Denied')
.setDescription('This command is only available to Admins!')
.setFooter({text: FOOTER_MESSAGE})
.setColor(EMBED_COLOUR as ColorResolvable)
Expand Down Expand Up @@ -140,7 +158,6 @@ export function warStatusEmbeds() {
.addFields(status['Terminids']);

const embeds = [automatonEmbed, terminidEmbed];
embeds[embeds.length - 1].setFooter({text: FOOTER_MESSAGE}).setTimestamp();

return embeds;
}
Expand Down
1 change: 1 addition & 0 deletions src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './dates';
export * from './embed';
export * from './gzip';
export * from './logging';
export * from './update';
Loading

0 comments on commit 5d69ec8

Please sign in to comment.