/[sudobot]/branches/5.x/src/core/Client.ts
ViewVC logotype

Annotation of /branches/5.x/src/core/Client.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months, 1 week ago) by rakinar2
File MIME type: application/typescript
File size: 10568 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 /**
2     * This file is part of SudoBot.
3     *
4     * Copyright (C) 2021-2023 OSN Developers.
5     *
6     * SudoBot is free software; you can redistribute it and/or modify it
7     * under the terms of the GNU Affero General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * SudoBot is distributed in the hope that it will be useful, but
12     * WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU Affero General Public License for more details.
15     *
16     * You should have received a copy of the GNU Affero General Public License
17     * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18     */
19    
20     import { PrismaClient } from "@prisma/client";
21     import { ClientOptions, Collection, Client as DiscordClient, GuildEmoji, UserResolvable } from "discord.js";
22     import fs from "fs/promises";
23     import path, { basename, dirname } from "path";
24     import Server from "../api/Server";
25     import type Antijoin from "../automod/Antijoin";
26     import type Antiraid from "../automod/Antiraid";
27     import type Antispam from "../automod/Antispam";
28     import type FileFilterService from "../automod/FileFilterService";
29     import type MessageFilter from "../automod/MessageFilter";
30     import type MessageRuleService from "../automod/MessageRuleService";
31     import type ProfileFilter from "../automod/ProfileFilter";
32     import { SuppressErrorsMetadata } from "../decorators/SuppressErrors";
33     import type AFKService from "../services/AFKService";
34     import type AutoRoleService from "../services/AutoRoleService";
35     import type BallotManager from "../services/BallotManager";
36     import type ChannelLockManager from "../services/ChannelLockManager";
37     import type CommandManager from "../services/CommandManager";
38     import type ConfigManager from "../services/ConfigManager";
39     import type InfractionManager from "../services/InfractionManager";
40     import type InviteTrackerService from "../services/InviteTrackerService";
41     import type LoggerService from "../services/LoggerService";
42     import type MetadataService from "../services/MetadataService";
43     import type PermissionManager from "../services/PermissionManager";
44     import type QueueManager from "../services/QueueManager";
45     import type QuickMuteService from "../services/QuickMuteService";
46     import type ReactionRoleService from "../services/ReactionRoleService";
47     import type SnippetManager from "../services/SnippetManager";
48     import type StartupManager from "../services/StartupManager";
49     import type TranslationService from "../services/TranslationService";
50     import type WelcomerService from "../services/WelcomerService";
51     import { log, logError, logInfo } from "../utils/logger";
52     import type Command from "./Command";
53     import ServiceManager from "./ServiceManager";
54    
55     export default class Client<Ready extends boolean = boolean> extends DiscordClient<Ready> {
56     aliases = {
57     "@services": path.resolve(__dirname, "../services"),
58     "@automod": path.resolve(__dirname, "../automod")
59     };
60    
61     services = [
62     "@services/StartupManager",
63     "@services/ConfigManager",
64     "@services/CommandManager",
65     "@services/InfractionManager",
66     "@services/LoggerService",
67     "@services/QueueManager",
68     "@services/WelcomerService",
69     "@services/SnippetManager",
70     "@services/ChannelLockManager",
71     "@services/PermissionManager",
72     "@services/MetadataService",
73     "@services/QuickMuteService",
74     "@services/TranslationService",
75     "@services/AutoRoleService",
76     "@services/ReactionRoleService",
77     "@services/AFKService",
78     "@services/InviteTrackerService",
79     "@services/BallotManager",
80    
81     "@automod/MessageFilter",
82     "@automod/Antispam",
83     "@automod/Antiraid",
84     "@automod/Antijoin",
85     "@automod/ProfileFilter",
86     "@automod/FileFilterService",
87     "@automod/MessageRuleService"
88     ];
89    
90     commandsDirectory = path.resolve(__dirname, "../commands");
91     eventsDirectory = path.resolve(__dirname, "../events");
92    
93     serviceManager = new ServiceManager(this);
94    
95     startupManager: StartupManager = {} as StartupManager;
96     configManager: ConfigManager = {} as ConfigManager;
97     commandManager: CommandManager = {} as CommandManager;
98     infractionManager: InfractionManager = {} as InfractionManager;
99     logger: LoggerService = {} as LoggerService;
100     messageFilter: MessageFilter = {} as MessageFilter;
101     antispam: Antispam = {} as Antispam;
102     queueManager: QueueManager = {} as QueueManager;
103     snippetManager: SnippetManager = {} as SnippetManager;
104     welcomerService: WelcomerService = {} as WelcomerService;
105     antiraid: Antiraid = {} as Antiraid;
106     channelLockManager: ChannelLockManager = {} as ChannelLockManager;
107     antijoin: Antijoin = {} as Antijoin;
108     profileFilter: ProfileFilter = {} as ProfileFilter;
109     permissionManager: PermissionManager = {} as PermissionManager;
110     metadata: MetadataService = {} as MetadataService;
111     quickMute: QuickMuteService = {} as QuickMuteService;
112     translator: TranslationService = {} as TranslationService;
113     autoRoleService: AutoRoleService = {} as AutoRoleService;
114     reactionRoleService: ReactionRoleService = {} as ReactionRoleService;
115     afkService: AFKService = {} as AFKService;
116     inviteTracker: InviteTrackerService = {} as InviteTrackerService;
117     ballotManager: BallotManager = {} as BallotManager;
118     fileFilter: FileFilterService = {} as FileFilterService;
119     messageRuleService: MessageRuleService = {} as MessageRuleService;
120    
121     prisma = new PrismaClient({
122     errorFormat: "pretty",
123     log: ["query", "error", "info", "warn"]
124     });
125    
126     server = new Server(this);
127    
128     commands = new Collection<string, Command>();
129     emojiMap = new Collection<string, GuildEmoji>();
130    
131     constructor(options: ClientOptions, { services }: { services?: string[] } = {}) {
132     super(options);
133    
134     if (services) {
135     this.services = services;
136     }
137     }
138    
139     async boot() {
140     await this.serviceManager.loadServices();
141     }
142    
143     async fetchUserSafe(user: UserResolvable) {
144     try {
145     return await this.users.fetch(user);
146     } catch (e) {
147     logError(e);
148     return null;
149     }
150     }
151    
152     async loadCommands(directory = this.commandsDirectory) {
153     const files = await fs.readdir(directory);
154     const includeOnly = process.env.COMMANDS?.split(",");
155    
156     for (const file of files) {
157     const filePath = path.join(directory, file);
158     const isDirectory = (await fs.lstat(filePath)).isDirectory();
159    
160     if (isDirectory) {
161     await this.loadCommands(filePath);
162     continue;
163     }
164    
165     if (!file.endsWith(".ts") && !file.endsWith(".js")) {
166     continue;
167     }
168    
169     if (includeOnly && !includeOnly.includes(file.replace(/\.(ts|js)/gi, ""))) {
170     continue;
171     }
172    
173     const { default: CommandClass }: { default: new (client: Client) => Command } = await import(filePath);
174     const command = new CommandClass(this);
175     command.group = basename(dirname(filePath));
176     this.commands.set(command.name, command);
177    
178     for (const alias of command.aliases) {
179     this.commands.set(alias, command);
180     }
181    
182     this.loadEventListenersFromMetadata(CommandClass, command);
183     logInfo("Loaded command: ", command.name);
184     }
185     }
186    
187     async loadEvents(directory = this.eventsDirectory) {
188     const files = await fs.readdir(directory);
189    
190     for (const file of files) {
191     const filePath = path.join(directory, file);
192     const isDirectory = (await fs.lstat(filePath)).isDirectory();
193    
194     if (isDirectory) {
195     await this.loadEvents(filePath);
196     continue;
197     }
198    
199     if (!file.endsWith(".ts") && !file.endsWith(".js")) {
200     continue;
201     }
202    
203     const { default: Event } = await import(filePath);
204     const event = new Event(this);
205     this.on(event.name, event.execute.bind(event));
206     }
207     }
208    
209     private supressErrorMessagesHandler(suppressErrors: SuppressErrorsMetadata, e: unknown) {
210     if (suppressErrors.mode === "log") {
211     logError(e);
212     } else if (suppressErrors.mode === "disabled") {
213     throw e;
214     }
215     }
216    
217     loadEventListenersFromMetadata<I extends Object = Object>(Class: I["constructor"], instance?: I) {
218     const metadata: { event: string; handler: Function; methodName: string }[] | undefined = Reflect.getMetadata(
219     "event_listeners",
220     Class.prototype
221     );
222    
223     if (metadata) {
224     for (const data of metadata) {
225     const callback = instance ? data.handler.bind(instance) : data.handler;
226     const suppressErrors: SuppressErrorsMetadata | undefined = Reflect.getMetadata(
227     "supress_errors",
228     Class.prototype,
229     data.methodName
230     );
231    
232     this.on(
233     data.event,
234     suppressErrors
235     ? (...args: any[]) => {
236     try {
237     const ret = callback(...args);
238    
239     if (ret instanceof Promise) ret.catch(e => this.supressErrorMessagesHandler(suppressErrors, e));
240    
241     return ret;
242     } catch (e) {
243     this.supressErrorMessagesHandler(suppressErrors, e);
244     }
245     }
246     : callback
247     );
248    
249     log("Added event listener for event: ", data.event);
250     }
251     }
252     }
253    
254     async getHomeGuild() {
255     const id = process.env.HOME_GUILD_ID;
256    
257     if (!id) {
258     logError(
259     "Environment variable `HOME_GUILD_ID` is not set. The bot can't work without it. Please follow the setup guide in the bot documentation."
260     );
261     process.exit(-1);
262     }
263    
264     try {
265     return this.guilds.cache.get(id) || (await this.guilds.fetch(id));
266     } catch (e) {
267     logError(e);
268     logError("Error fetching home guild: make sure the ID inside `HOME_GUILD_ID` environment variable is correct.");
269     process.exit(-1);
270     }
271     }
272     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26