Skip to content

Commit

Permalink
huge service refactor:
Browse files Browse the repository at this point in the history
services are now guranteed to exist at all times
  • Loading branch information
Techbot121 authored and Meta Construct committed Jan 4, 2025
1 parent 105cc2d commit f4a2374
Show file tree
Hide file tree
Showing 42 changed files with 348 additions and 382 deletions.
38 changes: 35 additions & 3 deletions app/Container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ type ProviderFactory = { (container: Container): Service | Promise<Service> }[];
export class Container {
readonly app: App;
private providers: ProviderFactory;
private services: ServiceMap = {};
private services = {} as ServiceMap;
private initPromises = new Map<string, Promise<Service>>();

constructor(app: App, providers: ProviderFactory) {
this.app = app;
Expand All @@ -28,7 +29,38 @@ export class Container {
this.services[service.name] = service;
}

getService<Name extends string>(type: Name): ServiceMap[Name] {
return this.services[type];
async getService<T extends keyof ServiceMap>(name: T): Promise<ServiceMap[T]> {
const service = this.services[name];
if (service) {
return service as ServiceMap[T];
}

// If already initializing, wait for it
if (this.initPromises.has(String(name))) {
return this.initPromises.get(String(name)) as Promise<ServiceMap[T]>;
}

// Find the provider
const provider = this.providers.find(p => {
const temp = p(this);
if (temp instanceof Promise) {
return temp.then(s => s.name === name);
}
return temp.name === name;
});

if (!provider) {
throw new Error(`Service ${String(name)} not found`);
}

// Initialize the service
const promise = Promise.resolve(provider(this)).then(service => {
this.services[name] = service;
this.initPromises.delete(String(name));
return service as ServiceMap[T];
});

this.initPromises.set(String(name), promise);
return promise;
}
}
8 changes: 0 additions & 8 deletions app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,5 @@ export class App {

constructor() {
this.container = new Container(this, providers);

this.init();
}

async init(): Promise<void> {
for (const provider of this.container.getProviders()) {
await this.container.addService(provider(this.container));
}
}
}
18 changes: 9 additions & 9 deletions app/services/DiscordMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ export class DiscordMetadata extends Service {

constructor(container: Container) {
super(container);
const sql = this.container.getService("SQL");
const bot = this.container.getService("DiscordBot");
const bans = this.container.getService("Bans");
if (!sql || !bot || !bans) return;
this.sql = sql;
this.bot = bot;
this.bans = bans;
this.initServices();
}

private async initServices() {
this.sql = await this.container.getService("SQL");
this.bot = await this.container.getService("DiscordBot");
this.bans = await this.container.getService("Bans");
}

private async getAccessToken(userId: string, data: LocalDatabaseEntry) {
Expand Down Expand Up @@ -202,8 +202,8 @@ export class DiscordMetadata extends Service {
if (query3[0]) {
nick = bytea.toString("utf-8").replace(/<[^>]*>/g, "");
} else {
const steam = this.bot.container.getService("Steam");
const summary = await steam?.getUserSummaries(data.steam_id);
const steam = await this.bot.container.getService("Steam");
const summary = await steam.getUserSummaries(data.steam_id);
nick = summary?.personaname;
}

Expand Down
12 changes: 7 additions & 5 deletions app/services/IRC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@ export class IRC extends Service {
this.client.say(config.relayIRCChannel, `\u000314[Discord]\u000f ${text}`);
}

constructor(container: Container) {
super(container);
const bot = this.container.getService("DiscordBot");

if (!bot) return;
private async setupDiscord() {
const bot = await this.container.getService("DiscordBot");
// Discord
bot.discord.on("messageCreate", async msg => {
if (msg.channelId === config.relayDiscordChannel) {
Expand All @@ -76,6 +73,11 @@ export class IRC extends Service {
);
}
});
}

constructor(container: Container) {
super(container);
this.setupDiscord();
// IRC
this.client.on("registered", () => {
this.client.say("NICKSERV", `IDENTIFY ${config.nick} ${config.password}`);
Expand Down
167 changes: 83 additions & 84 deletions app/services/Motd.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Container } from "@/app/Container";
import { Data, DiscordBot, Service } from "@/app/services";
import { DiscordBot, Service } from "@/app/services";
import { scheduleJob } from "node-schedule";
import FormData from "form-data";
import axios, { AxiosResponse } from "axios";
Expand Down Expand Up @@ -93,7 +93,6 @@ export class Motd extends Service {
images: ImgurImage[] = [];
lastimages: ImgurImage[] = [];

private data: Data;
private bot: DiscordBot;
private rerolls = 0;

Expand All @@ -102,14 +101,10 @@ export class Motd extends Service {
constructor(container: Container) {
super(container);
this.messages = [];
this.initServices();
scheduleJob("0 12 * * *", this.executeMessageJob.bind(this));
// scheduleJob("0 20 * * *", this.executeImageJob.bind(this));
// scheduleJob("0 0 * * 0", this.clearImageAlbumAndHistory.bind(this));
const data = this.container.getService("Data");
const bot = this.container.getService("DiscordBot");
if (!data || !bot) return;
this.data = data;
this.bot = bot;
// axios
// .get(`https://api.imgur.com/3/album/${config.imgurAlbumId}/images`, {
// headers: {
Expand All @@ -122,6 +117,10 @@ export class Motd extends Service {
// }
// });
}
private async initServices() {
const bot = await this.container.getService("DiscordBot");
this.bot = bot;
}

pushMessage(msg: string): void {
msg = msg.trim();
Expand Down Expand Up @@ -160,8 +159,9 @@ export class Motd extends Service {
const msg: string = this.messages[(Math.random() * this.messages.length) | 0];
this.messages = [];
if (msg == null || msg.length === 0) return;
this.data.lastMotd = msg;
this.data.save();
const data = await this.container.getService("Data");
data.lastMotd = msg;
data.save();

await axios.post(
config.webhook + "?wait=true",
Expand All @@ -179,85 +179,84 @@ export class Motd extends Service {
await this.setNicknameFromSentence(msg);
}

private async executeImageJob(patch?: boolean, msgId?: string): Promise<void> {
const res = await axios.get(`https://api.imgur.com/3/album/${config.imgurAlbumId}/images`, {
headers: {
Authorization: `Client-ID ${config.imgurClientId}`,
},
});
// private async executeImageJob(patch?: boolean, msgId?: string): Promise<void> {
// const res = await axios.get(`https://api.imgur.com/3/album/${config.imgurAlbumId}/images`, {
// headers: {
// Authorization: `Client-ID ${config.imgurClientId}`,
// },
// });

if (res.status === 200) {
const yesterday = dayjs().subtract(1, "d").unix();
this.images = res.data.data;
const urls: ImgurImage[] = res.data.data.filter(
(img: ImgurImage) =>
img.datetime >= yesterday &&
!this.lastimages.includes(img) &&
!this.ignorelist.some(id => img.title.includes(id))
); // keep only recent images
const authors = [...new Set(urls.map(image => image.title))];
const index = (Math.random() * urls.length) | 0;
const image = urls[index];
const url: string = image.link;
if (!url) return;
// if (res.status === 200) {
// const yesterday = dayjs().subtract(1, "d").unix();
// this.images = res.data.data;
// const urls: ImgurImage[] = res.data.data.filter(
// (img: ImgurImage) =>
// img.datetime >= yesterday &&
// !this.lastimages.includes(img) &&
// !this.ignorelist.some(id => img.title.includes(id))
// ); // keep only recent images
// const authors = [...new Set(urls.map(image => image.title))];
// const index = (Math.random() * urls.length) | 0;
// const image = urls[index];
// const url: string = image.link;
// if (!url) return;

let msg = `Image of the day\n(No. ${index + 1} out of ${urls.length} total from ${
authors.length
} user${authors.length > 1 ? "s" : ""})`;
// let msg = `Image of the day\n(No. ${index + 1} out of ${urls.length} total from ${
// authors.length
// } user${authors.length > 1 ? "s" : ""})`;

if (patch !== undefined && msgId) {
this.rerolls++;
msg = `Image of the day\n(No. ${index + 1} out of ${urls.length} total from ${
authors.length
} user${authors.length > 1 ? "s" : ""})\n(♻ rerolled ${this.rerolls}x)`;
await axios.patch(
`${config.webhook}/messages/${msgId}`,
JSON.stringify({
content: msg + `\n${url}`,
username: "Meta Construct",
avatar_url:
"https://pbs.twimg.com/profile_images/1503242277/meta4_crop.png",
}),
{
headers: {
"Content-Type": "application/json",
},
}
);
} else {
this.rerolls = 0;
await axios.post(
config.webhook,
JSON.stringify({
content: msg + `\n${url}`,
username: "Meta Construct",
avatar_url:
"https://pbs.twimg.com/profile_images/1503242277/meta4_crop.png",
}),
{
headers: {
"Content-Type": "application/json",
},
}
);
}
this.bot.setServerBanner(url);
this.lastimages.push(image);
await this.data.save();
setTimeout(async () => {
const last = await this.bot.getLastMotdMsg();
await last?.react("♻️");
}, 1000 * 10);
}
}
async rerollImageJob(): Promise<void> {
if (!(await this.bot.overLvl2())) return;
const lastmsg = await this.bot.getLastMotdMsg();
if (!lastmsg) return;
// if (patch !== undefined && msgId) {
// this.rerolls++;
// msg = `Image of the day\n(No. ${index + 1} out of ${urls.length} total from ${
// authors.length
// } user${authors.length > 1 ? "s" : ""})\n(♻ rerolled ${this.rerolls}x)`;
// await axios.patch(
// `${config.webhook}/messages/${msgId}`,
// JSON.stringify({
// content: msg + `\n${url}`,
// username: "Meta Construct",
// avatar_url:
// "https://pbs.twimg.com/profile_images/1503242277/meta4_crop.png",
// }),
// {
// headers: {
// "Content-Type": "application/json",
// },
// }
// );
// } else {
// this.rerolls = 0;
// await axios.post(
// config.webhook,
// JSON.stringify({
// content: msg + `\n${url}`,
// username: "Meta Construct",
// avatar_url:
// "https://pbs.twimg.com/profile_images/1503242277/meta4_crop.png",
// }),
// {
// headers: {
// "Content-Type": "application/json",
// },
// }
// );
// }
// this.bot.setServerBanner(url);
// this.lastimages.push(image);
// setTimeout(async () => {
// const last = await this.bot.getLastMotdMsg();
// await last?.react("♻️");
// }, 1000 * 10);
// }
// }
// async rerollImageJob(): Promise<void> {
// if (!(await this.bot.overLvl2())) return;
// const lastmsg = await this.bot.getLastMotdMsg();
// if (!lastmsg) return;

await this.executeImageJob(true, lastmsg.id);
await this.bot.removeMotdReactions();
}
// await this.executeImageJob(true, lastmsg.id);
// await this.bot.removeMotdReactions();
// }

async getImageInfo(id: string): Promise<ImgurImage | undefined> {
try {
Expand Down
3 changes: 1 addition & 2 deletions app/services/Resonite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ export class Resonite extends Service {
}

async GetOrFetchToken(): Promise<void> {
const data = this.container.getService("Data");
if (!data) return;
const data = await this.container.getService("Data");
let lastToken = data.lastResoniteToken;
let lastTokenTime = data.lastResoniteTokenTime ?? Date.now();

Expand Down
19 changes: 10 additions & 9 deletions app/services/Starboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Container } from "../Container";
import { SQL } from "./SQL";
import { Service } from ".";
import { DiscordBot, SQL, Service } from ".";
import Discord from "discord.js";
import config from "@/config/starboard.json";
import discordConfig from "@/config/discord.json";
Expand All @@ -14,18 +13,22 @@ const STARBOARD_CONFIG = {
export class Starboard extends Service {
name = "Starboard";
private isBusy = false;
private sql: SQL | undefined;
private sql: SQL;
private bot: DiscordBot;

constructor(container: Container) {
super(container);
this.sql = this.container.getService("SQL");
this.initServices();
}

private async initServices(): Promise<void> {
this.sql = await this.container.getService("SQL");
this.bot = await this.container.getService("DiscordBot");

const bot = this.container.getService("DiscordBot");
if (bot) {
const filter = (btn: Discord.MessageComponentInteraction) =>
btn.customId.startsWith("starboard");

bot.discord.on("interactionCreate", async interaction => {
this.bot.discord.on("interactionCreate", async interaction => {
if (!interaction.isButton()) return;
if (!filter(interaction)) return;
if (interaction.message.author.username !== interaction.user.username) return;
Expand All @@ -43,7 +46,6 @@ export class Starboard extends Service {
}

async isMsgStarred(msgId: string): Promise<boolean> {
if (!this.sql) return true;
const db = await this.sql.getLocalDatabase();
if (!(await this.sql.tableExists("starboard"))) {
await db.exec(`CREATE TABLE starboard (MessageId VARCHAR(1000));`);
Expand All @@ -54,7 +56,6 @@ export class Starboard extends Service {
}

private async starMsg(msgId: string): Promise<void> {
if (!this.sql) return;
const db = await this.sql.getLocalDatabase();
await db.run("INSERT INTO starboard(MessageId) VALUES(?)", msgId);
}
Expand Down
Loading

0 comments on commit f4a2374

Please sign in to comment.