making it testable

This commit is contained in:
Skillz4Killz
2021-10-25 00:11:07 +00:00
committed by GitHub
parent 5b4b10ae45
commit 231554d091
33 changed files with 459 additions and 781 deletions
+1
View File
@@ -1,6 +1,7 @@
{
"deno.enable": true,
"deno.lint": false,
"deno.unstable": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
+1 -1
View File
@@ -3,7 +3,7 @@ export * from "./src/cache.ts";
export * from "./src/handlers/mod.ts";
export * from "./src/helpers/mod.ts";
export * from "./src/rest/mod.ts";
export * from "./src/structures/mod.ts";
export * from "./src/transformers/mod.ts";
export * from "./src/types/mod.ts";
export * from "./src/util/mod.ts";
export * from "./src/ws/mod.ts";
+25 -26
View File
@@ -296,7 +296,7 @@ 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) {
export function createBot(options: CreateBotOptions) {
return {
id: options.botId,
applicationId: options.applicationId || options.botId,
@@ -430,13 +430,6 @@ export async function createBot(options: CreateBotOptions) {
};
}
const bot = await createBot({
token: "",
botId: 0n,
events: createEventHandlers({}),
intents: [],
});
export function createEventHandlers(events: Partial<EventHandlers>): EventHandlers {
function ignore() {}
@@ -543,14 +536,27 @@ export function createRestManager(options: CreateRestManagerOptions) {
export async function startBot(bot: Bot) {
// SETUP
bot.utils = createUtils({});
bot.transformers = createTransformers(bot.transformers);
bot.helpers = createHelpers(bot.helpers);
bot.transformers = createTransformers(bot.transformers || {});
bot.helpers = createHelpers(bot.helpers || {});
// START REST
bot.rest = createRestManager({ token: bot.token });
// START WS
bot.gateway = createGatewayManager({});
bot.gateway = createGatewayManager({
handleDiscordPayload:
// bot.handleDiscordPayload ||
async function (_, data: DiscordGatewayPayload, shardId: number) {
// TRIGGER RAW EVENT
bot.events.raw(bot as Bot, data, shardId);
if (!data.t) return;
// RUN DISPATCH CHECK
await bot.events.dispatchRequirements(bot as Bot, data, shardId);
bot.handlers[data.t as GatewayDispatchEventNames]?.(bot as Bot, data, shardId);
},
});
if (!bot.botGatewayData) bot.botGatewayData = await bot.helpers.getGatewayBot(bot);
}
@@ -612,7 +618,9 @@ export interface HelperUtils {
validateComponents: typeof validateComponents;
}
export function createGatewayManager(options: Partial<GatewayManager>): GatewayManager {
export function createGatewayManager(
options: Partial<GatewayManager> & Pick<GatewayManager, "handleDiscordPayload">
): GatewayManager {
return {
cache: {
guildIds: new Set(),
@@ -661,18 +669,7 @@ export function createGatewayManager(options: Partial<GatewayManager>): GatewayM
closeWS,
sendShardMessage,
resume,
handleDiscordPayload:
options.handleDiscordPayload ||
async function (_, data: DiscordGatewayPayload, shardId: number) {
// TRIGGER RAW EVENT
bot.events.raw(bot as Bot, data, shardId);
if (!data.t) return;
// RUN DISPATCH CHECK
await bot.events.dispatchRequirements(bot as Bot, data, shardId);
bot.handlers[data.t as GatewayDispatchEventNames]?.(bot as Bot, data, shardId);
},
handleDiscordPayload: options.handleDiscordPayload,
};
}
@@ -693,7 +690,7 @@ export interface CreateBotOptions {
export type UnPromise<T extends Promise<unknown>> = T extends Promise<infer K> ? K : never;
export type CreatedBot = UnPromise<ReturnType<typeof createBot>>;
export type CreatedBot = ReturnType<typeof createBot>;
export type Bot = CreatedBot & {
utils: HelperUtils;
@@ -1369,7 +1366,9 @@ export interface BotGatewayHandlerOptions {
INTEGRATION_DELETE: typeof handleIntegrationDelete;
}
export function createBotGatewayHandlers(options: Partial<BotGatewayHandlerOptions>): Record<GatewayDispatchEventNames | "GUILD_LOADED_DD", (bot: Bot, data: GatewayPayload, shardId: number) => any> {
export function createBotGatewayHandlers(
options: Partial<BotGatewayHandlerOptions>
): Record<GatewayDispatchEventNames | "GUILD_LOADED_DD", (bot: Bot, data: GatewayPayload, shardId: number) => any> {
return {
// misc
READY: options.READY ?? handleReady,
+8 -8
View File
@@ -1,17 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { eventHandlers } from "../../bot.ts";
// import { cacheHandlers } from "../../cache.ts";
import { Channel } from "../../types/channels/channel.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
export async function handleThreadDelete(data: DiscordGatewayPayload) {
const payload = data.d as Channel;
// const payload = data.d as Channel;
const cachedChannel = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
if (!cachedChannel) return;
// const cachedChannel = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
// if (!cachedChannel) return;
await cacheHandlers.delete("threads", snowflakeToBigint(payload.id));
await cacheHandlers.forEach("DELETE_MESSAGES_FROM_CHANNEL", { channelId: snowflakeToBigint(payload.id) });
// await cacheHandlers.delete("threads", snowflakeToBigint(payload.id));
// await cacheHandlers.forEach("DELETE_MESSAGES_FROM_CHANNEL", { channelId: snowflakeToBigint(payload.id) });
eventHandlers.threadDelete?.(cachedChannel);
// eventHandlers.threadDelete?.(cachedChannel);
}
+17 -17
View File
@@ -1,27 +1,27 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { eventHandlers } from "../../bot.ts";
// import { cacheHandlers } from "../../cache.ts";
import { ThreadListSync } from "../../types/channels/threads/thread_list_sync.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { channelToThread } from "../../util/transformers/channel_to_thread.ts";
// import { channelToThread } from "../../util/transformers/channel_to_thread.ts";
import { Collection } from "../../util/collection.ts";
import { threadMemberModified } from "../../util/transformers/thread_member_modified.ts";
// import { threadMemberModified } from "../../util/transformers/thread_member_modified.ts";
export async function handleThreadListSync(data: DiscordGatewayPayload) {
const payload = data.d as ThreadListSync;
// const payload = data.d as ThreadListSync;
const threads = await Promise.all(
payload.threads.map(async (thread) => {
const threadData = channelToThread(thread);
await cacheHandlers.set("threads", threadData.id, threadData);
// const threads = await Promise.all(
// payload.threads.map(async (thread) => {
// const threadData = channelToThread(thread);
// await cacheHandlers.set("threads", threadData.id, threadData);
return threadData;
})
);
// return threadData;
// })
// );
eventHandlers.threadListSync?.(
new Collection(threads.map((t) => [t.id, t])),
payload.members.map((member) => threadMemberModified(member)),
snowflakeToBigint(payload.guildId)
);
// eventHandlers.threadListSync?.(
// new Collection(threads.map((t) => [t.id, t])),
// payload.members.map((member) => threadMemberModified(member)),
// snowflakeToBigint(payload.guildId)
// );
}
@@ -1,17 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { eventHandlers } from "../../bot.ts";
// import { cacheHandlers } from "../../cache.ts";
import { ThreadMembersUpdate } from "../../types/channels/threads/thread_members_update.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { threadMembersUpdateModified } from "../../util/transformers/thread_members_update_modified.ts";
// import { threadMembersUpdateModified } from "../../util/transformers/thread_members_update_modified.ts";
export async function handleThreadMembersUpdate(data: DiscordGatewayPayload) {
const payload = data.d as ThreadMembersUpdate;
const thread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
if (!thread) return;
// const payload = data.d as ThreadMembersUpdate;
// const thread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
// if (!thread) return;
thread.memberCount = payload.memberCount;
await cacheHandlers.set("threads", thread.id, thread);
// thread.memberCount = payload.memberCount;
// await cacheHandlers.set("threads", thread.id, thread);
eventHandlers.threadMembersUpdate?.(threadMembersUpdateModified(payload));
// eventHandlers.threadMembersUpdate?.(threadMembersUpdateModified(payload));
}
+15 -15
View File
@@ -1,24 +1,24 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { eventHandlers } from "../../bot.ts";
// import { cacheHandlers } from "../../cache.ts";
import { ThreadMember } from "../../types/channels/threads/thread_member.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
export async function handleThreadMemberUpdate(data: DiscordGatewayPayload) {
const payload = data.d as ThreadMember;
// The id field is omitted from the thread member dispatched within the GUILD_CREATE gateway event.
const thread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id!));
if (!thread) return;
// const payload = data.d as ThreadMember;
// // The id field is omitted from the thread member dispatched within the GUILD_CREATE gateway event.
// const thread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id!));
// if (!thread) return;
thread.botIsMember = true;
await cacheHandlers.set("threads", thread.id, thread);
// thread.botIsMember = true;
// await cacheHandlers.set("threads", thread.id, thread);
const member = {
...payload,
id: snowflakeToBigint(payload.id!),
userId: snowflakeToBigint(payload.userId!),
joinTimestamp: Date.parse(payload.joinTimestamp),
};
// const member = {
// ...payload,
// id: snowflakeToBigint(payload.id!),
// userId: snowflakeToBigint(payload.userId!),
// joinTimestamp: Date.parse(payload.joinTimestamp),
// };
eventHandlers.threadMemberUpdate?.(member, thread);
// eventHandlers.threadMemberUpdate?.(member, thread);
}
+9 -9
View File
@@ -1,17 +1,17 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { eventHandlers } from "../../bot.ts";
// import { cacheHandlers } from "../../cache.ts";
import { Channel } from "../../types/channels/channel.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { snowflakeToBigint } from "../../util/bigint.ts";
import { channelToThread } from "../../util/transformers/channel_to_thread.ts";
// import { channelToThread } from "../../util/transformers/channel_to_thread.ts";
export async function handleThreadUpdate(data: DiscordGatewayPayload) {
const payload = data.d as Channel;
const oldThread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
if (!oldThread) return;
// const payload = data.d as Channel;
// const oldThread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id));
// if (!oldThread) return;
const thread = channelToThread(payload);
await cacheHandlers.set("threads", thread.id, thread);
// const thread = channelToThread(payload);
// await cacheHandlers.set("threads", thread.id, thread);
eventHandlers.threadUpdate?.(thread, oldThread);
// eventHandlers.threadUpdate?.(thread, oldThread);
}
+10 -10
View File
@@ -1,6 +1,6 @@
import type { ModifyThread } from "../../../types/channels/threads/modify_thread.ts";
import type { Bot } from "../../../bot.ts";
import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
// import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
/** Update a thread's settings. Requires the `MANAGE_CHANNELS` permission for the guild. */
export async function editThread(bot: Bot, threadId: bigint, options: ModifyThread, reason?: string) {
@@ -8,14 +8,14 @@ export async function editThread(bot: Bot, threadId: bigint, options: ModifyThre
// TODO: permission checking
const result = await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.CHANNEL_BASE(threadId), {
name: options.name,
archived: options.archived,
auto_archive_duration: options.autoArchiveDuration,
locked: options.locked,
rate_limit_per_user: options.rateLimitPerUser,
reason,
});
// const result = await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.CHANNEL_BASE(threadId), {
// name: options.name,
// archived: options.archived,
// auto_archive_duration: options.autoArchiveDuration,
// locked: options.locked,
// rate_limit_per_user: options.rateLimitPerUser,
// reason,
// });
return channelToThread(result);
// return channelToThread(result);
}
@@ -1,34 +1,34 @@
import type { Bot } from "../../../bot.ts";
import type { ListActiveThreads } from "../../../types/channels/threads/list_active_threads.ts";
import { Collection } from "../../../util/collection.ts";
import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
// import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
/** Returns all active threads in the channel, including public and private threads. Threads are ordered by their id, in descending order. Requires the VIEW_CHANNEL permission. */
export async function getActiveThreads(bot: Bot, channelId: bigint) {
await bot.utils.requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL"]);
// await bot.utils.requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL"]);
// TODO: pagination
const result = (await bot.rest.runMethod(
bot.rest,
"get",
bot.constants.endpoints.THREAD_ACTIVE(channelId)
)) as ListActiveThreads;
// // TODO: pagination
// const result = (await bot.rest.runMethod(
// bot.rest,
// "get",
// bot.constants.endpoints.THREAD_ACTIVE(channelId)
// )) as ListActiveThreads;
const threads = new Collection(
result.threads.map((t) => {
const ddThread = channelToThread(t);
return [ddThread.id, ddThread];
})
);
// const threads = new Collection(
// result.threads.map((t) => {
// const ddThread = channelToThread(t);
// return [ddThread.id, ddThread];
// })
// );
for (const member of result.members) {
const thread = threads.get(bot.transformers.snowflake(member.id!));
thread?.members.set(bot.transformers.snowflake(member.userId!), {
userId: bot.transformers.snowflake(member.userId!),
flags: member.flags,
joinTimestamp: Date.parse(member.joinTimestamp),
});
}
return threads;
// for (const member of result.members) {
// const thread = threads.get(bot.transformers.snowflake(member.id!));
// thread?.members.set(bot.transformers.snowflake(member.userId!), {
// userId: bot.transformers.snowflake(member.userId!),
// flags: member.flags,
// joinTimestamp: Date.parse(member.joinTimestamp),
// });
// }
// return threads;
}
@@ -3,7 +3,7 @@ import { ListPublicArchivedThreads } from "../../../types/channels/threads/list_
import { PermissionStrings } from "../../../types/permissions/permission_strings.ts";
import { Collection } from "../../../util/collection.ts";
import type { Bot } from "../../../bot.ts";
import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
// import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
/** Get the archived threads for this channel, defaults to public */
export async function getArchivedThreads(
@@ -13,45 +13,45 @@ export async function getArchivedThreads(
type?: "public" | "private" | "privateJoinedThreads";
}
) {
const permissions = new Set<PermissionStrings>(["READ_MESSAGE_HISTORY"]);
if (options?.type === "private") permissions.add("MANAGE_THREADS");
// const permissions = new Set<PermissionStrings>(["READ_MESSAGE_HISTORY"]);
// if (options?.type === "private") permissions.add("MANAGE_THREADS");
await bot.utils.requireBotChannelPermissions(bot, channelId, [...permissions]);
// await bot.utils.requireBotChannelPermissions(bot, channelId, [...permissions]);
// TODO: pagination
// // TODO: pagination
const result = (await bot.rest.runMethod(
bot.rest,
"get",
options?.type === "privateJoinedThreads"
? bot.constants.endpoints.THREAD_ARCHIVED_PRIVATE_JOINED(channelId)
: options?.type === "private"
? bot.constants.endpoints.THREAD_ARCHIVED_PRIVATE(channelId)
: bot.constants.endpoints.THREAD_ARCHIVED_PUBLIC(channelId),
options
? {
before: options.before,
limit: options.limit,
type: options.type,
}
: {}
)) as ListActiveThreads;
const threads = new Collection(
result.threads.map((t) => {
const ddThread = channelToThread(t);
return [ddThread.id, ddThread];
})
);
for (const member of result.members) {
const thread = threads.get(bot.transformers.snowflake(member.id!));
thread?.members.set(bot.transformers.snowflake(member.userId!), {
userId: bot.transformers.snowflake(member.userId!),
flags: member.flags,
joinTimestamp: Date.parse(member.joinTimestamp),
});
}
return threads;
// const result = (await bot.rest.runMethod(
// bot.rest,
// "get",
// options?.type === "privateJoinedThreads"
// ? bot.constants.endpoints.THREAD_ARCHIVED_PRIVATE_JOINED(channelId)
// : options?.type === "private"
// ? bot.constants.endpoints.THREAD_ARCHIVED_PRIVATE(channelId)
// : bot.constants.endpoints.THREAD_ARCHIVED_PUBLIC(channelId),
// options
// ? {
// before: options.before,
// limit: options.limit,
// type: options.type,
// }
// : {}
// )) as ListActiveThreads;
// const threads = new Collection(
// result.threads.map((t) => {
// const ddThread = channelToThread(t);
// return [ddThread.id, ddThread];
// })
// );
// for (const member of result.members) {
// const thread = threads.get(bot.transformers.snowflake(member.id!));
// thread?.members.set(bot.transformers.snowflake(member.userId!), {
// userId: bot.transformers.snowflake(member.userId!),
// flags: member.flags,
// joinTimestamp: Date.parse(member.joinTimestamp),
// });
// }
// return threads;
}
@@ -2,31 +2,32 @@ import type { Bot } from "../../../bot.ts";
import { ThreadMember } from "../../../types/channels/threads/thread_member.ts";
import { DiscordGatewayIntents } from "../../../types/gateway/gateway_intents.ts";
import { Collection } from "../../../util/collection.ts";
import { threadMemberModified } from "../../../util/transformers/thread_member_modified.ts";
// import { threadMemberModified } from "../../../util/transformers/thread_member_modified.ts";
/** Returns thread members objects that are members of the thread. */
export async function getThreadMembers(bot: Bot, threadId: bigint) {
// Check if intents is not 0 as proxy ws won't set intents in other instances
if (
bot.gateway.identifyPayload.intents &&
!(bot.gateway.identifyPayload.intents & DiscordGatewayIntents.GuildMembers)
) {
throw new Error(bot.constants.Errors.MISSING_INTENT_GUILD_MEMBERS);
}
const thread = await bot.cache.threads.get(threadId);
if (thread?.isPrivate) {
const channel = await bot.cache.channels.get(thread.parentId);
if (channel && !(await bot.utils.botHasChannelPermissions(bot, channel, ["MANAGE_THREADS"])) && !thread.botIsMember)
throw new Error(bot.constants.Errors.CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD);
}
const result = await bot.rest.runMethod<ThreadMember[]>(
bot.rest,
"get",
bot.constants.endpoints.THREAD_MEMBERS(threadId)
);
const members = result.map((member) => threadMemberModified(member));
return new Collection(members.map((member) => [member.id, member]));
// // Check if intents is not 0 as proxy ws won't set intents in other instances
// if (
// bot.gateway.identifyPayload.intents &&
// !(bot.gateway.identifyPayload.intents & DiscordGatewayIntents.GuildMembers)
// ) {
// throw new Error(bot.constants.Errors.MISSING_INTENT_GUILD_MEMBERS);
// }
// const thread = await bot.cache.threads.get(threadId);
// if (thread?.isPrivate) {
// const channel = await bot.cache.channels.get(thread.parentId);
// if (channel && !(await bot.utils.botHasChannelPermissions(bot, channel, ["MANAGE_THREADS"])) && !thread.botIsMember)
// throw new Error(bot.constants.Errors.CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD);
// }
// const result = await bot.rest.runMethod<ThreadMember[]>(
// bot.rest,
// "get",
// bot.constants.endpoints.THREAD_MEMBERS(threadId)
// );
// const members = result.map((member) => threadMemberModified(member));
// return new Collection(members.map((member) => [member.id, member]));
}
@@ -1,23 +1,22 @@
import type { Channel } from "../../../types/channels/channel.ts";
import type { StartThread } from "../../../types/channels/threads/start_thread.ts";
import type { Bot } from "../../../bot.ts";
import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
/** Creates a new private thread. Returns a thread channel. */
export async function startPrivateThread(bot: Bot, channelId: bigint, options: StartThread) {
const channel = await bot.cache.channels.get(channelId);
if (channel) {
if (!channel.isGuildTextBasedChannel) throw new Error(bot.constants.Errors.INVALID_THREAD_PARENT_CHANNEL_TYPE);
// const channel = await bot.cache.channels.get(channelId);
// if (channel) {
// if (!channel.isGuildTextBasedChannel) throw new Error(bot.constants.Errors.INVALID_THREAD_PARENT_CHANNEL_TYPE);
if (channel.isNewsChannel) throw new Error(bot.constants.Errors.GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS);
// if (channel.isNewsChannel) throw new Error(bot.constants.Errors.GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS);
await bot.utils.requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES", "USE_PRIVATE_THREADS"]);
}
return channelToThread(
await bot.rest.runMethod<Channel>(bot.rest, "post", bot.constants.endpoints.THREAD_START_PRIVATE(channelId), {
name: options.name,
auto_archive_duration: options.autoArchiveDuration,
})
);
// await bot.utils.requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES", "USE_PRIVATE_THREADS"]);
// }
// return channelToThread(
// await bot.rest.runMethod<Channel>(bot.rest, "post", bot.constants.endpoints.THREAD_START_PRIVATE(channelId), {
// name: options.name,
// auto_archive_duration: options.autoArchiveDuration,
// })
// );
}
+18 -19
View File
@@ -1,28 +1,27 @@
import type { Channel } from "../../../types/channels/channel.ts";
import type { StartThread } from "../../../types/channels/threads/start_thread.ts";
import type { Bot } from "../../../bot.ts";
import { channelToThread } from "../../../util/transformers/channel_to_thread.ts";
/** Creates a new public thread from an existing message. Returns a thread channel. */
export async function startThread(bot: Bot, channelId: bigint, messageId: bigint, options: StartThread) {
const channel = await bot.cache.channels.get(channelId);
if (channel) {
if (!channel.isGuildTextBasedChannel) {
throw new Error(bot.constants.Errors.INVALID_THREAD_PARENT_CHANNEL_TYPE);
}
// const channel = await bot.cache.channels.get(channelId);
// if (channel) {
// if (!channel.isGuildTextBasedChannel) {
// throw new Error(bot.constants.Errors.INVALID_THREAD_PARENT_CHANNEL_TYPE);
// }
await bot.utils.requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES", "USE_PUBLIC_THREADS"]);
}
// await bot.utils.requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES", "USE_PUBLIC_THREADS"]);
// }
return channelToThread(
await bot.rest.runMethod<Channel>(
bot.rest,
"post",
bot.constants.endpoints.THREAD_START_PUBLIC(channelId, messageId),
{
name: options.name,
auto_archive_duration: options.autoArchiveDuration,
}
)
);
// return channelToThread(
// await bot.rest.runMethod<Channel>(
// bot.rest,
// "post",
// bot.constants.endpoints.THREAD_START_PUBLIC(channelId, messageId),
// {
// name: options.name,
// auto_archive_duration: options.autoArchiveDuration,
// }
// )
// );
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { cacheHandlers } from "../../cache.ts";
// import { cacheHandlers } from "../../cache.ts";
import type { Bot } from "../../bot.ts";
/** Delete a message with the channel id and message id only. */
@@ -9,7 +9,7 @@ export async function deleteMessage(
reason?: string,
delayMilliseconds = 0
) {
const message = await cacheHandlers.get("messages", messageId);
const message = await bot.cache.messages.get(messageId);
if (message && message.authorId !== bot.id) {
await bot.utils.requireBotChannelPermissions(bot, message.channelId, ["MANAGE_MESSAGES"]);
+14 -3
View File
@@ -1,4 +1,4 @@
import { cacheHandlers } from "../../cache.ts";
// import { cacheHandlers } from "../../cache.ts";
import type { EditMessage } from "../../types/messages/edit_message.ts";
import type { Message } from "../../types/messages/message.ts";
import type { PermissionStrings } from "../../types/permissions/permission_strings.ts";
@@ -7,7 +7,7 @@ import type { SnakeCasedPropertiesDeep } from "../../types/util.ts";
/** Edit the message. */
export async function editMessage(bot: Bot, channelId: bigint, messageId: bigint, content: string | EditMessage) {
const message = await cacheHandlers.get("messages", messageId);
const message = await bot.cache.messages.get(messageId);
if (message) {
if (message.authorId !== bot.id) {
@@ -34,7 +34,18 @@ export async function editMessage(bot: Bot, channelId: bigint, messageId: bigint
bot.rest,
"patch",
bot.constants.endpoints.CHANNEL_MESSAGE(channelId, messageId),
bot.utils.snakelize(content)
{
content: content.content,
embeds: content.embeds,
allowed_mentions: {
parse: content.allowedMentions?.parse,
roles: content.allowedMentions?.roles,
users: content.allowedMentions?.users,
replied_user: content.allowedMentions?.repliedUser,
},
file: content.file,
components: content.components,
}
);
return bot.transformers.message(bot, result);
+2 -2
View File
@@ -1,11 +1,11 @@
import { cacheHandlers } from "../../cache.ts";
// import { cacheHandlers } from "../../cache.ts";
import type { Message } from "../../types/messages/message.ts";
import type { Bot } from "../../bot.ts";
import type { SnakeCasedPropertiesDeep } from "../../types/util.ts";
/** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */
export async function getMessage(bot: Bot, channelId: bigint, id: bigint) {
if (await cacheHandlers.has("channels", channelId)) {
if (await bot.cache.channels.has(channelId)) {
await bot.utils.requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY"]);
}
+8 -8
View File
@@ -1,4 +1,4 @@
import { cacheHandlers } from "../../cache.ts";
// import { cacheHandlers } from "../../cache.ts";
import { DiscordChannelTypes } from "../../types/channels/channel_types.ts";
import { Errors } from "../../types/discordeno/errors.ts";
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
@@ -12,7 +12,7 @@ import type { SnakeCasedPropertiesDeep } from "../../types/util.ts";
export async function sendMessage(bot: Bot, channelId: bigint, content: string | CreateMessage) {
if (typeof content === "string") content = { content };
const channel = await cacheHandlers.get("channels", channelId);
const channel = await bot.cache.channels.get(channelId);
if (channel) {
if (
![
@@ -77,15 +77,15 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: string |
bot.rest,
"post",
bot.constants.endpoints.CHANNEL_MESSAGES(channelId),
bot.utils.snakelize({
{
content: content.content,
tts: content.tts,
embeds: content.embeds,
allowed_mentions: {
parse: content.allowedMentions.parse,
roles: content.allowedMentions.roles,
users: content.allowedMentions.users,
replied_user: content.allowedMentions.repliedUser,
parse: content.allowedMentions?.parse,
roles: content.allowedMentions?.roles,
users: content.allowedMentions?.users,
replied_user: content.allowedMentions?.repliedUser,
},
file: content.file,
// TODO: Snakelize components??
@@ -100,7 +100,7 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: string |
},
}
: {}),
})
}
);
return bot.transformers.message(bot, result);
+2 -2
View File
@@ -1,11 +1,11 @@
import type { Bot } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
// import { cacheHandlers } from "../../cache.ts";
import type { Message } from "../../types/messages/message.ts";
import type { SnakeCasedPropertiesDeep } from "../../types/util.ts";
/** Suppress all the embeds in this message */
export async function suppressEmbeds(bot: Bot, channelId: bigint, messageId: bigint) {
const message = await cacheHandlers.get("messages", messageId);
const message = await bot.cache.messages.get(messageId);
await bot.utils.requireBotChannelPermissions(bot, channelId, message ? ["MANAGE_MESSAGES"] : ["SEND_MESSAGES"]);
+86 -24
View File
@@ -1,13 +1,10 @@
import { Bot } from "../bot.ts";
import type { PresenceUpdate } from "../types/activity/presence_update.ts";
import type { Emoji } from "../types/emojis/emoji.ts";
import type { Guild } from "../types/guilds/guild.ts";
import { Collection } from "../util/collection.ts";
import { iconHashToBigInt } from "../util/hash.ts";
import { channelToThread } from "../util/transformers/channel_to_thread.ts";
import { transformChannel } from "./channel.ts";
import { DiscordenoRole, transformRole } from "./role.ts";
import { DiscordenoVoiceState, transformVoiceState } from "./voice_state.ts";
import { DiscordenoRole } from "./role.ts";
import { DiscordenoVoiceState } from "./voice_state.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
export function transformGuild(
@@ -31,11 +28,34 @@ export function transformGuild(
preferredLocale: payload.guild.preferred_locale,
premiumSubscriptionCount: payload.guild.premium_subscription_count,
premiumTier: payload.guild.premium_tier,
stageInstances: payload.guild.stage_instances,
stageInstances: payload.guild.stage_instances?.map((si) => ({
/** The id of this Stage instance */
id: bot.transformers.snowflake(si.id),
/** The guild id of the associated Stage channel */
guildId: bot.transformers.snowflake(si.guild_id),
/** The id of the associated Stage channel */
channelId: bot.transformers.snowflake(si.channel_id),
/** The topic of the Stage instance (1-120 characters) */
topic: si.topic,
/** The privacy level of the Stage instance */
privacyLevel: si.privacy_level,
/** Whether or not Stage discovery is disabled */
discoverableDisabled: si.discoverable_disabled,
})),
systemChannelFlags: payload.guild.system_channel_flags,
vanityUrlCode: payload.guild.vanity_url_code,
verificationLevel: payload.guild.verification_level,
welcomeScreen: payload.guild.welcome_screen,
welcomeScreen: payload.guild.welcome_screen
? {
description: payload.guild.welcome_screen.description ?? undefined,
welcomeChannels: payload.guild.welcome_screen.welcome_channels.map((wc) => ({
channelId: bot.transformers.snowflake(wc.channel_id),
description: wc.description,
emojiId: wc.emoji_id ? bot.transformers.snowflake(wc.emoji_id) : undefined,
emojiName: wc.emoji_name ?? undefined,
})),
}
: undefined,
discoverySplash: payload.guild.discovery_splash,
bitfield:
@@ -49,26 +69,31 @@ export function transformGuild(
shardId: payload.shardId,
icon: payload.guild.icon ? iconHashToBigInt(payload.guild.icon) : undefined,
banner: payload.guild.banner ? iconHashToBigInt(payload.guild.banner) : undefined,
splash: payload.guild.icon ? iconHashToBigInt(payload.guild.splash) : undefined,
splash: payload.guild.splash ? iconHashToBigInt(payload.guild.splash) : undefined,
// TRANSFORMED STUFF BELOW
// TODO: Handle channels/threads in a better way?
channels: (payload.guild.channels || []).map((channel) =>
transformChannel(bot, { channel, guildId: bot.transformers.snowflake(payload.guild.id) })
),
threads: (payload.guild.threads || []).map((channel) => channelToThread(channel)),
// channels: (payload.guild.channels || []).map((channel) =>
// bot.transformers.channel(bot, { channel, guildId: bot.transformers.snowflake(payload.guild.id) })
// ),
// threads: (payload.guild.threads || []).map((channel) => channelToThread(channel)),
roles: new Collection(
(payload.guild.roles || [])
.map((role) => transformRole(bot, { role, guildId: bot.transformers.snowflake(payload.guild.id) }))
.map((role) => bot.transformers.role(bot, { role, guildId: bot.transformers.snowflake(payload.guild.id) }))
.map((role) => [role.id, role])
),
presences: new Collection((payload.guild.presences || []).map((p) => [bot.transformers.snowflake(p.user!.id), p])),
// presences: new Collection(
// (payload.guild.presences || []).map((p) => [
// bot.transformers.snowflake(p.user!.id),
// bot.transformers.presence(bot, p),
// ])
// ),
emojis: new Collection((payload.guild.emojis || []).map((emoji) => [bot.transformers.snowflake(emoji.id!), emoji])),
voiceStates: new Collection(
(payload.guild.voice_states || [])
.map((vs) =>
transformVoiceState(bot, { voiceState: vs, guildId: bot.transformers.snowflake(payload.guild.id) })
bot.transformers.voiceState(bot, { voiceState: vs, guildId: bot.transformers.snowflake(payload.guild.id) })
)
.map((vs) => [vs.userId, vs])
),
@@ -113,6 +138,13 @@ export interface DiscordenoGuild
| "systemChannelId"
| "rulesChannelId"
| "publicUpdatesChannelId"
| "joinedAt"
| "icon"
| "banner"
| "splash"
| "stageInstances"
| "welcomeScreen"
| "channels"
> {
/** Guild id */
id: bigint;
@@ -132,12 +164,6 @@ export interface DiscordenoGuild
rulesChannelId?: bigint;
/** The id of the channel where admins and moderators of Community guilds receive notices from Discord */
publicUpdatesChannelId?: bigint;
/** Whether this server's icon is animated */
animatedIcon: boolean;
/** Whether this server's banner is animated. */
animatedBanner: boolean;
/** Whether this server's splash is animated. */
animatedSplash: boolean;
/** The id of the shard this guild is bound to */
shardId: number;
/** Total number of members in this guild */
@@ -145,13 +171,49 @@ export interface DiscordenoGuild
/** The roles in the guild */
roles: Collection<bigint, DiscordenoRole>;
/** The presences of all the users in the guild. */
presences: Collection<bigint, PresenceUpdate>;
// presences: Collection<bigint, DiscordenoPresence>;
/** The Voice State data for each user in a voice channel in this server. */
voiceStates: Collection<bigint, DiscordenoVoiceState>;
/** Custom guild emojis */
emojis: Collection<bigint, Emoji>;
/** Whether the bot is the owner of this guild */
isOwner: boolean;
/** Holds all the boolean toggles. */
bitfield: bigint;
/** When this guild was joined at */
joinedAt?: number;
/** Icon hash */
icon?: bigint;
/** Splash hash */
splash?: bigint;
/** Banner hash */
banner?: bigint;
/** The stage instances in this guild */
stageInstances?: {
/** The id of this Stage instance */
id: bigint;
/** The guild id of the associated Stage channel */
guildId: bigint;
/** The id of the associated Stage channel */
channelId: bigint;
/** The topic of the Stage instance (1-120 characters) */
topic: string;
/** The privacy level of the Stage instance */
privacyLevel: number;
/** Whether or not Stage discovery is disabled */
discoverableDisabled: boolean;
}[];
welcomeScreen?: {
/** The server description shown in the welcome screen */
description?: string;
/** The channels shown in the welcome screen, up to 5 */
welcomeChannels: {
/** The channel's id */
channelId: bigint;
/** The descriptino schown for the channel */
description: string;
/** The emoji id, if the emoji is custom */
emojiId?: bigint;
/** The emoji name if custom, the unicode character if standard, or `null` if no emoji is set */
emojiName?: string;
}[];
};
}
+3 -1
View File
@@ -5,7 +5,7 @@ import { SnakeCasedPropertiesDeep } from "../types/util.ts";
export function transformVoiceState(
bot: Bot,
payload: { voiceState: SnakeCasedPropertiesDeep<VoiceState> } & { guildId: bigint }
) {
): DiscordenoVoiceState {
return {
bitfield:
(payload.voiceState.deaf ? 1n : 0n) |
@@ -40,4 +40,6 @@ export interface DiscordenoVoiceState {
bitfield: bigint;
/** The time at which the user requested to speak */
requestToSpeakTimestamp?: number;
/** The unique session id */
sessionId: string;
}
-162
View File
@@ -1,162 +0,0 @@
import { DiscordenoThread } from "../../util/transformers/channel_to_thread.ts";
import type { Collection } from "../../util/collection.ts";
import type { PresenceUpdate } from "../activity/presence_update.ts";
import type { StageInstance } from "../channels/stage_instance.ts";
import type { ThreadMemberModified } from "../channels/threads/thread_member.ts";
import type { ThreadMembersUpdateModified } from "../channels/threads/thread_members_update.ts";
import type { Emoji } from "../emojis/emoji.ts";
import type { GatewayPayload } from "../gateway/gateway_payload.ts";
import type { DiscordGatewayPayload } from "../gateway/gateway_payload.ts";
import type { IntegrationCreateUpdate } from "../integrations/integration_create_update.ts";
import type { IntegrationDelete } from "../integrations/integration_delete.ts";
import type { ApplicationCommandCreateUpdateDelete } from "../interactions/commands/application_command_create_update_delete.ts";
import type { BigInteraction, Interaction } from "../interactions/interaction.ts";
import type { InviteCreate } from "../invites/invite_create.ts";
import type { InviteDelete } from "../invites/invite_delete.ts";
import type { MessageReactionAdd } from "../messages/message_reaction_add.ts";
import type { MessageReactionRemove } from "../messages/message_reaction_remove.ts";
import type { MessageReactionRemoveAll } from "../messages/message_reaction_remove_all.ts";
import type { TypingStart } from "../misc/typing_start.ts";
import type { User } from "../users/user.ts";
import type { VoiceServerUpdate } from "../voice/voice_server_update.ts";
import type { VoiceState } from "../voice/voice_state.ts";
import type { DebugArg } from "./debug_arg.ts";
import type { GuildUpdateChange } from "./guild_update_change.ts";
export type EventHandlersDefinitions = {
/** Sent when properties about the user change. */
botUpdate: [user: User];
/** Sent when a new guild channel is created, relevant to the current user. */
channelCreate: [channel: DiscordenoChannel];
/** Sent when a channel is updated. This is not sent when the field `last_message_id` is altered. To keep track of the `last_message_id` changes, you must listen for `MESSAGE_CREATE` events. */
channelUpdate: [channel: DiscordenoChannel, oldChannel: DiscordenoChannel];
/** Sent when a channel relevant to the current user is deleted. */
channelDelete: [channel: DiscordenoChannel];
/** Sent when a message pin is updated */
channelPinsUpdate: [channel: DiscordenoChannel, guild?: DiscordenoGuild, lastPinTimestamp?: string | null];
debug: [args: string | DebugArg, data?: string];
/** Sent before every event. Discordeno awaits the execution of this event before main event gets sent. */
dispatchRequirements: [data: DiscordGatewayPayload, shardId: number];
/** Sent when a user is banned from a guild. */
guildBanAdd: [guild: DiscordenoGuild, user: User, member?: DiscordenoMember];
/** Sent when a user is unbanned from a guild. */
guildBanRemove: [guild: DiscordenoGuild, user: User, member?: DiscordenoMember];
/**
* This event can be sent in three different scenarios:
* 1. When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the `READY` event. Guilds that are unavailable due to an outage will send a `GUILD_DELETE` event.
* 2. When a Guild becomes available again to the client.
* 3. When the current user joins a new Guild.
*
* This event does not get sent on startup
*/
guildCreate: [guild: DiscordenoGuild];
/** This event does get sent on start when shards are loading the guilds */
guildLoaded: [guild: DiscordenoGuild];
/** When a guild goes available this event will be ran. */
guildAvailable: [guild: DiscordenoGuild];
/** When a guild goes unavailable this event will be ran. */
guildUnavailable: [guild: DiscordenoGuild];
/** Sent when a guilds integration gets updated */
guildIntegrationsUpdate: [guild: DiscordenoGuild];
/** Sent when a guild is updated. */
guildUpdate: [guild: DiscordenoGuild, changes: GuildUpdateChange[]];
/** Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. If the `unavailable` field is not set, the user was removed from the guild. */
guildDelete: [guild: DiscordenoGuild];
/** Sent when a guild's emojis have been updated. */
guildEmojisUpdate: [guild: DiscordenoGuild, emojis: Collection<bigint, Emoji>, oldEmojis: Collection<bigint, Emoji>];
/** Sent when a new user joins a guild. */
guildMemberAdd: [guild: DiscordenoGuild, member: DiscordenoMember];
/** Sent when a user is removed from a guild (leave/kick/ban). */
guildMemberRemove: [guild: DiscordenoGuild, user: User, member?: DiscordenoMember];
/** Sent when a guild member is updated. This will also fire when the user object of a guild member changes. */
guildMemberUpdate: [guild: DiscordenoGuild, member: DiscordenoMember, oldMember?: DiscordenoMember];
/** Sent when a user uses a Slash Command (type 2) or clicks a button (type 3). */
interactionCreate: [data: BigInteraction, member?: DiscordenoMember];
/** Sent when a user uses a Slash Command in a guild (type 2) or clicks a button (type 3). */
interactionGuildCreate: [data: BigInteraction, member: DiscordenoMember];
/** Sent when a user uses a Slash Command in a dm (type 2) or clicks a button (type 3). */
interactionDMCreate: [data: Omit<BigInteraction, "member">];
/** Sent when a lurker joins/leaves/moves stage channels. */
lurkerVoiceStateUpdate: [member: DiscordenoMember, voiceState: VoiceState];
/** Sent when a message is created. */
messageCreate: [message: DiscordenoMessage];
/** Sent when a message is deleted. */
messageDelete: [partial: { id: string; channel: DiscordenoChannel }, message?: DiscordenoMessage];
/** Sent when a message is updated. */
messageUpdate: [message: DiscordenoMessage, oldMessage: DiscordenoMessage];
/** Sent when a user updates its nickname */
nicknameUpdate: [guild: DiscordenoGuild, member: DiscordenoMember, nickname: string, oldNickname?: string];
/** A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated. */
presenceUpdate: [presence: PresenceUpdate, oldPresence?: PresenceUpdate];
/** Sent before every event execution. Discordeno will not await its execution. */
raw: [data: GatewayPayload];
/** Sent when all shards went ready. */
ready: [];
/** Sent when a user adds a reaction to a message. */
reactionAdd: [data: MessageReactionAdd, message?: DiscordenoMessage];
/** Sent when a user removes a reaction from a message. */
reactionRemove: [data: MessageReactionRemove, message?: DiscordenoMessage];
/** Sent when a user explicitly removes all reactions from a message. */
reactionRemoveAll: [payload: MessageReactionRemoveAll, message?: DiscordenoMessage];
/** Sent when a bot removes all instances of a given emoji from the reactions of a message. */
reactionRemoveEmoji: [emoji: Partial<Emoji>, messageId: bigint, channelId: bigint, guildId?: bigint];
/** Sent when a guild role is created. */
roleCreate: [guild: DiscordenoGuild, role: DiscordenoRole];
/** Sent when a guild role is deleted. */
roleDelete: [guild: DiscordenoGuild, role: DiscordenoRole];
/** Sent when a guild role is updated. */
roleUpdate: [guild: DiscordenoGuild, role: DiscordenoRole, old: DiscordenoRole];
roleGained: [guild: DiscordenoGuild, member: DiscordenoMember, roleId: bigint];
roleLost: [guild: DiscordenoGuild, member: DiscordenoMember, roleId: bigint];
shardReady: [shardId: number];
/** Sent when a shard failed to load. */
shardFailedToLoad: [shardId: number, unavailableGuildIds: Set<bigint>];
/** Sent when a Stage instance is created (i.e. the Stage is now "live"). */
stageInstanceCreate: [instance: StageInstance];
/** Sent when a Stage instance has been deleted (i.e. the Stage has been closed). */
stageInstanceDelete: [instance: StageInstance];
/** Sent when a Stage instance has been updated. */
stageInstanceUpdate: [instance: StageInstance];
/** Sent when a thread is created */
threadCreate: [thread: DiscordenoThread];
/** Sent when a thread is updated */
threadUpdate: [thread: DiscordenoThread, oldThread: DiscordenoThread];
/** Sent when the bot gains access to threads */
threadListSync: [threads: Collection<bigint, DiscordenoThread>, members: ThreadMemberModified[], guildId: bigint];
/** Sent when the current users thread member is updated */
threadMemberUpdate: [threadMember: ThreadMemberModified, thread: DiscordenoThread];
/** Sent when anyone is added to or removed from a thread */
threadMembersUpdate: [update: ThreadMembersUpdateModified];
/** Sent when a thread is deleted */
threadDelete: [thread: DiscordenoThread];
/** Sent when a user starts typing in a channel. */
typingStart: [data: TypingStart];
/** Sent when a user joins a voice channel */
voiceChannelJoin: [member: DiscordenoMember, channelId: bigint];
/** Sent when a user leaves a voice channel. Does not get sent when user switches the voice channel */
voiceChannelLeave: [member: DiscordenoMember, channelId: bigint];
/** Sent when a user switches the voice channel */
voiceChannelSwitch: [member: DiscordenoMember, channelId: bigint, oldChannelId: bigint];
/** Sent when a voice server is updated with information for making the bot connect to a voice channel. */
voiceServerUpdate: [payload: VoiceServerUpdate, guild: DiscordenoGuild];
/** Sent when someone joins/leaves/moves voice channels. */
voiceStateUpdate: [member: DiscordenoMember, voiceState: VoiceState];
/** Sent when a guild channel's webhook is created, updated, or deleted. */
webhooksUpdate: [channelId: bigint, guildId: bigint];
/** Sent when a member has passed the guild's Membership Screening requirements */
membershipScreeningPassed: [guild: DiscordenoGuild, member: DiscordenoMember];
/** Sent when an integration is created on a server such as twitch, youtube etc.. */
integrationCreate: [data: IntegrationCreateUpdate];
/** Sent when an integration is updated. */
integrationUpdate: [data: IntegrationCreateUpdate];
/** Sent when an integration is deleted. */
integrationDelete: [data: IntegrationDelete];
/** Sent when a new invite to a channel is created. */
inviteCreate: [data: InviteCreate];
/** Sent when an invite is deleted. */
inviteDelete: [data: InviteDelete];
};
export type EventHandlers = {
[E in keyof EventHandlersDefinitions]?: (...args: EventHandlersDefinitions[E]) => unknown;
};
-1
View File
@@ -2,7 +2,6 @@ export * from "./create_slash_command.ts";
export * from "./debug_arg.ts";
export * from "./edit_webhook_message.ts";
export * from "./errors.ts";
export * from "./event_handlers.ts";
export * from "./file_content.ts";
export * from "./guild_member.ts";
export * from "./guild_update_change.ts";
+2 -2
View File
@@ -1,11 +1,11 @@
/** https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure */
export interface WelcomeScreenChannel {
/** The channel's id */
channelId: bigint;
channelId: string;
/** The descriptino schown for the channel */
description: string;
/** The emoji id, if the emoji is custom */
emojiId: bigint | null;
emojiId: string | null;
/** The emoji name if custom, the unicode character if standard, or `null` if no emoji is set */
emojiName: string | null;
}
-3
View File
@@ -1,12 +1,9 @@
export * from "./transformers/mod.ts";
export * from "./bigint.ts";
export * from "./cache_members.ts";
export * from "./calculate_shard_id.ts";
export * from "./collection.ts";
export * from "./constants.ts";
export * from "./dispatch_requirements.ts";
export * from "./hash.ts";
export * from "./loop_object.ts";
export * from "./permissions.ts";
export * from "./utils.ts";
export * from "./validate_length.ts";
-108
View File
@@ -1,108 +0,0 @@
import { cache } from "../../cache.ts";
import { Channel } from "../../types/channels/channel.ts";
import { DiscordChannelTypes } from "../../types/channels/channel_types.ts";
import { ThreadMemberModified } from "../../types/channels/threads/thread_member.ts";
import { snowflakeToBigint } from "../bigint.ts";
import { Collection } from "../collection.ts";
import { createNewProp } from "../utils.ts";
export const threadToggles = {
/** Whether this thread is archived. */
archived: 1n,
/** Whether this thread is locked. */
locked: 2n,
};
const baseThread: Partial<DiscordenoThread> = {
get archived() {
return Boolean(this.bitfield! & threadToggles.archived);
},
get locked() {
return Boolean(this.bitfield! & threadToggles.locked);
},
get isPrivate() {
return this.type === DiscordChannelTypes.GuildPrivateThread;
},
get isPublic() {
return !this.isPrivate;
},
get guildId() {
return cache.channels.get(this.parentId!)!.guildId;
},
toJSON() {
return {
id: this.id?.toString(),
type: this.type,
parentId: this.parentId?.toString(),
memberCount: this.memberCount,
messageCount: this.messageCount,
archiveTimestamp: new Date(this.archiveTimestamp!).toISOString(),
autoArchiveDuration: this.autoArchiveDuration,
archived: this.archived,
locked: this.locked,
} as Thread;
},
};
export function channelToThread(channel: Channel) {
let bitfield = 0n;
if (channel.threadMetadata?.archived) bitfield |= threadToggles.archived;
if (channel.threadMetadata?.locked) bitfield |= threadToggles.locked;
return Object.create(baseThread, {
id: createNewProp(snowflakeToBigint(channel.id)),
type: createNewProp(channel.type),
parentId: createNewProp(snowflakeToBigint(channel.parentId!)),
memberCount: createNewProp(channel.memberCount),
messageCount: createNewProp(channel.messageCount),
archiveTimestamp: createNewProp(
channel.threadMetadata?.archiveTimestamp ? Date.parse(channel.threadMetadata.archiveTimestamp) : undefined
),
autoArchiveDuration: createNewProp(channel.threadMetadata?.autoArchiveDuration || 0),
bitfield: createNewProp(bitfield),
ownerId: createNewProp(snowflakeToBigint(channel.ownerId!)),
botIsMember: createNewProp(Boolean(channel.member)),
members: createNewProp(new Collection<bigint, Omit<ThreadMemberModified, "id">>()),
}) as DiscordenoThread;
}
export interface Thread {
id: string;
type:
| DiscordChannelTypes.GuildNewsThread
| DiscordChannelTypes.GuildPublicThread
| DiscordChannelTypes.GuildPrivateThread;
parentId: string;
memberCount: number;
messageCount: number;
archiveTimestamp: string;
autoArchiveDuration: number;
archived: boolean;
locked: boolean;
ownerId: string;
botIsMember: boolean;
}
export interface DiscordenoThread {
id: bigint;
type:
| DiscordChannelTypes.GuildNewsThread
| DiscordChannelTypes.GuildPublicThread
| DiscordChannelTypes.GuildPrivateThread;
parentId: bigint;
memberCount: number;
messageCount: number;
archiveTimestamp: number;
autoArchiveDuration: number;
archived: boolean;
locked: boolean;
bitfield: bigint;
ownerId: bigint;
isPrivate: boolean;
isPublic: boolean;
botIsMember: boolean;
guildId: bigint;
members: Collection<bigint, Omit<ThreadMemberModified, "id">>;
toJSON(): Thread;
}
-3
View File
@@ -1,3 +0,0 @@
export * from "./channel_to_thread.ts";
export * from "./thread_member_modified.ts";
export * from "./thread_members_update_modified.ts";
@@ -1,11 +0,0 @@
import { ThreadMember, ThreadMemberModified } from "../../types/channels/threads/thread_member.ts";
import { snowflakeToBigint } from "../bigint.ts";
export function threadMemberModified(member: ThreadMember) {
return {
...member,
id: snowflakeToBigint(member.id!),
userId: snowflakeToBigint(member.userId!),
joinTimestamp: Date.parse(member.joinTimestamp),
} as ThreadMemberModified;
}
@@ -1,16 +0,0 @@
import {
ThreadMembersUpdate,
ThreadMembersUpdateModified,
} from "../../types/channels/threads/thread_members_update.ts";
import { snowflakeToBigint } from "../bigint.ts";
import { threadMemberModified } from "./thread_member_modified.ts";
export function threadMembersUpdateModified(data: ThreadMembersUpdate) {
return {
...data,
id: snowflakeToBigint(data.id),
guildId: snowflakeToBigint(data.guildId),
addedMembers: data.addedMembers?.map((member) => threadMemberModified(member)) || [],
removedMemberIds: data.removedMemberIds?.map((id) => snowflakeToBigint(id)) || [],
} as ThreadMembersUpdateModified;
}
+14 -11
View File
@@ -1,15 +1,15 @@
import { DiscordGatewayIntents } from "../types/gateway/gateway_intents.ts";
import type { GetGatewayBot } from "../types/gateway/get_gateway_bot.ts";
import { camelize } from "../util/utils.ts";
import { StartGatewayOptions } from "./start_gateway_options.ts";
import { GatewayManager } from "../bot.ts";
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
/** ADVANCED DEVS ONLY!!!!!!
* Starts the standalone gateway.
* This will require starting the bot separately.
*/
export async function startGateway(gateway: GatewayManager, options: StartGatewayOptions) {
gateway.identifyPayload.token = `Bot ${options.token}`;
gateway.token = `Bot ${options.token}`;
gateway.secretKey = options.secretKey;
gateway.firstShardId = options.firstShardId;
gateway.url = options.url;
@@ -17,25 +17,28 @@ export async function startGateway(gateway: GatewayManager, options: StartGatewa
if (options.maxClusters) gateway.maxClusters = options.maxClusters;
if (options.compress) {
gateway.identifyPayload.compress = options.compress;
gateway.compress = options.compress;
}
if (options.reshard) gateway.reshard = options.reshard;
// Once an hour check if resharding is necessary
setInterval(() => gateway.resharder(gateway), 1000 * 60 * 60);
gateway.identifyPayload.intents = options.intents.reduce(
gateway.intents = options.intents.reduce(
(bits, next) => (bits |= typeof next === "string" ? DiscordGatewayIntents[next] : next),
0
);
gateway.botGatewayData = camelize(
await fetch(`https://discord.com/api/gateway/bot`, {
headers: { Authorization: gateway.identifyPayload.token },
}).then((res) => res.json())
) as GetGatewayBot;
const result = (await fetch(`https://discord.com/api/gateway/bot`, {
headers: { Authorization: gateway.token },
}).then((res) => res.json())) as SnakeCasedPropertiesDeep<GetGatewayBot>;
gateway.maxShards = options.maxShards || gateway.botGatewayData.shards;
gateway.lastShardId = options.lastShardId || gateway.botGatewayData.shards - 1;
gateway.url = result.url;
gateway.sessionStartLimitTotal = result.session_start_limit.total;
gateway.sessionStartLimitRemaining = result.session_start_limit.remaining;
gateway.sessionStartLimitResetAfter = result.session_start_limit.reset_after;
gateway.maxConcurrency = result.session_start_limit.max_concurrency;
gateway.maxShards = options.maxShards || result.shards;
gateway.lastShardId = options.lastShardId || result.shards - 1;
gateway.spawnShards(gateway, gateway.firstShardId);
}
-122
View File
@@ -1,126 +1,4 @@
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { Collection } from "../util/collection.ts";
import { closeWS } from "./close_ws.ts";
import { createShard } from "./create_shard.ts";
import { log } from "./events.ts";
import { handleDiscordPayload } from "./handle_discord_payload.ts";
import { handleOnMessage } from "./handle_on_message.ts";
import { heartbeat } from "./heartbeat.ts";
import { identify } from "./identify.ts";
import { processQueue } from "./process_queue.ts";
import { resharder } from "./resharder.ts";
import { sendShardMessage } from "./send_shard_message.ts";
import { spawnShards } from "./spawn_shards.ts";
import { startGateway } from "./start_gateway.ts";
import { tellClusterToIdentify } from "./tell_cluster_to_identify.ts";
import { resume } from "./resume.ts";
// CONTROLLER LIKE INTERFACE FOR WS HANDLING
export const ws = {
/** The secret key authorization header the bot will expect when sending payloads. */
secretKey: "",
/** The url that all discord payloads for the dispatch type should be sent to. */
url: "",
/** Whether or not to automatically reshard. */
reshard: true,
/** The percentage at which resharding should occur. */
reshardPercentage: 80,
/** The delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 2500. YOU DON"T WANT TO HIT THE RATE LIMIT!!! */
spawnShardDelay: 2600,
/** The maximum shard Id number. Useful for zero-downtime updates or resharding. */
maxShards: 0,
/** Whether or not the resharder should automatically switch to LARGE BOT SHARDING when you are above 100K servers. */
useOptimalLargeBotSharding: true,
/** The amount of shards to load per cluster. */
shardsPerCluster: 25,
/** The maximum amount of clusters to use for your bot. */
maxClusters: 4,
/** The first shard Id to start spawning. */
firstShardId: 0,
/** The last shard Id for this cluster. */
lastShardId: 1,
/** The identify payload holds the necessary data to connect and stay connected with Discords WSS. */
identifyPayload: {
token: "",
compress: false,
properties: {
$os: "linux",
$browser: "Discordeno",
$device: "Discordeno",
},
intents: 0,
shard: [0, 0],
},
botGatewayData: {
/** The WSS URL that can be used for connecting to the gateway. */
url: "wss://gateway.discord.gg/?v=9&encoding=json",
/** The recommended number of shards to use when connecting. */
shards: 1,
/** Info on the current start limit. */
sessionStartLimit: {
/** The total number of session starts the current user is allowed. */
total: 1000,
/** The remaining number of session starts the current user is allowed. */
remaining: 1000,
/** Milliseconds left until limit is reset. */
resetAfter: 0,
/** The number of identify requests allowed per 5 seconds.
* So, if you had a max concurrency of 16, and 16 shards for example, you could start them all up at the same time.
* Whereas if you had 32 shards, if you tried to start up shard 0 and 16 at the same time for example, it would not work. You can start shards 0-15 concurrently, then 16-31...
*/
maxConcurrency: 1,
},
},
shards: new Collection<number, DiscordenoShard>(),
loadingShards: new Collection<
number,
{
shardId: number;
resolve: (value: unknown) => void;
startedAt: number;
}
>(),
/** Stored as bucketId: { clusters: [clusterId, [ShardIds]], createNextShard: boolean } */
buckets: new Collection<
number,
{
clusters: number[][];
createNextShard: (() => unknown)[];
}
>(),
utf8decoder: new TextDecoder(),
// METHODS
/** The handler function that starts the gateway. */
startGateway,
/** The handler for spawning ALL the shards. */
spawnShards,
/** Create the websocket and adds the proper handlers to the websocket. */
createShard,
/** Begins identification of the shard to discord. */
identify,
/** Begins heartbeating of the shard to keep it alive. */
heartbeat,
/** Sends the discord payload to another server. */
handleDiscordPayload,
/** Tell the cluster/worker to begin identifying this shard */
tellClusterToIdentify,
/** Handle the different logs. Used for debugging. */
log,
/** Handles resharding the bot when necessary. */
resharder,
/** Handles the message events from websocket. */
handleOnMessage,
/** Handles processing queue of requests send to this shard. */
processQueue,
/** Closes shard WebSocket connection properly. */
closeWS,
/** Properly adds a message to the shards queue. */
sendShardMessage,
/** Properly resume an old shards session. */
resume,
};
export interface DiscordenoShard {
/** The shard id number. */
+15 -17
View File
@@ -1,22 +1,20 @@
// THE ORDER OF THE IMPORTS IN THIS FILE MATTER!
// DO NOT MOVE THEM UNLESS YOU KNOW WHAT YOUR DOING!
// // THE ORDER OF THE IMPORTS IN THIS FILE MATTER!
// // DO NOT MOVE THEM UNLESS YOU KNOW WHAT YOUR DOING!
import "./util/utils.ts";
import "./util/validate_length.ts";
import "./util/loop_object.ts";
// import "./util/utils.ts";
// import "./util/validate_length.ts";
// import "./util/loop_object.ts";
// Final cleanup
// // Final cleanup
import { cache } from "../src/cache.ts";
import { delay } from "../src/util/utils.ts";
if (import.meta.main) {
// clear all the sweeper intervals
for (const c of Object.values(cache)) {
if (!(c instanceof Map)) continue;
// import { delay } from "../src/util/utils.ts";
// if (import.meta.main) {
// // clear all the sweeper intervals
// for (const c of Object.values(cache)) {
// if (!(c instanceof Map)) continue;
c.stopSweeper();
console.log("Cleaned");
}
// console.log("Cleaned");
// }
await delay(3000);
}
// await delay(3000);
// }
+105 -76
View File
@@ -1,90 +1,119 @@
// THE ORDER OF THE IMPORTS IN THIS FILE MATTER!
// DO NOT MOVE THEM UNLESS YOU KNOW WHAT YOUR DOING!
import { TOKEN } from "../configs.ts";
import { Bot, createBot, createEventHandlers, startBot } from "../mod.ts";
// First complete non-api reliant testing.
// Don't waste api rate limits if a early test fails.
import "./local.ts";
Deno.test("[Bot] - Starting Tests", async (t) => {
const bot = createBot({
token: TOKEN || Deno.env.get('DISCORD_TOKEN'),
botId: 0n,
events: createEventHandlers({
// API TESTING BELOW
}),
intents: [],
})
// First initiate the connection
import "./ws/start_bot.ts";
import "./guilds/create_guild.ts";
await startBot(bot as Bot);
// Channel tests
import "./channels/category_children.ts";
import "./channels/channel_overwrite_has_permission.ts";
import "./channels/create_channel.ts";
import "./channels/clone_channel.ts";
import "./channels/delete_channel.ts";
import "./channels/delete_channel_overwrite.ts";
import "./channels/edit_channel.ts";
import "./channels/edit_channel_overwrite.ts";
import "./channels/get_channel.ts";
import "./channels/get_channels.ts";
import "./channels/get_pins.ts";
import "./channels/is_channel_synced.ts";
import "./channels/start_typing.ts";
import "./channels/swap_channels.ts";
console.log('Bot online')
})
// Emojis tests
import "./emojis/create_emoji.ts";
import "./emojis/delete_emoji.ts";
import "./emojis/edit_emoji.ts";
import "./emojis/get_emoji.ts";
import "./emojis/get_emojis.ts";
// Invites tests
import "./invites/create_invite.ts";
import "./invites/delete_invite.ts";
import "./invites/get_channel_invites.ts";
import "./invites/get_invite.ts";
import "./invites/get_invites.ts";
// Messages tests
import "./messages/add_reaction.ts";
import "./messages/add_reactions.ts";
import "./messages/remove_all_reactions.ts";
import "./messages/remove_reaction.ts";
import "./messages/remove_reaction_emoji.ts";
import "./messages/create_message.ts";
import "./messages/delete_message.ts";
import "./messages/delete_messages.ts";
import "./messages/edit_message.ts";
import "./messages/get_message.ts";
import "./messages/get_messages.ts";
import "./messages/get_reactions.ts";
import "./messages/pin_message.ts";
import "./messages/unpin_message.ts";
// Roles tests
import "./roles/add_role.ts";
import "./roles/create_role.ts";
import "./roles/delete_role.ts";
import "./roles/edit_role.ts";
import "./roles/remove_role.ts";
// Members tests
import "./members/search_members.ts";
// Discoveries tests
import "./discoveries/get_discovery_categories.ts";
import "./discoveries/valid_discovery_term.ts";
// Final cleanup
import "./guilds/delete_guild.ts";
import "./ws/ws_close.ts";
import { cache } from "../src/cache.ts";
import { delay } from "../src/util/utils.ts";
if (import.meta.main) {
// clear all the sweeper intervals
for (const c of Object.values(cache)) {
if (!(c instanceof Map)) continue;
c.stopSweeper();
console.log("Cleaned");
}
await delay(3000);
}
// // THE ORDER OF THE IMPORTS IN THIS FILE MATTER!
// // DO NOT MOVE THEM UNLESS YOU KNOW WHAT YOUR DOING!
// // First complete non-api reliant testing.
// // Don't waste api rate limits if a early test fails.
// // import "./local.ts";
// // API TESTING BELOW
// // First initiate the connection
// import "./ws/start_bot.ts";
// import "./guilds/create_guild.ts";
// // Channel tests
// import "./channels/category_children.ts";
// import "./channels/channel_overwrite_has_permission.ts";
// import "./channels/create_channel.ts";
// import "./channels/clone_channel.ts";
// import "./channels/delete_channel.ts";
// import "./channels/delete_channel_overwrite.ts";
// import "./channels/edit_channel.ts";
// import "./channels/edit_channel_overwrite.ts";
// import "./channels/get_channel.ts";
// import "./channels/get_channels.ts";
// import "./channels/get_pins.ts";
// import "./channels/is_channel_synced.ts";
// import "./channels/start_typing.ts";
// import "./channels/swap_channels.ts";
// // Emojis tests
// import "./emojis/create_emoji.ts";
// import "./emojis/delete_emoji.ts";
// import "./emojis/edit_emoji.ts";
// import "./emojis/get_emoji.ts";
// import "./emojis/get_emojis.ts";
// // Invites tests
// import "./invites/create_invite.ts";
// import "./invites/delete_invite.ts";
// import "./invites/get_channel_invites.ts";
// import "./invites/get_invite.ts";
// import "./invites/get_invites.ts";
// // Messages tests
// import "./messages/add_reaction.ts";
// import "./messages/add_reactions.ts";
// import "./messages/remove_all_reactions.ts";
// import "./messages/remove_reaction.ts";
// import "./messages/remove_reaction_emoji.ts";
// import "./messages/create_message.ts";
// import "./messages/delete_message.ts";
// import "./messages/delete_messages.ts";
// import "./messages/edit_message.ts";
// import "./messages/get_message.ts";
// import "./messages/get_messages.ts";
// import "./messages/get_reactions.ts";
// import "./messages/pin_message.ts";
// import "./messages/unpin_message.ts";
// // Roles tests
// import "./roles/add_role.ts";
// import "./roles/create_role.ts";
// import "./roles/delete_role.ts";
// import "./roles/edit_role.ts";
// import "./roles/remove_role.ts";
// // Members tests
// import "./members/search_members.ts";
// // Discoveries tests
// import "./discoveries/get_discovery_categories.ts";
// import "./discoveries/valid_discovery_term.ts";
// // Final cleanup
// import "./guilds/delete_guild.ts";
// import "./ws/ws_close.ts";
// import { cache } from "../src/cache.ts";
// import { delay } from "../src/util/utils.ts";
// if (import.meta.main) {
// // clear all the sweeper intervals
// for (const c of Object.values(cache)) {
// if (!(c instanceof Map)) continue;
// c.stopSweeper();
// console.log("Cleaned");
// }
// await delay(3000);
// }