some utils converted

This commit is contained in:
Skillz4Killz
2021-10-12 16:13:59 +00:00
committed by GitHub
parent 6b0d8d4921
commit 2f971f511f
5 changed files with 109 additions and 96 deletions

View File

@@ -12,7 +12,7 @@ import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts";
import { GetGatewayBot } from "./types/gateway/get_gateway_bot.ts";
import { dispatchRequirements } from "./util/dispatch_requirements.ts";
import { processQueue } from "./rest/process_queue.ts";
import { snowflakeToBigint } from "./util/bigint.ts";
import { bigintToSnowflake, snowflakeToBigint } from "./util/bigint.ts";
import { Collection } from "./util/collection.ts";
import { DiscordenoUser, transformMember, transformUser } from "./transformers/member.ts";
import { SnakeCasedPropertiesDeep } from "./types/util.ts";
@@ -36,31 +36,50 @@ import { handleOnMessage } from "./ws/handle_on_message.ts";
import { closeWS } from "./ws/close_ws.ts";
import { sendShardMessage } from "./ws/send_shard_message.ts";
import { resume } from "./ws/resume.ts";
import { calculateShardId } from "./util/calculate_shard_id.ts";
import {
baseEndpoints,
CHANNEL_MENTION_REGEX,
CONTEXT_MENU_COMMANDS_NAME_REGEX,
DISCORDENO_VERSION,
DISCORD_SNOWFLAKE_REGEX,
endpoints,
SLASH_COMMANDS_NAME_REGEX,
USER_AGENT,
} from "./util/constants.ts";
import { GatewayPayload } from "./types/gateway/gateway_payload.ts";
import { delay } from "./util/utils.ts";
import { iconBigintToHash, iconHashToBigInt } from "./util/hash.ts";
import { loopObject } from "./util/loop_object.ts";
export async function createBot(options: CreateBotOptions) {
return {
id: options.botId,
applicationId: options.applicationId || options.botId,
token: `Bot ${options.token}`,
events: { dispatchRequirements: dispatchRequirements, ...options.events },
events: options.events,
intents: options.intents.reduce((bits, next) => (bits |= DiscordGatewayIntents[next]), 0),
botGatewayData: options.botGatewayData || (await getGatewayBot()),
isReady: false,
activeGuildIds: new Set<bigint>(),
constants: createBotConstants(),
};
}
const bot = await createBot({
token: "",
botId: 0n,
events: createEventHandlers(),
events: createEventHandlers({}),
intents: [],
});
export function createEventHandlers(options?: Partial<EventHandlers>) {
export function createEventHandlers(events: Partial<EventHandlers>): EventHandlers {
function ignore() {}
return {
debug: () => undefined,
// PROVIDED OPTIONS OVERRIDE EVERYTHING ABOVE
...options,
channelCreate: events.channelCreate ?? ignore,
debug: events.debug ?? ignore,
dispatchRequirements: events.dispatchRequirements ?? ignore,
};
}
@@ -114,6 +133,9 @@ export function createRestManager(options: CreateRestManagerOptions) {
export async function startBot(bot: Bot) {
const transformers = createTransformers(bot.transformers);
// SETUP UTILS
bot.utils = createUtils({});
// SETUP CACHE
bot.users = new Collection();
@@ -124,6 +146,28 @@ export async function startBot(bot: Bot) {
bot.gateway = createGatewayManager({});
}
export function createUtils(options: Partial<HelperUtils>) {
return {
snowflakeToBigint,
bigintToSnowflake,
calculateShardId,
delay,
iconHashToBigInt,
iconBigintToHash,
loopObject,
};
}
export interface HelperUtils {
snowflakeToBigint: typeof snowflakeToBigint;
bigintToSnowflake: typeof bigintToSnowflake;
calculateShardId: typeof calculateShardId;
delay: typeof delay;
iconHashToBigInt: typeof iconHashToBigInt;
iconBigintToHash: typeof iconBigintToHash;
loopObject: typeof loopObject;
}
export function createGatewayManager(options: Partial<GatewayManager>): GatewayManager {
return {
secretKey: options.secretKey ?? "",
@@ -181,7 +225,7 @@ export interface CreateBotOptions {
token: string;
botId: bigint;
applicationId?: bigint;
events: Partial<EventHandlers>;
events: EventHandlers;
intents: (keyof typeof DiscordGatewayIntents)[];
botGatewayData?: GetGatewayBot;
rest?: Omit<CreateRestManagerOptions, "token">;
@@ -192,6 +236,7 @@ export type UnPromise<T extends Promise<unknown>> = T extends Promise<infer K> ?
export type CreatedBot = UnPromise<ReturnType<typeof createBot>>;
export type Bot = CreatedBot & {
utils: HelperUtils;
rest: RestManager;
gateway: GatewayManager;
transformers: Transformers;
@@ -326,4 +371,21 @@ export interface GatewayManager {
export interface EventHandlers {
debug: (text: string) => unknown;
channelCreate: (bot: Bot, channel: DiscordenoChannel) => unknown;
dispatchRequirements: (bot: Bot, data: GatewayPayload, shardId: number) => Promise<unknown> | unknown;
}
export function createBotConstants() {
return {
DISCORDENO_VERSION,
USER_AGENT,
BASE_URL: baseEndpoints.BASE_URL,
CDN_URL: baseEndpoints.CDN_URL,
endpoints,
regexes: {
SLASH_COMMANDS_NAME_REGEX,
CONTEXT_MENU_COMMANDS_NAME_REGEX,
CHANNEL_MENTION_REGEX,
DISCORD_SNOWFLAKE_REGEX,
},
};
}

View File

@@ -1,30 +1,25 @@
import { rest } from "../../rest/rest.ts";
import { Errors } from "../../types/discordeno/errors.ts";
import { Bot } from "../../bot.ts";
import type { CreateWebhook } from "../../types/webhooks/create_webhook.ts";
import type { Webhook } from "../../types/webhooks/webhook.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotChannelPermissions } from "../../util/permissions.ts";
import { urlToBase64 } from "../../util/utils.ts";
import { validateLength } from "../../util/validate_length.ts";
/**
* Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
*
* Webhook names cannot be: 'clyde'
*/
export async function createWebhook(channelId: bigint, options: CreateWebhook) {
await requireBotChannelPermissions(channelId, ["MANAGE_WEBHOOKS"]);
export async function createWebhook(bot: Bot, channelId: bigint, options: CreateWebhook) {
await bot.utils.requireBotChannelPermissions(channelId, ["MANAGE_WEBHOOKS"]);
if (
// Specific usernames that discord does not allow
options.name === "clyde" ||
!validateLength(options.name, { min: 2, max: 32 })
!bot.utils.validateLength(options.name, { min: 2, max: 32 })
) {
throw new Error(Errors.INVALID_WEBHOOK_NAME);
throw new Error(bot.constants.Errors.INVALID_WEBHOOK_NAME);
}
return await rest.runMethod<Webhook>("post", endpoints.CHANNEL_WEBHOOKS(channelId), {
return await bot.rest.runMethod<Webhook>(bot.rest, "post", bot.constants.endpoints.CHANNEL_WEBHOOKS(channelId), {
...options,
avatar: options.avatar ? await urlToBase64(options.avatar) : undefined,
avatar: options.avatar ? await bot.utils.urlToBase64(options.avatar) : undefined,
});
}

View File

@@ -1,44 +0,0 @@
import { eventHandlers } from "../bot.ts";
import { cacheHandlers } from "../cache.ts";
import { structures } from "../structures/mod.ts";
import { GuildMemberWithUser } from "../types/members/guild_member.ts";
const guildMemberQueue = new Map<bigint, { members: GuildMemberWithUser[]; resolve?: (value?: unknown) => void }>();
let processingQueue = false;
/** Cache all guild members without need to worry about overwriting something. */
// deno-lint-ignore require-await
export async function cacheMembers(guildId: bigint, members: GuildMemberWithUser[]) {
if (!members.length) return;
return new Promise((resolve) => {
guildMemberQueue.set(guildId, { members, resolve });
startQueue();
});
}
async function startQueue() {
if (processingQueue) return;
processingQueue = true;
while (guildMemberQueue.size) {
eventHandlers.debug?.("loop", "Running whille loop in cache_members file.");
const [guildId, queue]: [bigint, { members: GuildMemberWithUser[]; resolve: (value?: unknown) => void }] =
guildMemberQueue.entries().next().value;
await Promise.allSettled([
queue.members.map(async (member) => {
const discordenoMember = await structures.createDiscordenoMember(member, guildId);
await cacheHandlers.set("members", discordenoMember.id, discordenoMember);
}),
]);
queue.resolve?.();
guildMemberQueue.delete(guildId);
}
processingQueue = false;
}

View File

@@ -1,13 +1,7 @@
import { Bot } from "../bot.ts";
import { cache } from "../cache.ts";
import { getChannels } from "../helpers/channels/get_channels.ts";
import { getGuild } from "../helpers/guilds/get_guild.ts";
import { getMember } from "../helpers/members/get_member.ts";
import { structures } from "../structures/mod.ts";
import type { DiscordGatewayPayload } from "../types/gateway/gateway_payload.ts";
import type { Guild } from "../types/guilds/guild.ts";
import { snowflakeToBigint } from "./bigint.ts";
import { delay } from "./utils.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
const processing = new Set<bigint>();
@@ -17,7 +11,7 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
// DELETE MEANS WE DONT NEED TO FETCH. CREATE SHOULD HAVE DATA TO CACHE
if (data.t && ["GUILD_CREATE", "GUILD_DELETE"].includes(data.t)) return;
const id = snowflakeToBigint(
const id = bot.utils.snowflakeToBigint(
(data.t && ["GUILD_UPDATE"].includes(data.t)
? // deno-lint-ignore no-explicit-any
(data.d as any)?.id
@@ -25,11 +19,11 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
(data.d as any)?.guild_id) ?? ""
);
if (!id || cache.activeGuildIds.has(id)) return;
if (!id || bot.activeGuildIds.has(id)) return;
// If this guild is in cache, it has not been swept and we can cancel
if (cache.guilds.has(id)) {
cache.activeGuildIds.add(id);
if (bot.guilds.has(id)) {
bot.activeGuildIds.add(id);
return;
}
@@ -38,7 +32,7 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
let runs = 0;
do {
await delay(500);
await bot.utils.delay(500);
runs++;
} while (processing.has(id) && runs < 40);
@@ -54,10 +48,12 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
// New guild id has appeared, fetch all relevant data
bot.events.debug(`[DISPATCH] New Guild ID has appeared: ${id} in ${data.t} event`);
const rawGuild = (await getGuild(id, {
counts: true,
addToCache: false,
}).catch(console.log)) as Guild | undefined;
const rawGuild = (await bot.helpers
.getGuild(id, {
counts: true,
addToCache: false,
})
.catch(console.log)) as SnakeCasedPropertiesDeep<Guild> | undefined;
if (!rawGuild) {
processing.delete(id);
@@ -67,8 +63,8 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
bot.events.debug(`[DISPATCH] Guild ID ${id} has been found. ${rawGuild.name}`);
const [channels, botMember] = await Promise.all([
getChannels(id, false),
getMember(id, bot.id, { force: true }),
bot.helpers.getChannels(id, false),
bot.helpers.getMember(id, bot.id, { force: true }),
]).catch((error) => {
bot.events.debug(error);
return [];
@@ -81,17 +77,18 @@ export async function dispatchRequirements(bot: Bot, data: DiscordGatewayPayload
);
}
const guild = await structures.createDiscordenoGuild(
{ ...rawGuild, memberCount: rawGuild.approximateMemberCount },
shardId
);
const guild = await bot.transformers.guild(bot, {
...rawGuild,
member_count: rawGuild.approximateMemberCount,
shardId,
});
// Add to cache
cache.guilds.set(id, guild);
cache.dispatchedGuildIds.delete(id);
bot.guilds.set(id, guild);
bot.dispatchedGuildIds.delete(id);
channels.forEach((channel) => {
cache.dispatchedChannelIds.delete(channel.id);
cache.channels.set(channel.id, channel);
bot.dispatchedChannelIds.delete(channel.id);
bot.channels.set(channel.id, channel);
});
processing.delete(id);

View File

@@ -1,6 +1,6 @@
import { eventHandlers } from "../bot.ts";
import { Bot } from "../bot.ts";
export function loopObject<T = {}>(obj: {}, handler: (value: unknown, key: string) => unknown, log: string) {
export function loopObject<T = {}>(bot: Bot, obj: {}, handler: (value: unknown, key: string) => unknown, log: string) {
let res: Record<string, unknown> | unknown[] = {};
if (Array.isArray(obj)) {
@@ -9,18 +9,21 @@ export function loopObject<T = {}>(obj: {}, handler: (value: unknown, key: strin
for (const o of obj) {
if (typeof o === "object" && !Array.isArray(o) && o !== null) {
// A nested object
res.push(loopObject(o as {}, handler, log));
res.push(loopObject(bot, o as {}, handler, log));
} else {
res.push(handler(o, "array"));
}
}
} else {
for (const [key, value] of Object.entries(obj)) {
eventHandlers.debug?.("loop", log);
bot.events.debug(log);
if (typeof value === "object" && !Array.isArray(value) && value !== null && !(value instanceof Blob)) {
if (
Array.isArray(value) ||
(typeof value === "object" && !Array.isArray(value) && value !== null && !(value instanceof Blob))
) {
// A nested object
res[key] = loopObject(value as {}, handler, log);
res[key] = loopObject(bot, value as {}, handler, log);
} else {
res[key] = handler(value, key);
}