th3w1zard1's picture
Deploy GPTR trask-http from community-bots local CI
3a25f97 verified
import {
Client,
GatewayIntentBits,
Partials,
REST,
Routes,
type RESTPostAPIApplicationCommandsJSONBody,
} from "discord.js";
type LogDetails = Error | object | string | number | boolean | null;
export interface Logger {
info<T extends LogDetails = LogDetails>(message: string, details?: T): void;
warn<T extends LogDetails = LogDetails>(message: string, details?: T): void;
error<T extends LogDetails = LogDetails>(message: string, details?: T): void;
debug<T extends LogDetails = LogDetails>(message: string, details?: T): void;
}
export interface DiscordRuntimeConfigLike {
appId: string;
botToken: string;
guildId: string | undefined;
}
export interface ClientIntentOptions {
guildMembers?: boolean;
/** Implied when `guildMessageReactions` is true (required for reaction events on guild messages). */
guildMessages?: boolean;
messageContent?: boolean;
/** Enables Guild Message Reactions intent and reaction/message/channel/user partials for uncached messages. */
guildMessageReactions?: boolean;
}
const writeLog = (level: string, scope: string, message: string, details?: LogDetails): void => {
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] [${scope}] [${level}] ${message}`;
if (details === undefined) {
console.log(prefix);
return;
}
console.log(prefix, details);
};
export const createLogger = (scope: string): Logger => {
return {
info: (message, details) => writeLog("INFO", scope, message, details),
warn: (message, details) => writeLog("WARN", scope, message, details),
error: (message, details) => writeLog("ERROR", scope, message, details),
debug: (message, details) => writeLog("DEBUG", scope, message, details),
};
};
export const createBotClient = (options: ClientIntentOptions = {}): Client => {
const intents = [GatewayIntentBits.Guilds];
if (options.guildMembers) {
intents.push(GatewayIntentBits.GuildMembers);
}
if (options.guildMessages || options.guildMessageReactions) {
intents.push(GatewayIntentBits.GuildMessages);
}
if (options.guildMessageReactions) {
intents.push(GatewayIntentBits.GuildMessageReactions);
}
if (options.messageContent) {
intents.push(GatewayIntentBits.MessageContent);
}
const partials = options.guildMessageReactions
? [Partials.Message, Partials.Channel, Partials.Reaction, Partials.User]
: undefined;
return new Client(partials ? { intents, partials } : { intents });
};
export const deployGuildCommands = async (
config: DiscordRuntimeConfigLike,
commands: readonly RESTPostAPIApplicationCommandsJSONBody[],
logger: Logger,
): Promise<void> => {
if (!config.guildId) {
logger.warn("Skipping guild command deployment because no guild id is configured.");
return;
}
const rest = new REST({ version: "10" }).setToken(config.botToken);
await rest.put(Routes.applicationGuildCommands(config.appId, config.guildId), {
body: commands,
});
logger.info("Guild-scoped commands deployed.", {
guildId: config.guildId,
commandCount: commands.length,
});
};
/**
* Deploy commands globally (available in all guilds and DMs).
* Global commands can take up to one hour to propagate.
* If `guildId` is also configured on the same app, guild commands take precedence
* in that guild, so you can still override per-guild for faster testing.
*/
export const deployGlobalCommands = async (
config: DiscordRuntimeConfigLike,
commands: readonly RESTPostAPIApplicationCommandsJSONBody[],
logger: Logger,
): Promise<void> => {
const rest = new REST({ version: "10" }).setToken(config.botToken);
await rest.put(Routes.applicationCommands(config.appId), {
body: commands,
});
logger.info("Global commands deployed.", {
commandCount: commands.length,
});
};
export const toErrorMessage = <T>(error: T): string => {
if (error instanceof Error) {
return error.message;
}
return String(error);
};
export const formatDuration = (milliseconds: number): string => {
const seconds = Math.floor(milliseconds / 1000);
if (seconds < 60) {
return `${seconds}s`;
}
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}m ${remainingSeconds}s`;
};
export const mentionUser = (userId: string): string => `<@${userId}>`;