more handlers

This commit is contained in:
Skillz4Killz
2021-10-23 15:38:53 +00:00
committed by GitHub
parent cca34edaf9
commit 8b242c57aa
11 changed files with 289 additions and 112 deletions
+37 -5
View File
@@ -121,6 +121,9 @@ import {
} from "./handlers/mod.ts";
import { DiscordenoInteraction, transformInteraction } from "./transformers/interaction.ts";
import { DiscordenoIntegration, transformIntegration } from "./transformers/integration.ts";
import { transformApplication } from "./transformers/application.ts";
import { transformTeam } from "./transformers/team.ts";
import { DiscordenoInvite, transformInvite } from "./transformers/invite.ts";
export async function createBot(options: CreateBotOptions) {
return {
@@ -135,10 +138,17 @@ export async function createBot(options: CreateBotOptions) {
constants: createBotConstants(),
handlers: createBotGatewayHandlers({}),
cache: {
forEach: function (
type: "DELETE_MESSAGES_FROM_GUILD" | "DELETE_CHANNELS_FROM_GUILD" | "DELETE_GUILD_FROM_MEMBER",
execute: async function (
type:
| "GUILD_MEMBER_CHUNK"
| "GUILD_MEMBER_COUNT_DECREMENT"
| "GUILD_MEMBER_COUNT_INCREMENT"
| "DELETE_MESSAGES_FROM_GUILD"
| "DELETE_CHANNELS_FROM_GUILD"
| "DELETE_GUILD_FROM_MEMBER",
options: Record<string, any>
) {},
fetchAllMembersProcessingRequests: new Collection<string, Function>(),
guilds: {
get: async function (id: bigint): Promise<DiscordenoGuild | undefined> {
return {} as any as DiscordenoGuild;
@@ -233,7 +243,12 @@ export function createEventHandlers(events: Partial<EventHandlers>): EventHandle
dispatchRequirements: events.dispatchRequirements ?? ignore,
integrationCreate: events.integrationCreate ?? ignore,
integrationDelete: events.integrationDelete ?? ignore,
integrationUpdate: events.integrationUpdate ?? ignore,
interactionCreate: events.interactionCreate ?? ignore,
inviteCreate: events.inviteCreate ?? ignore,
inviteDelete: events.inviteDelete ?? ignore,
guildMemberAdd: events.guildMemberAdd ?? ignore,
guildMemberRemove: events.guildMemberRemove ?? ignore,
channelCreate: events.channelCreate ?? ignore,
voiceChannelLeave: events.voiceChannelLeave ?? ignore,
channelDelete: events.channelDelete ?? ignore,
@@ -246,7 +261,6 @@ export function createEventHandlers(events: Partial<EventHandlers>): EventHandle
guildCreate: events.guildCreate ?? ignore,
guildDelete: events.guildDelete ?? ignore,
guildUpdate: events.guildUpdate ?? ignore,
integrationsUpdate: events.integrationsUpdate ?? ignore,
raw: events.raw ?? ignore,
stageInstanceCreate: events.stageInstanceCreate ?? ignore,
stageInstanceDelete: events.stageInstanceDelete ?? ignore,
@@ -476,7 +490,10 @@ export interface Transformers {
role: typeof transformRole;
voiceState: typeof transformVoiceState;
interaction: typeof transformInteraction;
integration: typeof transformIntegration,
integration: typeof transformIntegration;
invite: typeof transformInvite;
application: typeof transformApplication;
team: typeof transformTeam;
}
export function createTransformers(options: Partial<Transformers>) {
@@ -490,6 +507,9 @@ export function createTransformers(options: Partial<Transformers>) {
role: options.role || transformRole,
voiceState: options.voiceState || transformVoiceState,
integration: options.integration || transformIntegration,
invite: options.invite || transformInvite,
application: options.application || transformApplication,
team: options.team || transformTeam,
};
}
@@ -602,7 +622,19 @@ export interface EventHandlers {
interactionCreate: (bot: Bot, interaction: DiscordenoInteraction) => any;
integrationCreate: (bot: Bot, integration: DiscordenoIntegration) => any;
integrationDelete: (bot: Bot, payload: { id: bigint; guildId: bigint; applicationId?: bigint }) => any;
integrationsUpdate: (bot: Bot, integration: DiscordenoIntegration) => any;
integrationUpdate: (bot: Bot, integration: DiscordenoIntegration) => any;
inviteCreate: (bot: Bot, invite: DiscordenoInvite) => any;
inviteDelete: (
bot: Bot,
payload: {
channelId: bigint;
guildId?: bigint;
code: string;
}
) => any;
guildMemberAdd: (bot: Bot, member: DiscordenoMember, user: DiscordenoUser) => any;
guildMemberRemove: (bot: Bot, user: DiscordenoUser, guildId: bigint) => any;
guildMemberUpdate: (bot: Bot, member: DiscordenoMember, user: DiscordenoUser) => any;
channelCreate: (bot: Bot, channel: DiscordenoChannel) => any;
dispatchRequirements: (bot: Bot, data: GatewayPayload, shardId: number) => any;
voiceChannelLeave: (bot: Bot, voiceState: DiscordenoVoiceState, channel: DiscordenoChannel) => any;
+4 -3
View File
@@ -1,7 +1,8 @@
import { eventHandlers } from "../../bot.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { InviteCreate } from "../../types/invites/invite_create.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export function handleInviteCreate(data: DiscordGatewayPayload) {
eventHandlers.inviteCreate?.(data.d as InviteCreate);
export function handleInviteCreate(bot: Bot, data: DiscordGatewayPayload) {
bot.events.inviteCreate(bot, bot.transformers.invite(bot, data.d as SnakeCasedPropertiesDeep<InviteCreate>));
}
+13 -3
View File
@@ -1,7 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { InviteDelete } from "../../types/invites/invite_delete.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export function handleInviteDelete(data: DiscordGatewayPayload) {
eventHandlers.inviteDelete?.(data.d as InviteDelete);
export function handleInviteDelete(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<InviteDelete>;
bot.events.inviteDelete(bot, {
/** The channel of the invite */
channelId: bot.transformers.snowflake(payload.channel_id),
/** The guild of the invite */
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
/** The unique invite code */
code: payload.code,
});
}
+20 -27
View File
@@ -1,37 +1,30 @@
import { cache, cacheHandlers } from "../../cache.ts";
import { structures } from "../../structures/mod.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { GuildMembersChunk } from "../../types/members/guild_members_chunk.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { Collection } from "../../util/collection.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildMembersChunk(data: DiscordGatewayPayload) {
const payload = data.d as GuildMembersChunk;
export async function handleGuildMembersChunk(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildMembersChunk>;
const guildId = snowflakeToBigint(payload.guildId);
const guildId = bot.transformers.snowflake(payload.guild_id);
const members = await Promise.all(
payload.members.map(async (member) => {
const discordenoMember = await structures.createDiscordenoMember(member, guildId);
await cacheHandlers.set("members", discordenoMember.id, discordenoMember);
return discordenoMember;
})
);
await bot.cache.execute("GUILD_MEMBER_CHUNK", {
members: payload.members.map((m) => bot.transformers.member(bot, m, guildId)),
users: payload.members.map((m) => bot.transformers.user(bot, m.user)),
});
if (!payload.nonce) return;
// Check if its necessary to resolve the fetchmembers promise for this chunk or if more chunks will be coming
if (payload.nonce) {
const resolve = cache.fetchAllMembersProcessingRequests.get(payload.nonce);
if (!resolve) return;
const resolve = bot.cache.fetchAllMembersProcessingRequests.get(payload.nonce);
if (!resolve) return;
if (payload.chunkIndex + 1 === payload.chunkCount) {
cache.fetchAllMembersProcessingRequests.delete(payload.nonce);
// Only 1 chunk most likely is all members or users only request a small amount of users
if (payload.chunkCount === 1) {
return resolve(new Collection(members.map((m) => [m.id, m])));
}
return resolve(await cacheHandlers.filter("GET_MEMBERS_IN_GUILD", { guildId }));
}
if (payload.chunk_index + 1 === payload.chunk_count) {
bot.cache.fetchAllMembersProcessingRequests.delete(payload.nonce);
return resolve("Finished chunking members");
}
}
// TODO: add a helper function that runs await fetch
// await fetchMembers();
// const members = await bot.cache.members.findMany(guildId);
+13 -12
View File
@@ -1,18 +1,19 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { structures } from "../../structures/mod.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { GuildMemberAdd } from "../../types/members/guild_member_add.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildMemberAdd(data: DiscordGatewayPayload) {
const payload = data.d as GuildMemberAdd;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
if (!guild) return;
export async function handleGuildMemberAdd(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildMemberAdd>;
const guildId = bot.transformers.snowflake(payload.guild_id);
const member = bot.transformers.member(bot, payload, guildId);
const user = bot.transformers.user(bot, payload.user);
guild.memberCount++;
const discordenoMember = await structures.createDiscordenoMember(payload, guild.id);
await cacheHandlers.set("members", discordenoMember.id, discordenoMember);
await Promise.all([
bot.cache.members.set(member.id, member),
bot.cache.users.set(user.id, user),
bot.cache.execute("GUILD_MEMBER_COUNT_INCREMENT", { guildId }),
]);
eventHandlers.guildMemberAdd?.(guild, discordenoMember);
bot.events.guildMemberAdd(bot, member, user);
}
+11 -13
View File
@@ -1,20 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { GuildMemberRemove } from "../../types/members/guild_member_remove.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
export async function handleGuildMemberRemove(data: DiscordGatewayPayload) {
const payload = data.d as GuildMemberRemove;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
if (!guild) return;
export async function handleGuildMemberRemove(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildMemberRemove>;
const guildId = bot.transformers.snowflake(payload.guild_id);
const user = bot.transformers.user(bot, payload.user);
guild.memberCount--;
const member = await cacheHandlers.get("members", snowflakeToBigint(payload.user.id));
eventHandlers.guildMemberRemove?.(guild, payload.user, member);
await Promise.all([
bot.cache.members.delete(user.id),
bot.cache.execute("GUILD_MEMBER_COUNT_DECREMENT", { guildId }),
]);
member?.guilds.delete(guild.id);
if (member && !member.guilds.size) {
await cacheHandlers.delete("members", member.id);
}
bot.events.guildMemberRemove(bot, user, guildId);
}
+11 -48
View File
@@ -1,54 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { structures } from "../../structures/mod.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { GuildMemberUpdate } from "../../types/members/guild_member_update.ts";
import { bigintToSnowflake, snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildMemberUpdate(data: DiscordGatewayPayload) {
const payload = data.d as GuildMemberUpdate;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
if (!guild) return;
export async function handleGuildMemberUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildMemberUpdate>;
const cachedMember = await cacheHandlers.get("members", snowflakeToBigint(payload.user.id));
const guildMember = cachedMember?.guilds.get(guild.id);
// TODO: IDK IF THIS IS BUT IS IT LURKER IN STAGE CHANNEL WHO ISN'T A MEMBER
if (!payload.joined_at) return;
const newMemberData = {
...payload,
premiumSince: payload.premiumSince || undefined,
joinedAt: new Date(guildMember?.joinedAt || Date.now()).toISOString(),
deaf: guildMember?.deaf || false,
mute: guildMember?.mute || false,
roles: payload.roles,
};
const discordenoMember = await structures.createDiscordenoMember(newMemberData, guild.id);
await cacheHandlers.set("members", discordenoMember.id, discordenoMember);
if (guildMember) {
if (guildMember.nick !== payload.nick) {
eventHandlers.nicknameUpdate?.(guild, discordenoMember, payload.nick!, guildMember.nick ?? undefined);
}
if (payload.pending === false && guildMember.pending === true) {
eventHandlers.membershipScreeningPassed?.(guild, discordenoMember);
}
const roleIds = guildMember.roles || [];
roleIds.forEach((id) => {
eventHandlers.debug?.("loop", `1. Running forEach loop in GUILD_MEMBER_UPDATE file.`);
if (!payload.roles.includes(bigintToSnowflake(id))) {
eventHandlers.roleLost?.(guild, discordenoMember, id);
}
});
payload.roles.forEach((id) => {
eventHandlers.debug?.("loop", `2. Running forEach loop in GUILD_MEMBER_UPDATE file.`);
if (!roleIds.includes(snowflakeToBigint(id))) {
eventHandlers.roleGained?.(guild, discordenoMember, snowflakeToBigint(id));
}
});
}
eventHandlers.guildMemberUpdate?.(guild, discordenoMember, cachedMember);
bot.events.guildMemberUpdate(
bot,
bot.transformers.member(bot, payload, bot.transformers.snowflake(payload.guild_id)),
bot.transformers.user(bot, payload.user)
);
}
+69
View File
@@ -0,0 +1,69 @@
import { Bot } from "../bot.ts";
import { Application } from "../types/applications/application.ts";
import { DiscordApplicationFlags } from "../types/applications/application_flags.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
import { DiscordenoUser } from "./member.ts";
import { DiscordenoTeam } from "./team.ts";
export function transformApplication(bot: Bot, payload: SnakeCasedPropertiesDeep<Application>): DiscordenoApplication {
return {
name: payload.name,
description: payload.description,
rpcOrigins: payload.rpc_origins,
botPublic: payload.bot_public,
botRequireCodeGrant: payload.bot_require_code_grant,
termsOfServiceUrl: payload.terms_of_service_url,
privacyPolicyUrl: payload.privacy_policy_url,
summary: payload.summary,
verifyKey: payload.verify_key,
primarySkuId: payload.primary_sku_id,
slug: payload.slug,
coverImage: payload.cover_image,
flags: payload.flags,
id: bot.transformers.snowflake(payload.id),
icon: payload.icon ? bot.utils.iconHashToBigInt(payload.icon) : undefined,
owner: payload.owner ? bot.transformers.user(bot, payload.owner) : undefined,
team: payload.team ? bot.transformers.team(bot, payload.team) : undefined,
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
};
}
export interface DiscordenoApplication {
/** The id of the app */
id: bigint;
/** The name of the app */
name: string;
/** The icon hash of the app */
icon?: bigint;
/** The description of the app */
description: string;
/** An array of rpc origin urls, if rpc is enabled */
rpcOrigins?: string[];
/** When false only app owner can join the app's bot to guilds */
botPublic: boolean;
/** When true the app's bot will only join upon completion of the full oauth2 code grant flow */
botRequireCodeGrant: boolean;
/** The url of the app's terms of service */
termsOfServiceUrl?: string;
/** The url of the app's privacy policy */
privacyPolicyUrl?: string;
/** Partial user object containing info on the owner of the application */
owner?: Partial<DiscordenoUser>;
/** If this application is a game sold on Discord, this field will be the summary field for the store page of its primary sku */
summary: string;
/** The hex encoded key for verification in interactions and the GameSDK's GetTicket */
verifyKey: string;
/** If the application belongs to a team, this will be a list of the members of that team */
team?: DiscordenoTeam;
/** If this application is a game sold on Discord, this field will be the guild to which it has been linked */
guildId?: bigint;
/** If this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists */
primarySkuId?: string;
/** If this application is a game sold on Discord, this field will be the URL slug that links to the store page */
slug?: string;
/** If this application is a game sold on Discord, this field will be the hash of the image on store embeds */
coverImage?: string;
/** The application's public flags */
flags: DiscordApplicationFlags;
}
+63
View File
@@ -0,0 +1,63 @@
import { Bot } from "../bot.ts";
import { InviteCreate, DiscordTargetTypes, User, Application } from "../types/mod.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
import { DiscordenoApplication } from "./application.ts";
import { DiscordenoUser } from "./member.ts";
export function transformInvite(bot: Bot, invite: SnakeCasedPropertiesDeep<InviteCreate>): DiscordenoInvite {
return {
/** The channel the invite is for */
channelId: bot.transformers.snowflake(invite.channel_id),
/** The unique invite code */
code: invite.code,
/** The time at which the invite was created */
createdAt: Date.parse(invite.created_at),
/** The guild of the invite */
guildId: invite.guild_id ? bot.transformers.snowflake(invite.guild_id) : undefined,
/** The user that created the invite */
inviter: invite.inviter ? bot.transformers.user(bot, invite.inviter) : undefined,
/** How long the invite is valid for (in seconds) */
maxAge: invite.max_age,
/** The maximum number of times the invite can be used */
maxUses: invite.max_uses,
/** The type of target for this voice channel invite */
targetType: invite.target_type,
/** The target user for this invite */
targetUser: invite.target_user ? bot.transformers.user(bot, invite.target_user) : undefined,
/** The embedded application to open for this voice channel embedded application invite */
targetApplication: invite.target_application
? bot.transformers.application(bot, invite.target_application)
: undefined,
/** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */
temporary: invite.temporary,
/** How many times the invite has been used (always will be 0) */
uses: invite.uses,
};
}
export interface DiscordenoInvite {
/** The channel the invite is for */
channelId: bigint;
/** The unique invite code */
code: string;
/** The time at which the invite was created */
createdAt: number;
/** The guild of the invite */
guildId?: bigint;
/** The user that created the invite */
inviter?: DiscordenoUser;
/** How long the invite is valid for (in seconds) */
maxAge: number;
/** The maximum number of times the invite can be used */
maxUses: number;
/** The type of target for this voice channel invite */
targetType: DiscordTargetTypes;
/** The target user for this invite */
targetUser?: DiscordenoUser;
/** The embedded application to open for this voice channel embedded application invite */
targetApplication?: Partial<DiscordenoApplication>;
/** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */
temporary: boolean;
/** How many times the invite has been used (always will be 0) */
uses: number;
}
+47
View File
@@ -0,0 +1,47 @@
import { Bot } from "../bot.ts";
import { Team } from "../types/teams/team.ts";
import { DiscordTeamMembershipStates } from "../types/teams/team_membership_states.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
import { DiscordenoUser } from "./member.ts";
export function transformTeam(bot: Bot, payload: SnakeCasedPropertiesDeep<Team>): DiscordenoTeam {
const id = bot.transformers.snowflake(payload.id);
return {
name: payload.name,
id,
icon: payload.icon ? bot.utils.iconHashToBigInt(payload.icon) : undefined,
ownerUserId: bot.transformers.snowflake(payload.owner_user_id),
members: payload.members.map((member) => ({
membershipState: member.membership_state,
// TODO: think about this seems useless to add ["*"] to everything
permissions: member.permissions,
// TODO: think about this seems useless to add another id here when its also on the one above
teamId: id,
user: bot.transformers.user(bot, member.user),
})),
};
}
export interface DiscordenoTeam {
/** A hash of the image of the team's icon */
icon?: bigint;
/** The unique id of the team */
id: bigint;
/** The members of the team */
members: {
/** The user's membership state on the team */
membershipState: DiscordTeamMembershipStates;
/** Will always be `["*"]` */
permissions: "*"[];
/** The id of the parent team of which they are a member */
teamId: bigint;
/** The avatar, discriminator, id, and username of the user */
user: Partial<DiscordenoUser>;
}[];
/** The name of the team */
name: string;
/** The user id of the current team owner */
ownerUserId: bigint;
}
+1 -1
View File
@@ -21,7 +21,7 @@ export interface InviteCreate {
/** The type of target for this voice channel invite */
targetType: DiscordTargetTypes;
/** The target user for this invite */
targetUser?: Partial<User>;
targetUser?: User;
/** The embedded application to open for this voice channel embedded application invite */
targetApplication?: Partial<Application>;
/** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */