refactor: rename controllers to handlers and handlers to helpers (#660)

* refactor: rename controllers to handlers and handlers to helpers

* fmt
This commit is contained in:
ayntee
2021-03-11 21:41:03 +04:00
committed by GitHub
parent aaed064709
commit 8654aeded5
72 changed files with 271 additions and 296 deletions
+103
View File
@@ -0,0 +1,103 @@
import {
eventHandlers,
lastShardID,
setApplicationID,
setBotID,
} from "../bot.ts";
import { DiscordPayload, ReadyPayload } from "../types/discord.ts";
import { cache } from "../util/cache.ts";
import { delay } from "../util/utils.ts";
import { allowNextShard, basicShards } from "../ws/mod.ts";
import { initialMemberLoadQueue } from "../structures/guild.ts";
import { structures } from "../structures/mod.ts";
import { cacheHandlers } from "../cache.ts";
export async function handleReady(
data: DiscordPayload,
shardID: number,
) {
// The bot has already started, the last shard is resumed, however.
if (cache.isReady) return;
const payload = data.d as ReadyPayload;
setBotID(payload.user.id);
setApplicationID(payload.application.id);
// Triggered on each shard
eventHandlers.shardReady?.(shardID);
// Save when the READY event was received to prevent infinite load loops
const now = Date.now();
const shard = basicShards.get(shardID);
if (!shard) return;
// Set ready to false just to go sure
shard.ready = false;
// All guilds are unavailable at first
shard.unavailableGuildIDs = new Set(payload.guilds.map((g) => g.id));
// Start ready check in 2 seconds
setTimeout(() => checkReady(payload, shardID, now), 2000);
// Wait 5 seconds to spawn next shard
await delay(5000);
allowNextShard();
}
// Don't pass the shard itself because unavailableGuilds won't be updated by the GUILD_CREATE event
/** This function checks if the shard is fully loaded */
function checkReady(payload: ReadyPayload, shardID: number, now: number) {
const shard = basicShards.get(shardID);
if (!shard) return;
// Check if all guilds were loaded
if (shard.unavailableGuildIDs.size) {
if (Date.now() - now > 10000) {
eventHandlers.shardFailedToLoad?.(shardID, shard.unavailableGuildIDs);
// Force execute the loaded function to prevent infinite loop
loaded(shardID);
} else {
// Not all guilds were loaded but 10 seconds haven't passed so check again
setTimeout(() => checkReady(payload, shardID, now), 2000);
}
} else {
// All guilds were loaded
loaded(shardID);
}
}
async function loaded(shardID: number) {
const shard = basicShards.get(shardID);
if (!shard) return;
shard.ready = true;
// If it is the last shard we can go full ready
if (shardID === lastShardID - 1) {
// Still some shards are loading so wait another 2 seconds for them
if (basicShards.some((shard) => !shard.ready)) {
setTimeout(() => loaded(shardID), 2000);
} else {
cache.isReady = true;
eventHandlers.ready?.();
// All the members that came in on guild creates should now be processed 1 by 1
for (const [guildID, members] of initialMemberLoadQueue.entries()) {
await Promise.allSettled(
members.map(async (member) => {
const memberStruct = await structures.createMemberStruct(
member,
guildID,
);
return cacheHandlers.set(
"members",
memberStruct.id,
memberStruct,
);
}),
);
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
import { eventHandlers } from "../../bot.ts";
import { ChannelCreatePayload, DiscordPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleChannelCreate(data: DiscordPayload) {
const payload = data.d as ChannelCreatePayload;
const channelStruct = await structures.createChannelStruct(payload);
await cacheHandlers.set("channels", channelStruct.id, channelStruct);
eventHandlers.channelCreate?.(channelStruct);
}
+40
View File
@@ -0,0 +1,40 @@
import { eventHandlers } from "../../bot.ts";
import {
ChannelCreatePayload,
ChannelTypes,
DiscordPayload,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleChannelDelete(data: DiscordPayload) {
const payload = data.d as ChannelCreatePayload;
const cachedChannel = await cacheHandlers.get("channels", payload.id);
if (!cachedChannel) return;
if (cachedChannel.type === ChannelTypes.GUILD_VOICE && payload.guild_id) {
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (guild) {
return Promise.all(guild.voiceStates.map(async (vs, key) => {
if (vs.channelID !== payload.id) return;
// Since this channel was deleted all voice states for this channel should be deleted
guild.voiceStates.delete(key);
const member = await cacheHandlers.get("members", vs.userID);
if (!member) return;
eventHandlers.voiceChannelLeave?.(member, vs.channelID);
}));
}
}
await cacheHandlers.delete("channels", payload.id);
cacheHandlers.forEach("messages", (message) => {
if (message.channelID === payload.id) {
cacheHandlers.delete("messages", message.id);
}
});
eventHandlers.channelDelete?.(cachedChannel);
}
@@ -0,0 +1,19 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordChannelPinsUpdateEvent,
DiscordPayload,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleChannelPinsUpdate(data: DiscordPayload) {
const payload = data.d as DiscordChannelPinsUpdateEvent;
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (!channel) return;
const guild = payload.guild_id
? await cacheHandlers.get("guilds", payload.guild_id)
: undefined;
eventHandlers.channelPinsUpdate?.(channel, guild, payload.last_pin_timestamp);
}
+16
View File
@@ -0,0 +1,16 @@
import { eventHandlers } from "../../bot.ts";
import { ChannelCreatePayload, DiscordPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleChannelUpdate(data: DiscordPayload) {
const payload = data.d as ChannelCreatePayload;
const cachedChannel = await cacheHandlers.get("channels", payload.id);
const channelStruct = await structures.createChannelStruct(payload);
await cacheHandlers.set("channels", channelStruct.id, channelStruct);
if (!cachedChannel) return;
eventHandlers.channelUpdate?.(channelStruct, cachedChannel);
}
@@ -0,0 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { ApplicationCommandEvent, DiscordPayload } from "../../types/mod.ts";
export function handleApplicationCommandCreate(
data: DiscordPayload,
) {
const {
guild_id: guildID,
application_id: applicationID,
...rest
} = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandCreate?.({
...rest,
guildID,
applicationID,
});
}
@@ -0,0 +1,16 @@
import { eventHandlers } from "../../bot.ts";
import { ApplicationCommandEvent, DiscordPayload } from "../../types/mod.ts";
export function handleApplicationCommandDelete(data: DiscordPayload) {
const {
application_id: applicationID,
guild_id: guildID,
...rest
} = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandDelete?.({
...rest,
guildID,
applicationID,
});
}
@@ -0,0 +1,16 @@
import { eventHandlers } from "../../bot.ts";
import { ApplicationCommandEvent, DiscordPayload } from "../../types/mod.ts";
export function handleApplicationCommandUpdate(data: DiscordPayload) {
const {
application_id: applicationID,
guild_id: guildID,
...rest
} = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandUpdate?.({
...rest,
guildID,
applicationID,
});
}
+12
View File
@@ -0,0 +1,12 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildBanPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildBanAdd(data: DiscordPayload) {
const payload = data.d as GuildBanPayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const member = await cacheHandlers.get("members", payload.user.id);
eventHandlers.guildBanAdd?.(guild, payload.user, member);
}
+12
View File
@@ -0,0 +1,12 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildBanPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildBanRemove(data: DiscordPayload) {
const payload = data.d as GuildBanPayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const member = await cacheHandlers.get("members", payload.user.id);
eventHandlers.guildBanRemove?.(guild, payload.user, member);
}
+32
View File
@@ -0,0 +1,32 @@
import { eventHandlers } from "../../bot.ts";
import { CreateGuildPayload, DiscordPayload } from "../../types/mod.ts";
import { cache } from "../../util/cache.ts";
import { basicShards } from "../../ws/shard.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildCreate(
data: DiscordPayload,
shardID: number,
) {
const payload = data.d as CreateGuildPayload;
// When shards resume they emit GUILD_CREATE again.
if (await cacheHandlers.has("guilds", payload.id)) return;
const guildStruct = await structures.createGuildStruct(
data.d as CreateGuildPayload,
shardID,
);
await cacheHandlers.set("guilds", guildStruct.id, guildStruct);
const shard = basicShards.get(shardID);
if (shard?.unavailableGuildIDs.has(payload.id)) {
await cacheHandlers.delete("unavailableGuilds", payload.id);
shard.unavailableGuildIDs.delete(payload.id);
}
if (!cache.isReady) return eventHandlers.guildLoaded?.(guildStruct);
eventHandlers.guildCreate?.(guildStruct);
}
+49
View File
@@ -0,0 +1,49 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildDeletePayload } from "../../types/mod.ts";
import { basicShards } from "../../ws/shard.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildDelete(
data: DiscordPayload,
shardID: number,
) {
const payload = data.d as GuildDeletePayload;
cacheHandlers.forEach("messages", (message) => {
if (message.guildID === payload.id) {
cacheHandlers.delete("messages", message.id);
}
});
cacheHandlers.forEach("channels", (channel) => {
if (channel.guildID === payload.id) {
cacheHandlers.delete("channels", channel.id);
}
});
cacheHandlers.forEach("members", async (member) => {
if (!member.guilds.has(payload.id)) return;
member.guilds.delete(payload.id);
if (!member.guilds.size) {
await cacheHandlers.delete("members", member.id);
return;
}
await cacheHandlers.set("members", member.id, member);
});
if (payload.unavailable) {
const shard = basicShards.get(shardID);
if (shard) shard.unavailableGuildIDs.add(payload.id);
return cacheHandlers.set("unavailableGuilds", payload.id, Date.now());
}
const guild = await cacheHandlers.get("guilds", payload.id);
if (!guild) return;
await cacheHandlers.delete("guilds", payload.id);
eventHandlers.guildDelete?.(guild);
}
@@ -0,0 +1,23 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildEmojisUpdatePayload } from "../../types/mod.ts";
import { Collection } from "../../util/collection.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildEmojisUpdate(data: DiscordPayload) {
const payload = data.d as GuildEmojisUpdatePayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const cachedEmojis = guild.emojis;
guild.emojis = new Collection(
payload.emojis.map((emoji) => [emoji.id ?? emoji.name, emoji]),
);
cacheHandlers.set("guilds", payload.guild_id, guild);
eventHandlers.guildEmojisUpdate?.(
guild,
guild.emojis,
cachedEmojis,
);
}
@@ -0,0 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordGuildIntegrationsUpdateEvent,
DiscordPayload,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildIntegrationsUpdate(
data: DiscordPayload,
) {
const payload = data.d as DiscordGuildIntegrationsUpdateEvent;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
eventHandlers.guildIntegrationsUpdate?.(guild);
}
@@ -0,0 +1,44 @@
import { DiscordPayload, GuildMemberChunkPayload } from "../../types/mod.ts";
import { cache } from "../../util/cache.ts";
import { Collection } from "../../util/collection.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildMembersChunk(data: DiscordPayload) {
const payload = data.d as GuildMemberChunkPayload;
const members = await Promise.all(
payload.members.map(async (member) => {
const memberStruct = await structures.createMemberStruct(
member,
payload.guild_id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
return memberStruct;
}),
);
// 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;
if (payload.chunk_index + 1 === payload.chunk_count) {
cache.fetchAllMembersProcessingRequests.delete(payload.nonce);
// Only 1 chunk most likely is all members or users only request a small amount of users
if (payload.chunk_count === 1) {
return resolve(new Collection(members.map((m) => [m.id, m])));
}
return resolve(
await cacheHandlers.filter(
"members",
(m) => m.guilds.has(payload.guild_id),
),
);
}
}
}
+19
View File
@@ -0,0 +1,19 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildMemberAddPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildMemberAdd(data: DiscordPayload) {
const payload = data.d as GuildMemberAddPayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
guild.memberCount++;
const memberStruct = await structures.createMemberStruct(
payload,
payload.guild_id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
eventHandlers.guildMemberAdd?.(guild, memberStruct);
}
@@ -0,0 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildBanPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildMemberRemove(data: DiscordPayload) {
const payload = data.d as GuildBanPayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
guild.memberCount--;
const member = await cacheHandlers.get("members", payload.user.id);
eventHandlers.guildMemberRemove?.(guild, payload.user, member);
member?.guilds.delete(guild.id);
if (member && !member.guilds.size) {
await cacheHandlers.delete("members", member.id);
}
}
@@ -0,0 +1,59 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildMemberUpdatePayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildMemberUpdate(data: DiscordPayload) {
const payload = data.d as GuildMemberUpdatePayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const cachedMember = await cacheHandlers.get("members", payload.user.id);
const guildMember = cachedMember?.guilds.get(payload.guild_id);
const newMemberData = {
...payload,
// deno-lint-ignore camelcase
premium_since: payload.premium_since || undefined,
// deno-lint-ignore camelcase
joined_at: new Date(guildMember?.joinedAt || Date.now())
.toISOString(),
deaf: guildMember?.deaf || false,
mute: guildMember?.mute || false,
roles: payload.roles,
};
const memberStruct = await structures.createMemberStruct(
newMemberData,
payload.guild_id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
if (guildMember?.nick !== payload.nick) {
eventHandlers.nicknameUpdate?.(
guild,
memberStruct,
payload.nick,
guildMember?.nick,
);
}
if (payload.pending === false && guildMember?.pending === true) {
eventHandlers.membershipScreeningPassed?.(guild, memberStruct);
}
const roleIDs = guildMember?.roles || [];
roleIDs.forEach((id) => {
if (!payload.roles.includes(id)) {
eventHandlers.roleLost?.(guild, memberStruct, id);
}
});
payload.roles.forEach((id) => {
if (!roleIDs.includes(id)) {
eventHandlers.roleGained?.(guild, memberStruct, id);
}
});
eventHandlers.guildMemberUpdate?.(guild, memberStruct, cachedMember);
}
+20
View File
@@ -0,0 +1,20 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
GuildRoleDeletePayload,
GuildRolePayload,
} from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildRoleCreate(data: DiscordPayload) {
const payload = data.d as GuildRolePayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const role = await structures.createRoleStruct(payload.role);
guild.roles = guild.roles.set(payload.role.id, role);
await cacheHandlers.set("guilds", payload.guild_id, guild);
eventHandlers.roleCreate?.(guild, role);
}
+28
View File
@@ -0,0 +1,28 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, GuildRoleDeletePayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildRoleDelete(data: DiscordPayload) {
const payload = data.d as GuildRoleDeletePayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const cachedRole = guild.roles.get(payload.role_id)!;
guild.roles.delete(payload.role_id);
if (cachedRole) eventHandlers.roleDelete?.(guild, cachedRole);
// For bots without GUILD_MEMBERS member.roles is never updated breaking permissions checking.
cacheHandlers.forEach("members", (member) => {
// Not in the relevant guild so just skip.
if (!member.guilds.has(guild.id)) return;
member.guilds.forEach((g) => {
// Member does not have this role
if (!g.roles.includes(payload.role_id)) return;
// Remove this role from the members cache
g.roles = g.roles.filter((id) => id !== payload.role_id);
cacheHandlers.set("members", member.id, member);
});
});
}
+23
View File
@@ -0,0 +1,23 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
GuildRoleDeletePayload,
GuildRolePayload,
} from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildRoleUpdate(data: DiscordPayload) {
const payload = data.d as GuildRolePayload;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const cachedRole = guild.roles.get(payload.role.id);
if (!cachedRole) return;
const role = await structures.createRoleStruct(payload.role);
guild.roles.set(payload.role.id, role);
await cacheHandlers.set("guilds", guild.id, guild);
eventHandlers.roleUpdate?.(guild, role, cachedRole);
}
+48
View File
@@ -0,0 +1,48 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
GuildUpdateChange,
UpdateGuildPayload,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleGuildUpdate(data: DiscordPayload) {
const payload = data.d as UpdateGuildPayload;
const cachedGuild = await cacheHandlers.get("guilds", payload.id);
if (!cachedGuild) return;
const keysToSkip = [
"roles",
"guild_hashes",
"guild_id",
"max_members",
"emojis",
];
const changes = Object.entries(payload)
.map(([key, value]) => {
if (keysToSkip.includes(key)) return;
// @ts-ignore index signature
const cachedValue = cachedGuild[key];
if (cachedValue !== value) {
// Guild create sends undefined and update sends false.
if (!cachedValue && !value) return;
if (Array.isArray(cachedValue) && Array.isArray(value)) {
const different = (cachedValue.length !== value.length) ||
cachedValue.find((val) => !value.includes(val)) ||
value.find((val) => !cachedValue.includes(val));
if (!different) return;
}
// @ts-ignore index signature
cachedGuild[key] = value;
return { key, oldValue: cachedValue, value };
}
}).filter((change) => change) as GuildUpdateChange[];
await cacheHandlers.set("guilds", payload.id, cachedGuild);
eventHandlers.guildUpdate?.(cachedGuild, changes);
}
@@ -0,0 +1,31 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
IntegrationCreateUpdateEvent,
} from "../../types/mod.ts";
export function handleIntegrationCreate(
data: DiscordPayload,
) {
const {
guild_id: guildID,
enable_emoticons: enableEmoticons,
expire_behavior: expireBehavior,
expire_grace_period: expireGracePeriod,
subscriber_count: subscriberCount,
role_id: roleID,
synced_at: syncedAt,
...rest
} = data.d as IntegrationCreateUpdateEvent;
eventHandlers.integrationCreate?.({
...rest,
guildID,
enableEmoticons,
expireBehavior,
expireGracePeriod,
syncedAt,
subscriberCount,
roleID,
});
}
@@ -0,0 +1,16 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, IntegrationDeleteEvent } from "../../types/mod.ts";
export function handleIntegrationDelete(data: DiscordPayload) {
const {
guild_id: guildID,
application_id: applicationID,
...rest
} = data.d as IntegrationDeleteEvent;
eventHandlers.integrationDelete?.({
...rest,
applicationID,
guildID,
});
}
@@ -0,0 +1,29 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
IntegrationCreateUpdateEvent,
} from "../../types/mod.ts";
export function handleIntegrationUpdate(data: DiscordPayload) {
const {
enable_emoticons: enableEmoticons,
expire_behavior: expireBehavior,
expire_grace_period: expireGracePeriod,
role_id: roleID,
subscriber_count: subscriberCount,
synced_at: syncedAt,
guild_id: guildID,
...rest
} = data.d as IntegrationCreateUpdateEvent;
eventHandlers.integrationUpdate?.({
...rest,
guildID,
subscriberCount,
enableEmoticons,
expireGracePeriod,
roleID,
expireBehavior,
syncedAt,
});
}
@@ -0,0 +1,20 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, InteractionCommandPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleInteractionCreate(data: DiscordPayload) {
const payload = data.d as InteractionCommandPayload;
const memberStruct = await structures.createMemberStruct(
payload.member,
payload.guild_id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
eventHandlers.interactionCreate?.(
{
...payload,
member: memberStruct,
},
);
}
+28
View File
@@ -0,0 +1,28 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, InviteCreateEvent } from "../../types/mod.ts";
export function handleInviteCreate(payload: DiscordPayload) {
if (payload.t !== "INVITE_CREATE") return;
//TODO: replace with tocamelcase
const {
channel_id: channelID,
created_at: createdAt,
max_age: maxAge,
guild_id: guildID,
target_user: targetUser,
target_user_type: targetUserType,
max_uses: maxUses,
...rest
} = payload.d as InviteCreateEvent;
eventHandlers.inviteCreate?.({
...rest,
channelID,
guildID,
maxAge,
targetUser,
targetUserType,
maxUses,
createdAt,
});
}
+18
View File
@@ -0,0 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, InviteDeleteEvent } from "../../types/mod.ts";
export function handleInviteDelete(payload: DiscordPayload) {
if (payload.t !== "INVITE_DELETE") return;
const {
channel_id: channelID,
guild_id: guildID,
...rest
} = payload.d as InviteDeleteEvent;
eventHandlers.inviteDelete?.({
...rest,
channelID,
guildID,
});
}
+41
View File
@@ -0,0 +1,41 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageCreateOptions } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageCreate(data: DiscordPayload) {
const payload = data.d as MessageCreateOptions;
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (channel) channel.lastMessageID = payload.id;
const guild = payload.guild_id
? await cacheHandlers.get("guilds", payload.guild_id)
: undefined;
if (payload.member && guild) {
// If in a guild cache the author as a member
const memberStruct = await structures.createMemberStruct(
{ ...payload.member, user: payload.author },
guild.id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
}
await Promise.all(payload.mentions.map(async (mention) => {
// Cache the member if its a valid member
if (mention.member && guild) {
const memberStruct = await structures.createMemberStruct(
{ ...mention.member, user: mention },
guild.id,
);
return cacheHandlers.set("members", memberStruct.id, memberStruct);
}
}));
const message = await structures.createMessageStruct(payload);
// Cache the message
await cacheHandlers.set("messages", payload.id, message);
eventHandlers.messageCreate?.(message);
}
+16
View File
@@ -0,0 +1,16 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageDeletePayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageDelete(data: DiscordPayload) {
const payload = data.d as MessageDeletePayload;
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (!channel) return;
eventHandlers.messageDelete?.(
{ id: payload.id, channel },
await cacheHandlers.get("messages", payload.id),
);
await cacheHandlers.delete("messages", payload.id);
}
@@ -0,0 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageDeleteBulkPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageDeleteBulk(data: DiscordPayload) {
const payload = data.d as MessageDeleteBulkPayload;
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (!channel) return;
return Promise.all(payload.ids.map(async (id) => {
eventHandlers.messageDelete?.(
{ id, channel },
await cacheHandlers.get("messages", id),
);
await cacheHandlers.delete("messages", id);
}));
}
@@ -0,0 +1,56 @@
import { botID, eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageReactionPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageReactionAdd(data: DiscordPayload) {
const payload = data.d as MessageReactionPayload;
const message = await cacheHandlers.get("messages", payload.message_id);
if (message) {
const reactionExisted = message.reactions?.find(
(reaction) =>
reaction.emoji.id === payload.emoji.id &&
reaction.emoji.name === payload.emoji.name,
);
if (reactionExisted) reactionExisted.count++;
else {
const newReaction = {
count: 1,
me: payload.user_id === botID,
emoji: { ...payload.emoji, id: payload.emoji.id || undefined },
};
message.reactions = message.reactions
? [...message.reactions, newReaction]
: [newReaction];
}
await cacheHandlers.set("messages", payload.message_id, message);
}
if (payload.member && payload.guild_id) {
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (guild) {
const memberStruct = await structures.createMemberStruct(
payload.member,
guild.id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
}
}
const uncachedOptions = {
...payload,
id: payload.message_id,
channelID: payload.channel_id,
guildID: payload.guild_id || "",
};
eventHandlers.reactionAdd?.(
uncachedOptions,
payload.emoji,
payload.user_id,
message,
);
}
@@ -0,0 +1,58 @@
import { botID, eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageReactionPayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageReactionRemove(
data: DiscordPayload,
) {
const payload = data.d as MessageReactionPayload;
const message = await cacheHandlers.get("messages", payload.message_id);
if (message) {
const reactionExisted = message.reactions?.find(
(reaction) =>
reaction.emoji.id === payload.emoji.id &&
reaction.emoji.name === payload.emoji.name,
);
if (reactionExisted) reactionExisted.count--;
else {
const newReaction = {
count: 1,
me: payload.user_id === botID,
emoji: { ...payload.emoji, id: payload.emoji.id || undefined },
};
message.reactions = message.reactions
? [...message.reactions, newReaction]
: [newReaction];
}
await cacheHandlers.set("messages", payload.message_id, message);
}
if (payload.member && payload.guild_id) {
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (guild) {
const memberStruct = await structures.createMemberStruct(
payload.member,
guild.id,
);
await cacheHandlers.set("members", memberStruct.id, memberStruct);
}
}
const uncachedOptions = {
...payload,
id: payload.message_id,
channelID: payload.channel_id,
guildID: payload.guild_id,
};
eventHandlers.reactionRemove?.(
uncachedOptions,
payload.emoji,
payload.user_id,
message,
);
}
@@ -0,0 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { BaseMessageReactionPayload, DiscordPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageReactionRemoveAll(
data: DiscordPayload,
) {
const payload = data.d as BaseMessageReactionPayload;
const message = await cacheHandlers.get("messages", payload.message_id);
if (message?.reactions) {
message.reactions = undefined;
await cacheHandlers.set("messages", payload.message_id, message);
}
eventHandlers.reactionRemoveAll?.(data.d as BaseMessageReactionPayload);
}
@@ -0,0 +1,29 @@
import { botID, eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
MessageReactionRemoveEmojiPayload,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageReactionRemoveEmoji(
data: DiscordPayload,
) {
const payload = data.d as MessageReactionRemoveEmojiPayload;
const message = await cacheHandlers.get("messages", payload.message_id);
if (message?.reactions) {
message.reactions = message.reactions?.filter(
(reaction) =>
!(
reaction.emoji.id === payload.emoji.id &&
reaction.emoji.name === payload.emoji.name
),
);
await cacheHandlers.set("messages", payload.message_id, message);
}
eventHandlers.reactionRemoveEmoji?.(
data.d as MessageReactionRemoveEmojiPayload,
);
}
+36
View File
@@ -0,0 +1,36 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, MessageCreateOptions } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleMessageUpdate(data: DiscordPayload) {
const payload = data.d as MessageCreateOptions;
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (!channel) return;
const cachedMessage = await cacheHandlers.get("messages", payload.id);
if (!cachedMessage) return;
const oldMessage = {
attachments: cachedMessage.attachments,
content: cachedMessage.content,
embeds: cachedMessage.embeds,
editedTimestamp: cachedMessage.editedTimestamp,
tts: cachedMessage.tts,
pinned: cachedMessage.pinned,
};
// Messages with embeds can trigger update but they wont have edited_timestamp
if (
!payload.edited_timestamp ||
(cachedMessage.content === payload.content)
) {
return;
}
const message = await structures.createMessageStruct(payload);
await cacheHandlers.set("messages", payload.id, message);
eventHandlers.messageUpdate?.(message, oldMessage);
}
+149
View File
@@ -0,0 +1,149 @@
import { handleChannelCreate } from "./channels/CHANNEL_CREATE.ts";
import { handleChannelDelete } from "./channels/CHANNEL_DELETE.ts";
import { handleChannelPinsUpdate } from "./channels/CHANNEL_PINS_UPDATE.ts";
import { handleChannelUpdate } from "./channels/CHANNEL_UPDATE.ts";
import { handleApplicationCommandCreate } from "./commands/APPLICATION_COMMAND_CREATE.ts";
import { handleApplicationCommandDelete } from "./commands/APPLICATION_COMMAND_DELETE.ts";
import { handleApplicationCommandUpdate } from "./commands/APPLICATION_COMMAND_UPDATE.ts";
import { handleGuildBanAdd } from "./guilds/GUILD_BAN_ADD.ts";
import { handleGuildBanRemove } from "./guilds/GUILD_BAN_REMOVE.ts";
import { handleGuildCreate } from "./guilds/GUILD_CREATE.ts";
import { handleGuildDelete } from "./guilds/GUILD_DELETE.ts";
import { handleGuildEmojisUpdate } from "./guilds/GUILD_EMOJIS_UPDATE.ts";
import { handleGuildIntegrationsUpdate } from "./guilds/GUILD_INTEGRATIONS_UPDATE.ts";
import { handleGuildMembersChunk } from "./guilds/GUILD_MEMBERS_CHUNK.ts";
import { handleGuildMemberAdd } from "./guilds/GUILD_MEMBER_ADD.ts";
import { handleGuildMemberRemove } from "./guilds/GUILD_MEMBER_REMOVE.ts";
import { handleGuildMemberUpdate } from "./guilds/GUILD_MEMBER_UPDATE.ts";
import { handleGuildRoleCreate } from "./guilds/GUILD_ROLE_CREATE.ts";
import { handleGuildRoleDelete } from "./guilds/GUILD_ROLE_DELETE.ts";
import { handleGuildRoleUpdate } from "./guilds/GUILD_ROLE_UPDATE.ts";
import { handleGuildUpdate } from "./guilds/GUILD_UPDATE.ts";
import { handleIntegrationCreate } from "./integrations/INTEGRATION_CREATE.ts";
import { handleIntegrationDelete } from "./integrations/INTEGRATION_DELETE.ts";
import { handleIntegrationUpdate } from "./integrations/INTEGRATION_UPDATE.ts";
import { handleInteractionCreate } from "./interactions/INTERACTION_CREATE.ts";
import { handleInviteCreate } from "./invites/INVITE_CREATE.ts";
import { handleMessageCreate } from "./messages/MESSAGE_CREATE.ts";
import { handleMessageDelete } from "./messages/MESSAGE_DELETE.ts";
import { handleMessageDeleteBulk } from "./messages/MESSAGE_DELETE_BULK.ts";
import { handleMessageReactionAdd } from "./messages/MESSAGE_REACTION_ADD.ts";
import { handleMessageReactionRemove } from "./messages/MESSAGE_REACTION_REMOVE.ts";
import { handleMessageReactionRemoveAll } from "./messages/MESSAGE_REACTION_REMOVE_ALL.ts";
import { handleMessageReactionRemoveEmoji } from "./messages/MESSAGE_REACTION_REMOVE_EMOJI.ts";
import { handleMessageUpdate } from "./messages/MESSAGE_UPDATE.ts";
import { handlePresenceUpdate } from "./presence/PRESENCE_UPDATE.ts";
import { handleTypingStart } from "./presence/TYPING_START.ts";
import { handleUserUpdate } from "./presence/USER_UPDATE.ts";
import { handleReady } from "./READY.ts";
import { handleVoiceServerUpdate } from "./voice/VOICE_SERVER_UPDATE.ts";
import { handleVoiceStateUpdate } from "./voice/VOICE_STATE_UPDATE.ts";
import { handleWebhooksUpdate } from "./webhooks/WEBHOOKS_UPDATE.ts";
export {
handleApplicationCommandCreate,
handleApplicationCommandDelete,
handleApplicationCommandUpdate,
handleChannelCreate,
handleChannelDelete,
handleChannelPinsUpdate,
handleChannelUpdate,
handleGuildBanAdd,
handleGuildBanRemove,
handleGuildCreate,
handleGuildDelete,
handleGuildEmojisUpdate,
handleGuildIntegrationsUpdate,
handleGuildMemberAdd,
handleGuildMemberRemove,
handleGuildMembersChunk,
handleGuildMemberUpdate,
handleGuildRoleCreate,
handleGuildRoleDelete,
handleGuildRoleUpdate,
handleGuildUpdate,
handleIntegrationCreate,
handleIntegrationDelete,
handleIntegrationUpdate,
handleInteractionCreate,
handleInviteCreate,
handleMessageCreate,
handleMessageDelete,
handleMessageDeleteBulk,
handleMessageReactionAdd,
handleMessageReactionRemove,
handleMessageReactionRemoveAll,
handleMessageReactionRemoveEmoji,
handleMessageUpdate,
handlePresenceUpdate,
handleReady,
handleTypingStart,
handleUserUpdate,
handleVoiceServerUpdate,
handleVoiceStateUpdate,
handleWebhooksUpdate,
};
export let handlers = {
READY: handleReady,
// channels
CHANNEL_CREATE: handleChannelCreate,
CHANNEL_DELETE: handleChannelDelete,
CHANNEL_PINS_UPDATE: handleChannelPinsUpdate,
CHANNEL_UPDATE: handleChannelUpdate,
// commands
APPLICATION_COMMAND_CREATE: handleApplicationCommandCreate,
APPLICATION_COMMAND_DELETE: handleApplicationCommandDelete,
APPLICATION_COMMAND_UPDATE: handleApplicationCommandUpdate,
// guilds
GUILD_BAN_ADD: handleGuildBanAdd,
GUILD_BAN_REMOVE: handleGuildBanRemove,
GUILD_CREATE: handleGuildCreate,
GUILD_DELETE: handleGuildDelete,
GUILD_EMOJIS_UPDATE: handleGuildEmojisUpdate,
GUILD_INTEGRATIONS_UPDATE: handleGuildIntegrationsUpdate,
GUILD_MEMBER_ADD: handleGuildMemberAdd,
GUILD_MEMBER_REMOVE: handleGuildMemberRemove,
GUILD_MEMBER_UPDATE: handleGuildMemberUpdate,
GUILD_MEMBERS_CHUNK: handleGuildMembersChunk,
GUILD_ROLE_CREATE: handleGuildRoleCreate,
GUILD_ROLE_DELETE: handleGuildRoleDelete,
GUILD_ROLE_UPDATE: handleGuildRoleUpdate,
GUILD_UPDATE: handleGuildUpdate,
// interactions
INTERACTION_CREATE: handleInteractionCreate,
// invites
INVITE_CREATE: handleInviteCreate,
INVITE_DELETE: handleInviteCreate,
// messages
MESSAGE_CREATE: handleMessageCreate,
MESSAGE_DELETE_BULK: handleMessageDeleteBulk,
MESSAGE_DELETE: handleMessageDelete,
MESSAGE_REACTION_ADD: handleMessageReactionAdd,
MESSAGE_REACTION_REMOVE_ALL: handleMessageReactionRemoveAll,
MESSAGE_REACTION_REMOVE_EMOJI: handleMessageReactionRemoveEmoji,
MESSAGE_REACTION_REMOVE: handleMessageReactionRemove,
MESSAGE_UPDATE: handleMessageUpdate,
// presence
PRESENCE_UPDATE: handlePresenceUpdate,
TYPING_START: handleTypingStart,
USER_UPDATE: handleUserUpdate,
// voice
VOICE_SERVER_UPDATE: handleVoiceServerUpdate,
VOICE_STATE_UPDATE: handleVoiceStateUpdate,
// webhooks
WEBHOOKS_UPDATE: handleWebhooksUpdate,
// integrations
INTEGRATION_CREATE: handleIntegrationCreate,
INTEGRATION_UPDATE: handleIntegrationUpdate,
INTEGRATION_DELETE: handleIntegrationDelete,
};
export type Handlers = typeof handlers;
export function updateHandlers(newHandlers: Handlers) {
handlers = {
...handlers,
...newHandlers,
};
}
+11
View File
@@ -0,0 +1,11 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, PresenceUpdatePayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handlePresenceUpdate(data: DiscordPayload) {
const payload = data.d as PresenceUpdatePayload;
const oldPresence = await cacheHandlers.get("presences", payload.user.id);
await cacheHandlers.set("presences", payload.user.id, payload);
eventHandlers.presenceUpdate?.(payload, oldPresence);
}
+6
View File
@@ -0,0 +1,6 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, TypingStartPayload } from "../../types/mod.ts";
export function handleTypingStart(data: DiscordPayload) {
eventHandlers.typingStart?.(data.d as TypingStartPayload);
}
+19
View File
@@ -0,0 +1,19 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, UserPayload } from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleUserUpdate(data: DiscordPayload) {
const userData = data.d as UserPayload;
const member = await cacheHandlers.get("members", userData.id);
if (!member) return;
Object.entries(userData).forEach(([key, value]) => {
// @ts-ignore index signatures
if (member[key] !== value) return member[key] = value;
});
await cacheHandlers.set("members", userData.id, member);
eventHandlers.botUpdate?.(userData);
}
+15
View File
@@ -0,0 +1,15 @@
import { eventHandlers } from "../../bot.ts";
import {
DiscordPayload,
DiscordVoiceServerUpdateEvent,
} from "../../types/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleVoiceServerUpdate(data: DiscordPayload) {
const payload = data.d as DiscordVoiceServerUpdateEvent;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
eventHandlers.voiceServerUpdate?.(payload.token, guild, payload.endpoint);
}
+54
View File
@@ -0,0 +1,54 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, VoiceStateUpdatePayload } from "../../types/mod.ts";
import { structures } from "../../structures/mod.ts";
import { cacheHandlers } from "../../cache.ts";
export async function handleVoiceStateUpdate(data: DiscordPayload) {
const payload = data.d as VoiceStateUpdatePayload;
if (!payload.guild_id) return;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
if (!guild) return;
const member = payload.member
? await structures.createMemberStruct(payload.member, guild.id)
: await cacheHandlers.get("members", payload.user_id);
if (!member) return;
// No cached state before so lets make one for em
const cachedState = guild.voiceStates.get(payload.user_id);
guild.voiceStates.set(payload.user_id, {
...payload,
guildID: payload.guild_id,
channelID: payload.channel_id || "",
userID: payload.user_id,
sessionID: payload.session_id,
selfDeaf: payload.self_deaf,
selfMute: payload.self_mute,
selfStream: payload.self_stream || false,
});
await cacheHandlers.set("guilds", payload.guild_id, guild);
if (cachedState?.channelID !== payload.channel_id) {
// Either joined or moved channels
if (payload.channel_id) {
if (cachedState?.channelID) { // Was in a channel before
eventHandlers.voiceChannelSwitch?.(
member,
payload.channel_id,
cachedState.channelID,
);
} else { // Was not in a channel before so user just joined
eventHandlers.voiceChannelJoin?.(member, payload.channel_id);
}
} // Left the channel
else if (cachedState?.channelID) {
guild.voiceStates.delete(payload.user_id);
eventHandlers.voiceChannelLeave?.(member, cachedState.channelID);
}
}
eventHandlers.voiceStateUpdate?.(member, payload);
}
+10
View File
@@ -0,0 +1,10 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordPayload, WebhookUpdatePayload } from "../../types/mod.ts";
export function handleWebhooksUpdate(data: DiscordPayload) {
const options = data.d as WebhookUpdatePayload;
eventHandlers.webhooksUpdate?.(
options.channel_id,
options.guild_id,
);
}