/[sudobot]/branches/8.x/src/utils/utils.ts
ViewVC logotype

Contents of /branches/8.x/src/utils/utils.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 9751 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
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 axios, { AxiosRequestConfig } from "axios";
21 import {
22 APIEmbedField,
23 Channel,
24 ChannelType,
25 ColorResolvable,
26 ComponentEmojiResolvable,
27 EmbedBuilder,
28 GuildEmoji,
29 GuildMember,
30 NewsChannel,
31 PermissionOverwrites,
32 PermissionResolvable,
33 PermissionsBitField,
34 TextBasedChannel,
35 TextChannel,
36 ThreadChannel,
37 User,
38 escapeMarkdown
39 } from "discord.js";
40 import { mkdirSync } from "fs";
41 import path from "path";
42 import Client from "../core/Client";
43 import { ActionDoneName } from "../services/InfractionManager";
44 import { userInfo } from "./embed";
45
46 export function isSnowflake(input: string) {
47 return /^\d{16,22}$/.test(input);
48 }
49
50 export function pick<T, K extends Array<keyof T>>(object: T, keys: K): Pick<T, K extends Array<infer E> ? E : never> {
51 if (typeof object === "object" && object !== null) {
52 const picked: Partial<T> = {};
53
54 for (const key of keys) {
55 picked[key] = object[key];
56 }
57
58 return picked as Pick<T, K extends Array<infer E> ? E : never>;
59 }
60
61 return {} as Pick<T, K extends Array<infer E> ? E : never>;
62 }
63
64 export interface CreateModerationEmbedOptions {
65 user: User;
66 moderator: User;
67 actionDoneName?: ActionDoneName;
68 reason?: string | null;
69 description?: string;
70 fields?:
71 | APIEmbedField[]
72 | ((fields: APIEmbedField[], id: string, reason?: string) => Promise<APIEmbedField[]> | APIEmbedField[]);
73 id: string | number;
74 color?: ColorResolvable;
75 }
76
77 export async function createModerationEmbed({
78 user,
79 actionDoneName,
80 reason,
81 description,
82 fields,
83 id,
84 color = 0xf14a60,
85 moderator
86 }: CreateModerationEmbedOptions) {
87 return new EmbedBuilder({
88 author: {
89 name: user.tag,
90 icon_url: user.displayAvatarURL()
91 },
92 description: description ?? `**${escapeMarkdown(user.tag)}** has been ${actionDoneName}.`,
93 fields:
94 typeof fields === "function"
95 ? await fields(
96 [
97 {
98 name: "Reason",
99 value: reason ?? "*No reason provided*"
100 },
101 {
102 name: "User",
103 value: userInfo(user)
104 },
105 {
106 name: "Moderator",
107 value: userInfo(moderator)
108 },
109 {
110 name: "Infraction ID",
111 value: `${id}`
112 }
113 ],
114 `${id}`,
115 reason ?? undefined
116 )
117 : [
118 {
119 name: "Reason",
120 value: reason ?? "*No reason provided*"
121 },
122 {
123 name: "User",
124 value: userInfo(user)
125 },
126 {
127 name: "Moderator",
128 value: userInfo(moderator)
129 },
130 ...(fields ?? []),
131 {
132 name: "Infraction ID",
133 value: `${id}`
134 }
135 ],
136 footer: actionDoneName
137 ? {
138 text: actionDoneName[0].toUpperCase() + actionDoneName.substring(1)
139 }
140 : undefined
141 })
142 .setTimestamp()
143 .setColor(color);
144 }
145
146 export function getEmoji<B extends boolean = false>(
147 client: Client,
148 name: string,
149 returnNull: B = false as B
150 ): B extends false ? string : string | null {
151 return (
152 client.guilds.cache
153 .get(process.env.HOME_GUILD_ID!)
154 ?.emojis.cache.find(e => e.name === name)
155 ?.toString() ??
156 client.configManager.systemConfig.emojis?.[name] ??
157 client.emojiMap.get(name)?.toString() ??
158 (returnNull ? null : "")
159 );
160 }
161
162 export function getEmojiObject(client: Client, name: string): GuildEmoji | null {
163 return (
164 client.guilds.cache.get(process.env.HOME_GUILD_ID!)?.emojis.cache.find(e => e.name === name) ??
165 client.emojiMap.get(name) ??
166 null
167 );
168 }
169
170 export function getComponentEmojiResolvable(client: Client, name: string): ComponentEmojiResolvable | null {
171 const emoji = getEmojiObject(client, name);
172
173 return emoji
174 ? {
175 id: emoji?.id,
176 name: emoji?.name ?? emoji?.identifier,
177 animated: emoji?.animated ?? false
178 }
179 : null;
180 }
181
182 export function isTextableChannel(
183 channel: Channel | ThreadChannel,
184 DMs = false
185 ): channel is TextChannel | NewsChannel | ThreadChannel {
186 return [
187 ...(DMs ? [ChannelType.DM, ChannelType.GroupDM] : []),
188 ChannelType.GuildAnnouncement,
189 ChannelType.GuildText,
190 ChannelType.PrivateThread,
191 ChannelType.PublicThread,
192 ChannelType.GuildVoice
193 ].includes(channel.type);
194 }
195
196 export function developmentMode() {
197 return (
198 ["dev", "development"].includes(process.env.NODE_ENV?.toLowerCase() ?? "production") ||
199 ["dev", "development"].includes(process.env.SUDO_ENV?.toLowerCase() ?? "production")
200 );
201 }
202
203 export function isImmuneToAutoMod(
204 client: Client,
205 member: GuildMember,
206 permission?: PermissionResolvable[] | PermissionResolvable
207 ) {
208 return client.permissionManager.isImmuneToAutoMod(member, permission);
209 }
210
211 export function wait(time: number) {
212 return new Promise(resolve => setTimeout(resolve, time));
213 }
214
215 export function sudoPrefix(pathLike: string, createDirIfNotExists = false) {
216 const directoryOrFile = path.resolve(process.env.SUDO_PREFIX ?? __dirname, process.env.SUDO_PREFIX ? "" : "../..", pathLike);
217
218 if (createDirIfNotExists) mkdirSync(directoryOrFile, { recursive: true });
219
220 return directoryOrFile;
221 }
222
223 export function getPermissionNames(permissionsBit: bigint) {
224 const result = [];
225 const permissions = new PermissionsBitField(permissionsBit);
226
227 for (const permission of Object.keys(PermissionsBitField.Flags) as (keyof typeof PermissionsBitField.Flags)[]) {
228 if (permissions.has(PermissionsBitField.Flags[permission])) {
229 result.push(permission);
230 }
231 }
232
233 return result;
234 }
235
236 export function forceGetPermissionNames(permissions: PermissionResolvable[]) {
237 const strings: string[] = [];
238
239 for (const permission of permissions) {
240 if (typeof permission === "bigint") {
241 strings.push(...getPermissionNames(permission));
242 } else if (typeof permission === "string") {
243 strings.push(permission);
244 } else throw new Error("Unknown permission type");
245 }
246
247 return strings;
248 }
249
250 export function getChannelPermissionOverride(permission: PermissionResolvable, permissionOverwrites: PermissionOverwrites) {
251 return permissionOverwrites.allow.has(permission, true)
252 ? true
253 : permissionOverwrites.deny.has(permission, true)
254 ? true
255 : null;
256 }
257
258 export function chunkedString(str: string, chunkSize = 4000) {
259 const output = [];
260 let chunk = "";
261
262 for (let i = 0; i < str.length; i++) {
263 if (i !== 0 && i % chunkSize === 0) {
264 output.push(chunk);
265 chunk = str[i];
266 continue;
267 }
268
269 chunk += str[i];
270 }
271
272 if (chunk.length !== 0) {
273 output.push(chunk);
274 }
275
276 return output;
277 }
278
279 export function escapeRegex(string: string) {
280 return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
281 }
282
283 export function safeMessageContent(content: string, member: GuildMember, channel?: TextBasedChannel) {
284 return member.permissions.has("MentionEveryone") &&
285 (!channel || member.permissionsIn(channel as TextChannel).has("MentionEveryone"))
286 ? content
287 : content
288 .replaceAll(/@everyone/gi, "`@everyone`")
289 .replaceAll(/@here/gi, "`@here`")
290 .replaceAll(`<@&${member.guild.id}>`, "`@everyone`")
291 .replace(/<@&(\d+)>/gim, (_, id) => `@${member.guild.roles.cache.get(id)?.name ?? id}`);
292 }
293
294 export function isSystemAdmin(client: Client, userId: string) {
295 return client.configManager.systemConfig.system_admins.includes(userId);
296 }
297
298 // eslint-disable-next-line @typescript-eslint/no-unused-vars
299 export function assertUnreachable(_value: never): never {
300 throw new Error("This statement should be unreachable");
301 }
302
303 export function TODO(message?: string): never {
304 throw new Error(message ?? "Not implemented");
305 }
306
307 export async function request<D = unknown>(options: AxiosRequestConfig<D>) {
308 try {
309 return [await axios(options), null] as const;
310 } catch (error) {
311 return [null, error] as const;
312 }
313 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26