Skip to content

Commit

Permalink
move exports out of app.ts to make testing easier whenever I feel lik…
Browse files Browse the repository at this point in the history
…e adding some tests
  • Loading branch information
TiltedToast committed Feb 8, 2024
1 parent 1a87e1b commit 9cfefe8
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 87 deletions.
31 changes: 8 additions & 23 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import dedent from "dedent";
import {
Client as DiscordClient,
GatewayIntentBits,
Partials,
type Snowflake,
TextChannel,
} from "discord.js";
import { Client, GatewayIntentBits, Partials, TextChannel } from "discord.js";
import "dotenv/config";
import { existsSync, rmSync } from "node:fs";
import strftime from "strftime";
import { avoidDbSleeping, startStatusLoop } from "./commands/loops.ts";
import { avoidDbSleeping } from "./commands/loops.ts";
import { LOG_CHANNEL } from "./config.ts";
import { db } from "./db/index.ts";
import { prefixes, statuses } from "./db/schema.ts";
import type { Status } from "./db/types.ts";
import handleInteraction from "./handlers/interactions.ts";
import handleMessage from "./handlers/messages.ts";
import * as prefixHandler from "./handlers/prefixes.ts";
import * as statusHandler from "./handlers/statuses.ts";
import { isDev } from "./helpers/utils.ts";

const startTime = Date.now();

export const client = new DiscordClient({
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
Expand All @@ -32,9 +25,6 @@ export const client = new DiscordClient({
],
partials: [Partials.Channel],
});
export const prefixMap = new Map<Snowflake, string>();
export let statusArr: Status[] = [];
export let botIsLoading = true;

client.once("ready", async () => {
const time = strftime("%d/%m/%Y %H:%M:%S");
Expand All @@ -49,16 +39,11 @@ client.once("ready", async () => {
console.log("------------------");

// Puts all statuses into an array to avoid reading the database on every status change
statusArr = await db.select().from(statuses);

if (statusArr.length) startStatusLoop(client).catch(console.error);
statusHandler.init(client).catch(console.error);
prefixHandler.init().catch(console.error);
prefixHandler.setLoading();
avoidDbSleeping().catch(console.error);

for (const prefixDoc of await db.select().from(prefixes)) {
prefixMap.set(prefixDoc.serverId, prefixDoc.prefix);
}
botIsLoading = false;

const logChannel = client.channels.cache.get(LOG_CHANNEL) as TextChannel;
// const catFactChannel = client.channels.cache.get(CAT_FACT_CHANNEL) as TextChannel;
// startCatFactLoop(catFactChannel);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/database.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DatabaseError } from "@planetscale/database";
import { Message, PermissionFlagsBits, codeBlock } from "discord.js";
import { fromZodError } from "zod-validation-error";
import { prefixMap, statusArr } from "../app.ts";
import { prefixMap } from "../handlers/prefixes.ts";
import { statusArr } from "../handlers/statuses.ts";
import { BOT_OWNERS } from "../config.ts";
import { PSConnection, db, updatePrefix as updatePrefixDB } from "../db/index.ts";
import { statuses } from "../db/schema.ts";
Expand Down
3 changes: 1 addition & 2 deletions src/commands/emoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
import Fuse from "fuse.js";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { client } from "../app.ts";
import { FileSizeLimit } from "../helpers/types.ts";
import {
createTemp,
Expand Down Expand Up @@ -97,7 +96,7 @@ export async function addEmoji(message: Message, prefix: string) {
const [channelId, msgId] = matchedArr.slice(1);

try {
const ch = await client.channels.fetch(channelId);
const ch = await message.client.channels.fetch(channelId);
if (!ch?.isTextBased()) {
return await message.channel.send("Somehow this is not a text channel?");
}
Expand Down
39 changes: 3 additions & 36 deletions src/commands/loops.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import type { Client, TextChannel } from "discord.js";
import { statusArr } from "../app.ts";
import type { TextChannel } from "discord.js";
import { db } from "../db/index.ts";
import { errorLogs } from "../db/schema.ts";
import { CatFactResponseSchema, StatusType, type CatFactResponse } from "../helpers/types.ts";
import {
isDev,
randomElementFromArray,
randomIntFromRange,
sleep as utilSleep,
} from "../helpers/utils.ts";
import { CatFactResponseSchema, type CatFactResponse } from "../helpers/types.ts";
import { isDev, randomIntFromRange, sleep as utilSleep } from "../helpers/utils.ts";

export async function startCatFactLoop(channel: TextChannel) {
const sleep = async () => await utilSleep(randomIntFromRange(54000000, 86400000)); // 15h-24h
Expand All @@ -35,33 +29,6 @@ export async function startCatFactLoop(channel: TextChannel) {
}
}

/**
* Starts a loop which periodically changes the status to a random entry in the database
* @param {Client} client Discord client which is used to access the API
*/
export async function startStatusLoop(client: Client) {
while (true) {
const status = setRandomStatus(client);
if (!status) break;
await utilSleep(randomIntFromRange(300000, 900000)); // 5m-15m
}
}

/**
* Grabs a random status from the database and sets it as the status of the bot
* @param client Discord client used to access the API
*/
function setRandomStatus(client: Client) {
if (!client.user) return console.error("Could not set status, client user is undefined");
const randStatus = randomElementFromArray(statusArr);

return client.user.setActivity({
name: randStatus.type !== "CUSTOM" ? randStatus.status : "Custom status",
state: randStatus.type === "CUSTOM" ? randStatus.status : undefined,
type: StatusType[randStatus.type],
});
}

export async function avoidDbSleeping() {
const sixDaysinSeconds = 518400;

Expand Down
4 changes: 2 additions & 2 deletions src/commands/miscellaneous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import { all, create } from "mathjs";
import { exec } from "node:child_process";
import { promisify } from "node:util";
import { client, prefixMap } from "../app.ts";
import {
BOT_NAME,
DEFAULT_PREFIX,
Expand Down Expand Up @@ -52,6 +51,7 @@ import {
sleep,
writeUpdateFile,
} from "../helpers/utils.ts";
import { prefixMap } from "../handlers/prefixes.ts";

const { WOLFRAM_ALPHA_APP_ID, EXCHANGE_API_KEY } = process.env;

Expand Down Expand Up @@ -614,7 +614,7 @@ export async function bye(message: Message) {

// Closes the MongoDB connection and stops the running daemon via pm2
await message.channel.send("Bai baaaaaaaai!!");
await client.destroy();
await message.client.destroy();
exec("bun run stop");
process.exit(0);
}
3 changes: 1 addition & 2 deletions src/handlers/interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
codeBlock,
userMention,
} from "discord.js";
import { client } from "../app.ts";
import { convert, helpCmd, patUser, urban, urbanEmbeds } from "../commands/miscellaneous.ts";
import { sub } from "../commands/reddit.ts";
import {
Expand Down Expand Up @@ -38,7 +37,7 @@ export default async function handleInteraction(interaction: Interaction) {

const channel = DEV_CHANNELS.includes(interaction.channel?.id ?? "")
? interaction.channel
: await client.channels.fetch(LOG_CHANNEL);
: await interaction.client.channels.fetch(LOG_CHANNEL);

if (!channel?.isTextBased()) return;

Expand Down
2 changes: 1 addition & 1 deletion src/handlers/messages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Message } from "discord.js";
import { botIsLoading, prefixMap } from "../app.ts";
import * as db from "../commands/database.ts";
import * as emoji from "../commands/emoji.ts";
import * as imgProcess from "../commands/imgProcess.ts";
Expand All @@ -11,6 +10,7 @@ import { DEFAULT_PREFIX, DEV_PREFIX, RELOAD_PREFIX } from "../config.ts";
import { prefixes } from "../db/schema.ts";
import type { MessageCommandData } from "../helpers/types.ts";
import { clientNoPermissions, errorLog, isDev, isMikuTrigger } from "../helpers/utils.ts";
import { prefixMap, botIsLoading } from "./prefixes.ts";

export default async function handleMessage(message: Message) {
const guildClient = await message.guild?.members.fetchMe();
Expand Down
16 changes: 16 additions & 0 deletions src/handlers/prefixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Snowflake } from "discord.js";
import { db } from "../db/index.ts";
import { prefixes } from "../db/schema.ts";

export const prefixMap = new Map<Snowflake, string>();
export let botIsLoading = false;

export function setLoading() {
botIsLoading = true;
}

export async function init() {
for (const prefixDoc of await db.select().from(prefixes)) {
prefixMap.set(prefixDoc.serverId, prefixDoc.prefix);
}
}
40 changes: 40 additions & 0 deletions src/handlers/statuses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Status } from "../db/types.ts";
import { db } from "../db/index.ts";
import { statuses } from "../db/schema.ts";
import type { Client } from "discord.js";
import { StatusType } from "../helpers/types.ts";
import { randomIntFromRange, randomElementFromArray, sleep } from "../helpers/utils.ts";

export let statusArr: Status[] = [];

export async function init(client: Client) {
statusArr = await db.select().from(statuses);
if (statusArr.length) startStatusLoop(client).catch(console.error);
}

/**
* Starts a loop which periodically changes the status to a random entry in the database
* @param {Client} client Discord client which is used to access the API
*/
export async function startStatusLoop(client: Client) {
while (true) {
const status = setRandomStatus(client);
if (!status) break;
await sleep(randomIntFromRange(300000, 900000)); // 5m-15m
}
}

/**
* Grabs a random status from the database and sets it as the status of the bot
* @param client Discord client used to access the API
*/
function setRandomStatus(client: Client) {
if (!client.user) return console.error("Could not set status, client user is undefined");
const randStatus = randomElementFromArray(statusArr);

return client.user.setActivity({
name: randStatus.type !== "CUSTOM" ? randStatus.status : "Custom status",
state: randStatus.type === "CUSTOM" ? randStatus.status : undefined,
type: StatusType[randStatus.type],
});
}
2 changes: 1 addition & 1 deletion src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const PairConversionResponseSchema = z.union([
export type PairConversionResponse = z.infer<typeof PairConversionResponseSchema>;

export type ErrorLogOptions = {
message?: Message;
message: Message;
errorObject: Error;
};

Expand Down
25 changes: 8 additions & 17 deletions src/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import os from "os";
import sharp from "sharp";
import strftime from "strftime";
import { table } from "table";
import { client } from "../app.ts";
import { BOT_OWNERS, DEV_CHANNELS, LOG_CHANNEL, OWNER_NAME } from "../config.ts";
import { db } from "../db/index.ts";
import { errorLogs } from "../db/schema.ts";
Expand Down Expand Up @@ -136,14 +135,17 @@ export function isBotOwner(user: User): boolean {
}

export function isMikuTrigger(message: Message, reactCmd: string): boolean {
if (!client.user) return false;
if (!message.client.user) return false;
if (message.content.startsWith(`$${reactCmd}`) && message.type === MessageType.Reply) {
const repliedMsg = message.channel.messages.resolve(message.reference?.messageId ?? "");
if (!repliedMsg) return false;
if (repliedMsg.author.id === client.user.id) return true;
if (repliedMsg.author.id === message.client.user.id) return true;
}

return message.content.startsWith(`$${reactCmd}`) && message.content.includes(client.user.id);
return (
message.content.startsWith(`$${reactCmd}`) &&
message.content.includes(message.client.user.id)
);
}

export function setEmbedArr<T>(args: UpdateEmbedArrParams<T>): void {
Expand Down Expand Up @@ -248,7 +250,7 @@ export async function getUserObjectPingId(message: Message): Promise<User | unde

try {
if (!Number.isNaN(parseInt(pingOrIdString)))
user = await client.users.fetch(pingOrIdString);
user = await message.client.users.fetch(pingOrIdString);
if (!user && pingOrIdString.startsWith("<")) user = message.mentions.users.first();
return user ? user : undefined;
} catch (err) {
Expand Down Expand Up @@ -286,17 +288,6 @@ export function errorLog({ message, errorObject }: ErrorLogOptions) {
let channel: Channel | undefined;
let errorMessage: string;

if (message === undefined) {
channel = client.channels.cache.get(LOG_CHANNEL) as TextChannel;
errorMessage = dedent`
Unhandled Rejection
${errorObject.stack ?? "Stack missing"}
<@${BOT_OWNERS[0]}>`;
return channel.send(errorMessage);
}

const commandUsed =
message.content.substring(0, 500) +
(message.content.substring(0, 500) !== message.content ? " ..." : "");
Expand Down Expand Up @@ -351,7 +342,7 @@ export function errorLog({ message, errorObject }: ErrorLogOptions) {
if (DEV_CHANNELS.includes(message.channel.id)) {
channel = message.channel;
} else {
channel = client.channels.cache.get(LOG_CHANNEL);
channel = message.client.channels.cache.get(LOG_CHANNEL);
}
return (channel as TextChannel).send(errorMessage);
}
Expand Down
2 changes: 0 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"lib": ["ESNext"],
"target": "ESNext",
"moduleDetection": "force",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "Bundler",
Expand Down

0 comments on commit 9cfefe8

Please sign in to comment.