This commit is contained in:
Skillz4Killz
2021-10-24 19:13:23 +00:00
committed by GitHub
parent ebdbfcf721
commit 2427a5d2c8
17 changed files with 293 additions and 211 deletions
+40 -2
View File
@@ -293,6 +293,8 @@ import {
validDiscoveryTerm,
} from "./helpers/mod.ts";
import { DiscordenoEmoji, transformEmoji } from "./transformers/emoji.ts";
import { transformActivity } from "./transformers/activity.ts";
import { DiscordenoPresence, transformPresence } from "./transformers/presence.ts";
export async function createBot(options: CreateBotOptions) {
return {
@@ -309,6 +311,9 @@ export async function createBot(options: CreateBotOptions) {
cache: {
execute: async function (
type:
| "DELETE_MESSAGES_FROM_CHANNEL"
| "DELETE_ROLE_FROM_MEMBER"
| "BULK_DELETE_MESSAGES"
| "GUILD_MEMBER_CHUNK"
| "GUILD_MEMBER_COUNT_DECREMENT"
| "GUILD_MEMBER_COUNT_INCREMENT"
@@ -374,6 +379,20 @@ export async function createBot(options: CreateBotOptions) {
return;
},
},
presence: {
get: async function (id: bigint): Promise<DiscordenoPresence | undefined> {
return {} as any as DiscordenoPresence;
},
has: async function (id: bigint): Promise<boolean> {
return false;
},
set: async function (id: bigint, guild: DiscordenoPresence): Promise<void> {
return;
},
delete: async function (id: bigint): Promise<void> {
return;
},
},
users: {
get: async function (id: bigint): Promise<DiscordenoUser | undefined> {
return {} as any as DiscordenoUser;
@@ -440,6 +459,9 @@ export function createEventHandlers(events: Partial<EventHandlers>): EventHandle
reactionRemove: events.reactionRemove ?? ignore,
reactionRemoveAll: events.reactionRemoveAll ?? ignore,
reactionRemoveEmoji: events.reactionRemoveEmoji ?? ignore,
presenceUpdate: events.presenceUpdate ?? ignore,
voiceServerUpdate: events.voiceServerUpdate ?? ignore,
voiceStateUpdate: events.voiceStateUpdate ?? ignore,
channelCreate: events.channelCreate ?? ignore,
voiceChannelLeave: events.voiceChannelLeave ?? ignore,
channelDelete: events.channelDelete ?? ignore,
@@ -1018,10 +1040,13 @@ export interface Transformers {
application: typeof transformApplication;
team: typeof transformTeam;
emoji: typeof transformEmoji;
activity: typeof transformActivity;
presence: typeof transformPresence;
}
export function createTransformers(options: Partial<Transformers>) {
return {
activity: options.activity || transformActivity,
application: options.application || transformApplication,
channel: options.channel || transformChannel,
emoji: options.emoji || transformEmoji,
@@ -1031,6 +1056,7 @@ export function createTransformers(options: Partial<Transformers>) {
invite: options.invite || transformInvite,
member: options.member || transformMember,
message: options.message || transformMessage,
presence: options.presence || transformPresence,
role: options.role || transformRole,
user: options.user || transformUser,
team: options.team || transformTeam,
@@ -1149,7 +1175,7 @@ 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;
integrationUpdate: (bot: Bot, integration: DiscordenoIntegration) => any;
integrationUpdate: (bot: Bot, payload: { guildId: bigint }) => any;
inviteCreate: (bot: Bot, invite: DiscordenoInvite) => any;
inviteDelete: (
bot: Bot,
@@ -1207,9 +1233,21 @@ export interface EventHandlers {
guildId?: bigint;
}
) => any;
presenceUpdate: (bot: Bot, presence: DiscordenoPresence, oldPresence?: DiscordenoPresence) => any;
voiceServerUpdate: (bot: Bot, payload: { token: string; endpoint?: string; guildId: bigint }) => any;
voiceStateUpdate: (
bot: Bot,
voiceState: DiscordenoVoiceState,
payload: { guild?: DiscordenoGuild; 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;
voiceChannelLeave: (
bot: Bot,
voiceState: DiscordenoVoiceState,
guild: DiscordenoGuild,
channel?: DiscordenoChannel
) => any;
channelDelete: (bot: Bot, channel: DiscordenoChannel) => any;
channelPinsUpdate: (bot: Bot, data: { guildId?: bigint; channelId: bigint; lastPinTimestamp?: number }) => any;
channelUpdate: (bot: Bot, channel: DiscordenoChannel, oldChannel: DiscordenoChannel) => any;
+10 -6
View File
@@ -6,18 +6,22 @@ import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleChannelDelete(bot: Bot, data: SnakeCasedPropertiesDeep<DiscordGatewayPayload>) {
const payload = data.d as SnakeCasedPropertiesDeep<Channel>;
if (!payload.guild_id) return;
const channel = await bot.cache.channels.get(bot.transformers.snowflake(payload.id));
const [channel, guild] = await Promise.all([
bot.cache.channels.get(bot.transformers.snowflake(payload.id)),
bot.cache.guilds.get(bot.transformers.snowflake(payload.guild_id)),
]);
if (!channel) return;
if ([DiscordChannelTypes.GuildVoice, DiscordChannelTypes.GuildStageVoice].includes(channel.type)) {
channel.voiceStates?.forEach((vs, key) => {
if (guild && [DiscordChannelTypes.GuildVoice, DiscordChannelTypes.GuildStageVoice].includes(channel.type)) {
guild.voiceStates?.forEach((vs, key) => {
if (vs.channelId !== channel.id) return;
// Since this channel was deleted all voice states for this channel should be deleted
channel.voiceStates?.delete(key);
guild.voiceStates?.delete(key);
bot.events.voiceChannelLeave(bot, vs, channel);
bot.events.voiceChannelLeave(bot, vs, guild, channel);
});
} else if (
[
@@ -27,7 +31,7 @@ export async function handleChannelDelete(bot: Bot, data: SnakeCasedPropertiesDe
DiscordChannelTypes.GuildNews,
].includes(payload.type)
) {
await bot.cache.channels.forEach("DELETE_MESSAGES_FROM_CHANNEL", {
await bot.cache.execute("DELETE_MESSAGES_FROM_CHANNEL", {
channelId: bot.transformers.snowflake(payload.id),
});
}
@@ -6,5 +6,5 @@ import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildIntegrationsUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildIntegrationsUpdate>;
bot.events.integrationsUpdate(bot, { guildId: bot.transformers.snowflake(payload.guild_id) });
bot.events.integrationUpdate(bot, { guildId: bot.transformers.snowflake(payload.guild_id) });
}
+11 -8
View File
@@ -1,14 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { Bot } from "../../bot.ts";
import type { PresenceUpdate } from "../../types/activity/presence_update.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handlePresenceUpdate(data: DiscordGatewayPayload) {
const payload = data.d as PresenceUpdate;
export async function handlePresenceUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<PresenceUpdate>;
const oldPresence = await cacheHandlers.get("presences", snowflakeToBigint(payload.user.id));
await cacheHandlers.set("presences", snowflakeToBigint(payload.user.id), payload);
const id = bot.transformers.snowflake(payload.user.id);
eventHandlers.presenceUpdate?.(payload, oldPresence);
const oldPresence = await bot.cache.presence.get(id);
const presence = bot.transformers.presence(bot, payload);
await bot.cache.presence.set(id, presence)
bot.events.presenceUpdate(bot, presence, oldPresence);
}
+15 -62
View File
@@ -1,67 +1,20 @@
import { eventHandlers, setApplicationId, setBotId } from "../../bot.ts";
import { cache } from "../../cache.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { Ready } from "../../types/gateway/ready.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { DiscordenoShard, ws } from "../../ws/ws.ts";
import type { DiscordReady } from "../../types/gateway/ready.ts";
export function handleReady(data: DiscordGatewayPayload, shardId: number) {
export function handleReady(bot: Bot, data: DiscordGatewayPayload, shardId: number) {
const payload = data.d as DiscordReady;
// Triggered on each shard
eventHandlers.shardReady?.(shardId);
bot.events.ready(bot, {
shardId,
v: payload.v,
user: bot.transformers.user(bot, payload.user),
guilds: payload.guilds.map((p) => bot.transformers.snowflake(p.id)),
sessionId: payload.session_id,
shard: payload.shard,
applicationId: bot.transformers.snowflake(payload.application.id),
}, payload);
// The bot has already started, the last shard is resumed, however.
if (cache.isReady) return;
const shard = ws.shards.get(shardId);
if (!shard) return;
const payload = data.d as Ready;
setBotId(payload.user.id);
setApplicationId(payload.application.id);
// 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) => snowflakeToBigint(g.id)));
// Falied to load check
shard.failedToLoadTimeoutId = setTimeout(() => {
eventHandlers.shardFailedToLoad?.(shard.id, shard.unavailableGuildIds);
// Force execute the loaded function to prevent infinite loop
return loaded(shard);
}, 5000);
}
export function guildAvailable(shard: DiscordenoShard, guildId: bigint) {
if (!shard.failedToLoadTimeoutId) return;
clearTimeout(shard.failedToLoadTimeoutId);
shard.unavailableGuildIds.delete(guildId);
if (!shard.unavailableGuildIds.size) return loaded(shard);
shard.failedToLoadTimeoutId = setTimeout(() => {
eventHandlers.shardFailedToLoad?.(shard.id, shard.unavailableGuildIds);
// Force execute the loaded function to prevent infinite loop
return loaded(shard);
}, 5000);
}
function loaded(shard: DiscordenoShard) {
shard.ready = true;
// If it is not the last shard we can't go full ready
if (shard.id !== ws.lastShardId) return;
// Still some shards are loading so wait another 2 seconds for them
if (ws.shards.some((shard) => !shard.ready)) {
setTimeout(() => {
eventHandlers.debug?.("loop", `3. Running setTimeout in READY file.`);
loaded(shard);
}, 2000);
return;
}
cache.isReady = true;
eventHandlers.ready?.();
if (!bot.id) bot.id = bot.transformers.snowflake(payload.user.id);
if (!bot.applicationId) bot.applicationId = bot.transformers.snowflake(payload.application.id);
}
+14 -3
View File
@@ -1,7 +1,18 @@
import { eventHandlers } from "../../bot.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { TypingStart } from "../../types/misc/typing_start.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export function handleTypingStart(data: DiscordGatewayPayload) {
eventHandlers.typingStart?.(data.d as TypingStart);
export function handleTypingStart(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<TypingStart>;
const guildId = payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined;
bot.events.typingStart(bot, {
guildId,
channelId: bot.transformers.snowflake(payload.channel_id),
userId: bot.transformers.snowflake(payload.user_id),
timestamp: payload.timestamp,
member: payload.member && guildId ? bot.transformers.member(bot, payload.member, guildId) : undefined,
});
}
+7 -30
View File
@@ -1,35 +1,12 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { memberToggles } from "../../structures/member.ts";
import { Bot } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { User } from "../../types/users/user.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { iconHashToBigInt } from "../../util/hash.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleUserUpdate(data: DiscordGatewayPayload) {
const userData = data.d as User;
export async function handleUserUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<User>;
const user = bot.transformers.user(bot, payload);
await bot.cache.users.set(user.id, user);
const member = await cacheHandlers.get("members", snowflakeToBigint(userData.id));
if (!member) return;
// Update username
member.username = userData.username;
// Update discriminator
member.discriminator = Number(userData.discriminator);
// Check if a avatar is available
const hash = userData.avatar ? iconHashToBigInt(userData.avatar) : undefined;
// Update the avatar
member.avatar = hash?.bigint || 0n;
// Update the animated status if its animated
if (hash?.animated) member.bitfield |= memberToggles.animatedAvatar;
else member.bitfield &= ~memberToggles.animatedAvatar;
member.flags = userData.flags;
member.premiumType = userData.premiumType;
member.publicFlags = userData.publicFlags;
await cacheHandlers.set("members", snowflakeToBigint(userData.id), member);
eventHandlers.botUpdate?.(userData);
bot.events.botUpdate(bot, user);
}
+12 -14
View File
@@ -1,21 +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 { GuildRoleCreate } from "../../types/guilds/guild_role_create.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildRoleCreate(data: DiscordGatewayPayload) {
const payload = data.d as GuildRoleCreate;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
export async function handleGuildRoleCreate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildRoleCreate>;
const guildId = bot.transformers.snowflake(payload.guild_id);
const guild = await bot.cache.guilds.get(guildId);
if (!guild) return;
const role = await structures.createDiscordenoRole({
...payload,
guildId: guild.id,
});
guild.roles = guild.roles.set(snowflakeToBigint(payload.role.id), role);
await cacheHandlers.set("guilds", guild.id, guild);
const role = bot.transformers.role(bot, { role: payload.role, guildId });
eventHandlers.roleCreate?.(guild, role);
guild.roles = guild.roles.set(role.id, role);
await bot.cache.guilds.set(guild.id, guild);
bot.events.roleCreate(bot, guild, role);
}
+12 -10
View File
@@ -1,21 +1,23 @@
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 { GuildRoleDelete } from "../../types/guilds/guild_role_delete.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildRoleDelete(data: DiscordGatewayPayload) {
const payload = data.d as GuildRoleDelete;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
export async function handleGuildRoleDelete(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildRoleDelete>;
const guildId = bot.transformers.snowflake(payload.guild_id);
const guild = await bot.cache.guilds.get(guildId);
if (!guild) return;
const roleId = snowflakeToBigint(payload.roleId);
const roleId = bot.transformers.snowflake(payload.role_id);
const cachedRole = guild.roles.get(roleId)!;
const cachedRole = guild.roles.get(roleId);
guild.roles.delete(roleId);
if (cachedRole) eventHandlers.roleDelete?.(guild, cachedRole);
await bot.cache.guilds.set(guild.id, guild);
if (cachedRole) bot.events.roleDelete(bot, guild, cachedRole);
// For bots without GUILD_MEMBERS member.roles is never updated breaking permissions checking.
await cacheHandlers.forEach("DELETE_ROLE_FROM_MEMBER", { guildId: guild.id, roleId });
await bot.cache.execute("DELETE_ROLE_FROM_MEMBER", { guildId: guild.id, roleId });
}
+10 -17
View File
@@ -1,24 +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 { GuildRoleUpdate } from "../../types/guilds/guild_role_update.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleGuildRoleUpdate(data: DiscordGatewayPayload) {
const payload = data.d as GuildRoleUpdate;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
export async function handleGuildRoleUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<GuildRoleUpdate>;
const guildId = bot.transformers.snowflake(payload.guild_id);
const guild = await bot.cache.guilds.get(guildId);
if (!guild) return;
const cachedRole = guild.roles.get(snowflakeToBigint(payload.role.id));
if (!cachedRole) return;
const role = bot.transformers.role(bot, { role: payload.role, guildId });
guild.roles = guild.roles.set(role.id, role);
await bot.cache.guilds.set(guild.id, guild);
const role = await structures.createDiscordenoRole({
...payload,
guildId: guild.id,
});
guild.roles.set(snowflakeToBigint(payload.role.id), role);
await cacheHandlers.set("guilds", guild.id, guild);
eventHandlers.roleUpdate?.(guild, role, cachedRole);
bot.events.roleUpdate(bot, guild, role, guild.roles.get(role.id));
}
+9 -9
View File
@@ -1,14 +1,14 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { Bot } from "../../bot.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { VoiceServerUpdate } from "../../types/voice/voice_server_update.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
export async function handleVoiceServerUpdate(data: DiscordGatewayPayload) {
const payload = data.d as VoiceServerUpdate;
export async function handleVoiceServerUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<VoiceServerUpdate>;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
if (!guild) return;
eventHandlers.voiceServerUpdate?.(payload, guild);
bot.events.voiceServerUpdate(bot, {
token: payload.token,
guildId: bot.transformers.snowflake(payload.guild_id),
endpoint: payload.endpoint ?? undefined,
});
}
+15 -42
View File
@@ -1,50 +1,23 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { structures } from "../../structures/mod.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { VoiceState } from "../../types/voice/voice_state.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { Bot } from "../../bot.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function handleVoiceStateUpdate(data: DiscordGatewayPayload) {
const payload = data.d as VoiceState;
if (!payload.guildId) return;
export async function handleVoiceStateUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<VoiceState>;
if (!payload.guild_id) return;
const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId));
if (!guild) return;
const guildId = bot.transformers.snowflake(payload.guild_id);
const voiceState = bot.transformers.voiceState(bot, { voiceState: payload, guildId });
const member = payload.member
? await structures.createDiscordenoMember(payload.member, guild.id)
: await cacheHandlers.get("members", snowflakeToBigint(payload.userId));
if (!member) return;
if (!payload.member?.joinedAt) return eventHandlers.lurkerVoiceStateUpdate?.(member, payload);
// No cached state before so lets make one for em
const cachedState = guild.voiceStates.get(snowflakeToBigint(payload.userId));
guild.voiceStates.set(
snowflakeToBigint(payload.userId),
await structures.createDiscordenoVoiceState(guild.id, payload)
);
await cacheHandlers.set("guilds", guild.id, guild);
if (cachedState?.channelId !== (payload.channelId ? snowflakeToBigint(payload.channelId) : null)) {
// Either joined or moved channels
if (payload.channelId) {
if (cachedState?.channelId) {
// Was in a channel before
eventHandlers.voiceChannelSwitch?.(member, snowflakeToBigint(payload.channelId), cachedState.channelId);
} else {
// Was not in a channel before so user just joined
eventHandlers.voiceChannelJoin?.(member, snowflakeToBigint(payload.channelId));
}
} // Left the channel
else if (cachedState?.channelId) {
guild.voiceStates.delete(snowflakeToBigint(payload.userId));
eventHandlers.voiceChannelLeave?.(member, cachedState.channelId);
}
const guild = await bot.cache.guilds.get(guildId);
if (guild) {
guild.voiceStates.set(voiceState.userId, voiceState);
await bot.cache.guilds.set(guild.id, guild);
}
eventHandlers.voiceStateUpdate?.(member, payload);
const member = payload.member ? bot.transformers.member(bot, payload.member, guildId) : undefined;
const user = payload.member ? bot.transformers.user(bot, payload.member.user) : undefined;
bot.events.voiceStateUpdate(bot, voiceState, { guild, member, user });
}
+8 -5
View File
@@ -1,9 +1,12 @@
import { eventHandlers } from "../../bot.ts";
import type { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import type { WebhookUpdate } from "../../types/webhooks/webhooks_update.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { Bot } from "../../bot.ts";
import { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export function handleWebhooksUpdate(data: DiscordGatewayPayload) {
const options = data.d as WebhookUpdate;
eventHandlers.webhooksUpdate?.(snowflakeToBigint(options.channelId), snowflakeToBigint(options.guildId));
export function handleWebhooksUpdate(bot: Bot, data: DiscordGatewayPayload) {
const payload = data.d as SnakeCasedPropertiesDeep<WebhookUpdate>;
bot.events.webhooksUpdate(bot, {
channelId: bot.transformers.snowflake(payload.channel_id),
guildId: bot.transformers.snowflake(payload.guild_id),
});
}
+82
View File
@@ -0,0 +1,82 @@
import { Bot } from "../bot.ts";
import { Activity } from "../types/activity/activity.ts";
import { DiscordActivityTypes } from "../types/activity/activity_types.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
import { DiscordenoEmoji } from "./emoji.ts";
export function transformActivity(bot: Bot, payload: SnakeCasedPropertiesDeep<Activity>): DiscordenoActivity {
return {
name: payload.name,
type: payload.type,
url: payload.url ?? undefined,
createdAt: payload.created_at,
startedAt: payload.timestamps?.start,
endedAt: payload.timestamps?.end,
applicationId: payload.application_id ? bot.transformers.snowflake(payload.application_id) : undefined,
details: payload.details ?? undefined,
state: payload.state ?? undefined,
emoji: payload.emoji ? bot.transformers.emoji(bot, payload.emoji) : undefined,
partyId: payload.party?.id,
partyCurrentSize: payload.party?.size?.[0],
partyMaxSize: payload.party?.size?.[1],
largeImage: payload.assets?.large_image,
largeText: payload.assets?.large_text,
smallImage: payload.assets?.small_image,
smallText: payload.assets?.small_text,
join: payload.secrets?.join,
spectate: payload.secrets?.spectate,
match: payload.secrets?.match,
instance: payload.instance,
flags: payload.flags,
buttonLabels: payload.buttons?.map((button) => button.label),
};
}
export interface DiscordenoActivity {
/** The activity's name */
name: string;
/** Activity type */
type: DiscordActivityTypes;
/** Stream url, is validated when type is 1 */
url?: string;
/** Unix timestamp of when the activity was added to the user's session */
createdAt: number;
/** Unix time (in milliseconds) of when the activity started */
startedAt?: number;
/** Unix time (in milliseconds) of when the activity ends */
endedAt?: number;
/** Application id for the game */
applicationId?: bigint;
/** What the player is currently doing */
details?: string;
/** The user's current party status */
state?: string;
/** The emoji used for a custom status */
emoji?: DiscordenoEmoji;
/** the id of the party */
partyId?: string;
/** The current size of the party if one exists */
partyCurrentSize?: number;
/** The max size of the party if one exists */
partyMaxSize?: number;
/** The id for a large asset of the activity, usually a snowflake */
largeImage?: string;
/** Text displayed when hovering over the large image of the activity */
largeText?: string;
/** The id for a small asset of the activity, usually a snowflake */
smallImage?: string;
/** Text displayed when hovering over the small image of the activity */
smallText?: string;
/** The secret for joining a party */
join?: string;
/** The secret for spectating a game */
spectate?: string;
/** The secret for a specific instanced match */
match?: string;
/** Whether or not the activity is an instanced game session */
instance?: boolean;
/** Activity flags `OR`d together, describes what the payload includes */
flags?: number;
/** The custom button's labels shown in the Rich Presence */
buttonLabels?: string[];
}
+42
View File
@@ -0,0 +1,42 @@
import { Bot } from "../bot.ts";
import { PresenceUpdate } from "../types/activity/presence_update.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
import { DiscordenoActivity } from "./activity.ts";
import { DiscordenoUser } from "./member.ts";
export const statusTypes = {
online: 0,
dnd: 1,
idle: 2,
invisible: 3,
offline: 4,
} as const;
export function transformPresence(bot: Bot, payload: SnakeCasedPropertiesDeep<PresenceUpdate>): DiscordenoPresence {
return {
user: bot.transformers.user(bot, payload.user),
guildId: bot.transformers.snowflake(payload.guild_id),
status: statusTypes[payload.status],
activities: payload.activities.map((activity) => bot.transformers.activity(bot, activity)),
desktop: payload.client_status.desktop,
mobile: payload.client_status.mobile,
web: payload.client_status.web,
};
}
export interface DiscordenoPresence {
/** The user presence is being updated for */
user: DiscordenoUser;
/** id of the guild */
guildId: bigint;
/** Either online: 0, dnd: 1, idle: 2, invisible: 3, offline: 4 */
status: 0 | 1 | 2 | 3 | 4;
/** User's current activities */
activities: DiscordenoActivity[];
/** The user's status set for an active desktop (Windows, Linux, Mac) application session */
desktop?: string;
/** The user's status set for an active mobile (iOS, Android) application session */
mobile?: string;
/** The user's status set for an active web (browser, bot account) application session */
web?: string;
}
+4 -2
View File
@@ -16,7 +16,7 @@ export function transformVoiceState(
(payload.voiceState.self_video ? 32n : 0n) |
(payload.voiceState.suppress ? 64n : 0n),
requestToSpeakTimestamp: payload.voiceState.request_to_speak_timestamp,
requestToSpeakTimestamp: payload.voiceState.request_to_speak_timestamp ? Date.parse(payload.voiceState.request_to_speak_timestamp) : undefined,
sessionId: payload.voiceState.session_id,
channelId: payload.voiceState.channel_id ? bot.transformers.snowflake(payload.voiceState.channel_id) : undefined,
@@ -27,7 +27,7 @@ export function transformVoiceState(
};
}
export interface DiscordenoVoiceState extends Omit<VoiceState, "channelId" | "guildId" | "userId" | "member"> {
export interface DiscordenoVoiceState {
/** The guild id */
guildId: bigint;
/** The channel id this user is connected to */
@@ -36,4 +36,6 @@ export interface DiscordenoVoiceState extends Omit<VoiceState, "channelId" | "gu
userId: bigint;
/** Holds all the boolean toggles. */
bitfield: bigint;
/** The time at which the user requested to speak */
requestToSpeakTimestamp?: number;
}
+1
View File
@@ -1,2 +1,3 @@
/** https://discord.com/developers/docs/topics/gateway#update-status-status-types */
export type DiscordStatusTypes = "online" | "dnd" | "idle" | "invisible" | "offline";