more stuff

This commit is contained in:
Skillz4Killz
2021-04-08 19:26:38 +00:00
committed by GitHub
parent 3eaf1a5dcc
commit ad87124bca
13 changed files with 371 additions and 372 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
export { serve } from "https://deno.land/std@0.90.0/http/server.ts";
export { verify } from "https://esm.sh/@evan/wasm@0.0.50/target/ed25519/deno.js";
export { verify } from "https://unpkg.com/@evan/wasm@0.0.50/target/ed25519/deno.js";
+8 -8
View File
@@ -8,6 +8,7 @@ export async function processQueue(id: string) {
if (!queue) return;
while (queue.length) {
console.log("process queue");
// IF THE BOT IS GLOBALLY RATELIMITED TRY AGAIN
if (rest.globallyRateLimited) {
setTimeout(() => processQueue(id), 1000);
@@ -36,16 +37,15 @@ export async function processQueue(id: string) {
// EXECUTE THE REQUEST
// IF THIS IS A GET REQUEST, CHANGE THE BODY TO QUERY PARAMETERS
const query = queuedRequest.request.method.toUpperCase() === "GET" &&
const query =
queuedRequest.request.method.toUpperCase() === "GET" &&
queuedRequest.payload.body
? Object.entries(queuedRequest.payload.body)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${
encodeURIComponent(
value as string,
)
}`,
`${encodeURIComponent(key)}=${encodeURIComponent(
value as string
)}`
)
.join("&")
: "";
@@ -60,13 +60,13 @@ export async function processQueue(id: string) {
try {
const response = await fetch(
urlToUse,
rest.createRequestBody(queuedRequest),
rest.createRequestBody(queuedRequest)
);
rest.eventHandlers.fetched(queuedRequest.payload);
const bucketIdFromHeaders = rest.processRequestHeaders(
queuedRequest.request.url,
response.headers,
response.headers
);
if (response.status < 200 || response.status >= 400) {
+1 -1
View File
@@ -1,6 +1,6 @@
import { SnakeCasedPropertiesDeep } from "../util.ts";
import { DiscordVisibilityTypes } from "./visibility_types.ts";
import { Integration } from "../guilds/integration.ts";
import { Integration } from "../integration/integration.ts";
export interface Connection {
/** id of the connection account */
+2 -2
View File
@@ -59,11 +59,11 @@ export const formatImageURL = (
};
function camelToSnakeCase(text: string) {
return text.replace(/Id|[A-Z]/g, ($1) => `_${$1.toLowerCase()}`);
return text.replace(/[A-Z]/g, ($1) => `_${$1.toLowerCase()}`);
}
function snakeToCamelCase(text: string) {
return text.replace(/_id|([-_][a-z])/gi, ($1) =>
return text.replace(/([-_][a-z])/gi, ($1) =>
$1.toUpperCase().replace("_", "")
);
}
+3 -1
View File
@@ -4,13 +4,15 @@ import { ws } from "./ws.ts";
/** The handler to clean up shards that identified but never received a READY. */
export async function cleanupLoadingShards() {
while (ws.loadingShards.size) {
console.log("cls");
const now = Date.now();
ws.loadingShards.forEach((loadingShard) => {
console.log(loadingShard);
// Not a minute yet. Max should be few seconds but do a minute to be safe.
if (now < loadingShard.startedAt + 60000) return;
loadingShard.reject(
`[Identify Failure] Shard ${loadingShard.shardId} has not received READY event in over a minute.`,
`[Identify Failure] Shard ${loadingShard.shardId} has not received READY event in over a minute.`
);
});
+4 -2
View File
@@ -16,6 +16,9 @@ export async function createShard(shardId: number) {
socket.onclose = (event) => {
ws.log("CLOSED", { shardId, payload: event });
if (event.code === 4009 && ["Resharded!", "Resuming the shard, closing old shard."].includes(event.reason)) {
return ws.log("CLOSED_RECONNECT", { shardId, payload: event });
}
// TODO: ENUM FOR THESE CODES?
switch (event.code) {
@@ -29,14 +32,13 @@ export async function createShard(shardId: number) {
case 4013:
case 4014:
throw new Error(
event.reason || "Discord gave no reason! GG! You broke Discord!",
event.reason || "Discord gave no reason! GG! You broke Discord!"
);
// THESE ERRORS CAN NO BE RESUMED! THEY MUST RE-IDENTIFY!
case 4003:
case 4007:
case 4008:
case 4009:
ws.log("CLOSED_RECONNECT", { shardId, payload: event });
identify(shardId, ws.maxShards);
break;
default:
+15 -5
View File
@@ -15,10 +15,8 @@ export async function handleOnMessage(message: any, shardId: number) {
}
if (message instanceof Uint8Array) {
message = decompressWith(
message,
0,
(slice: Uint8Array) => ws.utf8decoder.decode(slice),
message = decompressWith(message, 0, (slice: Uint8Array) =>
ws.utf8decoder.decode(slice)
);
}
@@ -31,7 +29,7 @@ export async function handleOnMessage(message: any, shardId: number) {
case DiscordGatewayOpcodes.Hello:
ws.heartbeat(
shardId,
(messageData.d as DiscordHeartbeat).heartbeat_interval,
(messageData.d as DiscordHeartbeat).heartbeat_interval
);
break;
case DiscordGatewayOpcodes.HeartbeatACK:
@@ -80,8 +78,20 @@ export async function handleOnMessage(message: any, shardId: number) {
shard.sessionId = (messageData.d as DiscordReady).session_id;
}
console.log(
"shoulda deleted it",
shardId,
ws.loadingShards.has(shardId),
ws.loadingShards
);
ws.loadingShards.get(shardId)?.resolve(true);
ws.loadingShards.delete(shardId);
console.log(
"shoulda deleted it",
shardId,
ws.loadingShards.has(shardId),
ws.loadingShards
);
}
// Update the sequence number if it is present
+2 -1
View File
@@ -32,11 +32,12 @@ export async function identify(shardId: number, maxShards: number) {
JSON.stringify({
op: DiscordGatewayOpcodes.Identify,
d: { ...ws.identifyPayload, shard: [shardId, maxShards] },
}),
})
);
};
return new Promise((resolve, reject) => {
console.log("setting the shard loader");
ws.loadingShards.set(shardId, {
shardId,
resolve,
+1
View File
@@ -44,6 +44,7 @@ export function spawnShards(firstShardId = 0) {
let shardId = queue.shift();
while (shardId !== undefined) {
console.log("spawn shards");
await ws.tellClusterToIdentify(clusterId as number, shardId, bucketId);
shardId = queue.shift();
}
+2 -2
View File
@@ -18,8 +18,8 @@ export async function startGateway(options: StartGatewayOptions) {
ws.identifyPayload.compress = options.compress;
}
if (options.reshard) ws.reshard = options.reshard;
// Once an hour check if resharding is necessary
setInterval(ws.resharder, 1000 * 60 * 60);
// TODO: Once an hour check if resharding is necessary
// setInterval(ws.resharder, 1000 * 60 * 60);
ws.identifyPayload.intents = options.intents.reduce(
(
-341
View File
@@ -1,341 +0,0 @@
import {
addReaction,
botId,
cache,
Channel,
channelOverwriteHasPermission,
createChannel,
createGuild,
createRole,
delay,
deleteChannel,
deleteMessage,
deleteRole,
deleteServer,
editChannel,
editRole,
getChannel,
getMessage,
getPins,
Guild,
OverwriteType,
pinMessage,
removeReaction,
Role,
sendMessage,
startBot,
unpinMessage,
} from "../mod.ts";
import { assertEquals, assertExists } from "./deps.ts";
// Default options for tests
export const defaultTestOptions: Partial<Deno.TestDefinition> = {
sanitizeOps: false,
sanitizeResources: false,
sanitizeExit: false,
};
// Temporary data
export const tempData = {
guildId: "",
roleId: "",
channelId: "",
messageId: "",
};
// Main
Deno.test({
name: "[main] connect to gateway",
async fn() {
const token = Deno.env.get("DISCORD_TOKEN");
if (!token) throw new Error("Token is not provided");
await startBot({
token,
intents: ["GUILD_MESSAGES", "GUILDS"],
});
// Delay the execution by 5 seconds
await delay(5000);
// Assertions
assertExists(botId);
},
...defaultTestOptions,
});
// Guild
Deno.test({
name: "[guild] create a new guild",
async fn() {
const guild = await createGuild({
name: "Discordeno Test",
}) as Guild;
// Assertions
assertExists(guild);
tempData.guildId = guild.id;
// Delay the execution by 5 seconds to allow GUILD_CREATE event to be processed
await delay(5000);
},
...defaultTestOptions,
});
// Role
Deno.test({
name: "[role] create a role in a guild",
async fn() {
if (!tempData.guildId) {
throw new Error("guildId not present in temporary data");
}
const name = "Discordeno Test";
const role = await createRole(tempData.guildId, {
name,
});
// Assertions
assertExists(role);
assertEquals(role.name, name);
tempData.roleId = role.id;
},
...defaultTestOptions,
});
Deno.test({
name: "[role] edit a role in a guild",
async fn() {
const name = "Discordeno Test Edited";
const color = 4320244;
const role = await editRole(tempData.guildId, tempData.roleId, {
name,
color,
hoist: false,
mentionable: false,
}) as Role;
// Assertions
assertExists(role);
assertEquals(role.name, name);
assertEquals(role.color, color);
assertEquals(role.hoist, false);
assertEquals(role.mentionable, false);
tempData.roleId = role.id;
},
...defaultTestOptions,
});
// Channel
Deno.test({
name: "[channel] create a channel in a guild",
async fn() {
const channel = await createChannel(tempData.guildId, "test");
// Assertions
assertExists(channel);
tempData.channelId = channel.id;
},
...defaultTestOptions,
});
Deno.test({
name: "[channel] get a channel in a guild",
async fn() {
const channel = await getChannel(tempData.channelId);
// Assertions
assertExists(channel);
assertEquals(channel.id, tempData.channelId);
},
...defaultTestOptions,
});
Deno.test({
name: "[channel] edit a channel in a guild",
async fn() {
const channel = await editChannel(tempData.channelId, {
name: "discordeno-test-edited",
overwrites: [
{
id: tempData.roleId,
type: OverwriteType.ROLE,
allow: ["VIEW_CHANNEL", "SEND_MESSAGES"],
deny: ["USE_EXTERNAL_EMOJIS"],
},
],
}) as Channel;
// Wait 5s for CHANNEL_UPDATE to fire
await delay(5000);
// Assertions
assertExists(channel);
assertEquals(channel.name, "discordeno-test-edited");
},
...defaultTestOptions,
});
Deno.test({
name: "[channel] channel overwrite has permission",
fn() {
const channel = cache.channels.get(tempData.channelId);
if (!channel) throw new Error("Channel not found");
if (!channel.permissionOverwrites) {
throw new Error("permissionOverwrites not found");
}
const hasPerm = channelOverwriteHasPermission(
tempData.guildId,
tempData.roleId,
channel.permissionOverwrites,
["VIEW_CHANNEL", "SEND_MESSAGES"],
);
const missingPerm = channelOverwriteHasPermission(
tempData.guildId,
tempData.roleId,
channel.permissionOverwrites,
["USE_EXTERNAL_EMOJIS"],
);
assertEquals(hasPerm, true);
assertEquals(missingPerm, false);
},
...defaultTestOptions,
});
// Message
Deno.test({
name: "[message] send a message in a text channel",
async fn() {
const message = await sendMessage(tempData.channelId, {
embed: {
title: "Discordeno Test",
},
});
// Assertions
assertExists(message);
assertEquals(message.embeds[0].title, "Discordeno Test");
tempData.messageId = message.id;
},
...defaultTestOptions,
});
Deno.test({
name: "[message] get a message in a guild",
async fn() {
const message = await getMessage(tempData.channelId, tempData.messageId);
// Assertions
assertExists(message);
assertEquals(message.embeds[0].title, "Discordeno Test");
},
...defaultTestOptions,
});
Deno.test({
name: "[message] pin a message in a channel",
async fn() {
await pinMessage(tempData.channelId, tempData.messageId);
},
...defaultTestOptions,
});
Deno.test({
name: "[message] get pinned message in a channel",
async fn() {
const [msg] = await getPins(tempData.channelId);
// Assertions
assertExists(msg);
assertEquals(msg.id, tempData.messageId);
assertEquals(msg.pinned, true);
},
...defaultTestOptions,
});
Deno.test({
name: "[message] unpin a message",
async fn() {
await unpinMessage(tempData.channelId, tempData.messageId);
},
...defaultTestOptions,
});
Deno.test({
name: "[message] add a reaction to a message",
async fn() {
// TODO: add tests for a guild emoji ― <:name:id>
await addReaction(tempData.channelId, tempData.messageId, "👍");
},
...defaultTestOptions,
});
// TODO(ayntee): add unit tests for getReactions()
Deno.test({
name: "[message] remove a reaction to a message",
async fn() {
await removeReaction(tempData.channelId, tempData.messageId, "👍");
},
...defaultTestOptions,
});
// Cleanup
Deno.test({
name: "[message] delete a message by channel Id",
async fn() {
await deleteMessage(tempData.channelId, tempData.messageId);
},
...defaultTestOptions,
});
Deno.test({
name: "[channel] delete a channel in a guild",
async fn() {
await deleteChannel(tempData.guildId, tempData.channelId);
},
...defaultTestOptions,
});
Deno.test({
name: "[role] delete a role in a guild",
async fn() {
await deleteRole(tempData.guildId, tempData.roleId);
},
...defaultTestOptions,
});
Deno.test({
name: "[guild] delete a guild",
async fn() {
await deleteServer(tempData.guildId);
// TODO(ayntee): remove this weird shit lol
// TODO(ayntee): check if the GUILD_DELETE event is fired
tempData.guildId = "";
assertEquals(tempData.guildId, "");
},
...defaultTestOptions,
});
// Forcefully exit the Deno process once all tests are done.
Deno.test({
name: "[main] exit the process forcefully",
fn() {
Deno.exit();
},
...defaultTestOptions,
});
+324
View File
@@ -0,0 +1,324 @@
import { botId, delay, startBot, ws } from "../mod.ts";
import { assertExists } from "./deps.ts";
// Set necessary settings
// Disables the logger which logs everything
ws.log = function (x: string, d: any) {
if (["RAW", "GUILD_CREATE", "HEARTBEATING_DETAILS"].includes(x)) return console.log(x);
console.log(x, d);
};
// Default options for tests
export const defaultTestOptions: Partial<Deno.TestDefinition> = {
sanitizeOps: false,
sanitizeResources: false,
sanitizeExit: false,
};
// Temporary data
export const tempData = {
guildId: "",
roleId: "",
channelId: "",
messageId: "",
};
// Main
Deno.test({
name: "[main] connect to gateway",
async fn() {
const token = Deno.env.get("DISCORD_TOKEN");
if (!token) throw new Error("Token is not provided");
await startBot({
token,
intents: ["GUILD_MESSAGES", "GUILDS"],
});
// Delay the execution by 5 seconds
await delay(5000);
// Assertions
assertExists(botId);
},
...defaultTestOptions,
});
// Guild
// Deno.test({
// name: "[guild] create a new guild",
// async fn() {
// const guild = await createGuild({
// name: "Discordeno Test",
// }) as Guild;
// // Assertions
// assertExists(guild);
// tempData.guildId = guild.id;
// // Delay the execution by 5 seconds to allow GUILD_CREATE event to be processed
// await delay(5000);
// },
// ...defaultTestOptions,
// });
// // Role
// Deno.test({
// name: "[role] create a role in a guild",
// async fn() {
// if (!tempData.guildId) {
// throw new Error("guildId not present in temporary data");
// }
// const name = "Discordeno Test";
// const role = await createRole(tempData.guildId, {
// name,
// });
// // Assertions
// assertExists(role);
// assertEquals(role.name, name);
// tempData.roleId = role.id;
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[role] edit a role in a guild",
// async fn() {
// const name = "Discordeno Test Edited";
// const color = 4320244;
// const role = await editRole(tempData.guildId, tempData.roleId, {
// name,
// color,
// hoist: false,
// mentionable: false,
// }) as Role;
// // Assertions
// assertExists(role);
// assertEquals(role.name, name);
// assertEquals(role.color, color);
// assertEquals(role.hoist, false);
// assertEquals(role.mentionable, false);
// tempData.roleId = role.id;
// },
// ...defaultTestOptions,
// });
// // Channel
// Deno.test({
// name: "[channel] create a channel in a guild",
// async fn() {
// const channel = await createChannel(tempData.guildId, "test");
// // Assertions
// assertExists(channel);
// tempData.channelId = channel.id;
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[channel] get a channel in a guild",
// async fn() {
// const channel = await getChannel(tempData.channelId);
// // Assertions
// assertExists(channel);
// assertEquals(channel.id, tempData.channelId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[channel] edit a channel in a guild",
// async fn() {
// const channel = await editChannel(tempData.channelId, {
// name: "discordeno-test-edited",
// overwrites: [
// {
// id: tempData.roleId,
// type: OverwriteType.ROLE,
// allow: ["VIEW_CHANNEL", "SEND_MESSAGES"],
// deny: ["USE_EXTERNAL_EMOJIS"],
// },
// ],
// }) as Channel;
// // Wait 5s for CHANNEL_UPDATE to fire
// await delay(5000);
// // Assertions
// assertExists(channel);
// assertEquals(channel.name, "discordeno-test-edited");
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[channel] channel overwrite has permission",
// fn() {
// const channel = cache.channels.get(tempData.channelId);
// if (!channel) throw new Error("Channel not found");
// if (!channel.permissionOverwrites) {
// throw new Error("permissionOverwrites not found");
// }
// const hasPerm = channelOverwriteHasPermission(
// tempData.guildId,
// tempData.roleId,
// channel.permissionOverwrites,
// ["VIEW_CHANNEL", "SEND_MESSAGES"],
// );
// const missingPerm = channelOverwriteHasPermission(
// tempData.guildId,
// tempData.roleId,
// channel.permissionOverwrites,
// ["USE_EXTERNAL_EMOJIS"],
// );
// assertEquals(hasPerm, true);
// assertEquals(missingPerm, false);
// },
// ...defaultTestOptions,
// });
// // Message
// Deno.test({
// name: "[message] send a message in a text channel",
// async fn() {
// const message = await sendMessage(tempData.channelId, {
// embed: {
// title: "Discordeno Test",
// },
// });
// // Assertions
// assertExists(message);
// assertEquals(message.embeds[0].title, "Discordeno Test");
// tempData.messageId = message.id;
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[message] get a message in a guild",
// async fn() {
// const message = await getMessage(tempData.channelId, tempData.messageId);
// // Assertions
// assertExists(message);
// assertEquals(message.embeds[0].title, "Discordeno Test");
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[message] pin a message in a channel",
// async fn() {
// await pinMessage(tempData.channelId, tempData.messageId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[message] get pinned message in a channel",
// async fn() {
// const [msg] = await getPins(tempData.channelId);
// // Assertions
// assertExists(msg);
// assertEquals(msg.id, tempData.messageId);
// assertEquals(msg.pinned, true);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[message] unpin a message",
// async fn() {
// await unpinMessage(tempData.channelId, tempData.messageId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[message] add a reaction to a message",
// async fn() {
// // TODO: add tests for a guild emoji ― <:name:id>
// await addReaction(tempData.channelId, tempData.messageId, "👍");
// },
// ...defaultTestOptions,
// });
// // TODO(ayntee): add unit tests for getReactions()
// Deno.test({
// name: "[message] remove a reaction to a message",
// async fn() {
// await removeReaction(tempData.channelId, tempData.messageId, "👍");
// },
// ...defaultTestOptions,
// });
// // Cleanup
// Deno.test({
// name: "[message] delete a message by channel Id",
// async fn() {
// await deleteMessage(tempData.channelId, tempData.messageId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[channel] delete a channel in a guild",
// async fn() {
// await deleteChannel(tempData.guildId, tempData.channelId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[role] delete a role in a guild",
// async fn() {
// await deleteRole(tempData.guildId, tempData.roleId);
// },
// ...defaultTestOptions,
// });
// Deno.test({
// name: "[guild] delete a guild",
// async fn() {
// await deleteServer(tempData.guildId);
// // TODO(ayntee): remove this weird shit lol
// // TODO(ayntee): check if the GUILD_DELETE event is fired
// tempData.guildId = "";
// assertEquals(tempData.guildId, "");
// },
// ...defaultTestOptions,
// });
// Forcefully exit the Deno process once all tests are done.
Deno.test({
name: "[main] exit the process forcefully",
fn() {
ws.shards.forEach((shard) => {
clearInterval(shard.heartbeat.intervalId);
shard.ws.close();
});
},
...defaultTestOptions,
});