import {
Arg,
BushConstants,
Global,
GlobalCache,
type BushClient,
type BushInspectOptions,
type BushMessage,
type BushSlashEditMessageType,
type BushSlashMessage,
type BushSlashSendMessageType,
type BushUser,
type CodeBlockLang,
type Pronoun,
type PronounCode
} from '#lib';
import { humanizeDuration } from '@notenoughupdates/humanize-duration';
import { exec } from 'child_process';
import deepLock from 'deep-lock';
import { ClientUtil, Util as AkairoUtil } from 'discord-akairo';
import { APIMessage } from 'discord-api-types';
import {
Constants as DiscordConstants,
GuildMember,
Message,
MessageEmbed,
ThreadMember,
User,
Util as DiscordUtil,
type ColorResolvable,
type CommandInteraction,
type InteractionReplyOptions,
type PermissionResolvable,
type Snowflake,
type TextChannel,
type UserResolvable
} from 'discord.js';
import got from 'got';
import _ from 'lodash';
import { inspect, promisify } from 'util';
import CommandErrorListener from '../../../listeners/commands/commandError.js';
import { Format } from '../../common/util/Format.js';
export class BushClientUtil extends ClientUtil {
/**
* The client.
*/
public declare readonly client: BushClient;
/**
* The hastebin urls used to post to hastebin, attempts to post in order
*/
#hasteURLs: string[] = [
'https://hst.sh',
// 'https://hasteb.in',
'https://hastebin.com',
'https://mystb.in',
'https://haste.clicksminuteper.net',
'https://paste.pythondiscord.com',
'https://haste.unbelievaboat.com'
// 'https://haste.tyman.tech'
];
/**
* Creates this client util
* @param client The client to initialize with
*/
public constructor(client: BushClient) {
super(client);
}
/**
* Maps an array of user ids to user objects.
* @param ids The list of IDs to map
* @returns The list of users mapped
*/
public async mapIDs(ids: Snowflake[]): Promise<User[]> {
return await Promise.all(ids.map((id) => client.users.fetch(id)));
}
/**
* Capitalizes the first letter of the given text
* @param text The text to capitalize
* @returns The capitalized text
*/
public capitalize(text: string): string {
return text.charAt(0).toUpperCase() + text.slice(1);
}
/**
* Runs a shell command and gives the output
* @param command The shell command to run
* @returns The stdout and stderr of the shell command
*/
public async shell(command: string): Promise<{
stdout: string;
stderr: string;
}> {
return await promisify(exec)(command);
}
/**
* Posts text to hastebin
* @param content The text to post
* @returns The url of the posted text
*/
public async haste(content: string, substr = false): Promise<HasteResults> {
let isSubstr = false;
if (content.length > 400_000 && !substr) {
void this.handleError('haste', new Error(`content over 400,000 characters (${content.length.toLocaleString()})`));
return { error: 'content too long' };
} else if (content.length > 400_000) {
content = content.substring(0, 400_000);
isSubstr = true;
}
for (const url of this.#hasteURLs) {
try {
const res: HastebinRes = await got.post(`${url}/documents`, { body: content }).json();
return { url: `${url}/${res.key}`, error: isSubstr ? 'substr' : undefined };
} catch {
void client.console.error('haste', `Unable to upload haste to ${url}`);
}
}
return { error: 'unable to post' };
}
/**
* Resolves a user-provided string into a user object, if possible
* @param text The text to try and resolve
* @returns The user resolved or null
*/
public async resolveUserAsync(text: string): Promise<User | null> {
const idReg = /\d{17,19}/;
const idMatch = text.match(idReg);
if (idMatch) {
try {
return await client.users.fetch(text as Snowflake);
} catch {
// pass
}
}
const mentionReg = /<@!?(?<id>\d{17,19})>/;
const mentionMatch = text.match(mentionReg);
if (mentionMatch) {
try {
return await client.users.fetch(mentionMatch.groups!.id as