mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-04 01:40:08 +00:00
Merge branch 'master' of https://github.com/Skillz4Killz/Discordeno into unit-tests
This commit is contained in:
@@ -56,9 +56,7 @@ The instructions below are meant for advanced developers!
|
||||
Starting with Discordeno is very simple, you can start from scratch without any boilerplates/frameworks: Add this snippet of code into a new TypeScript file:
|
||||
|
||||
```typescript
|
||||
import StartBot from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts";
|
||||
import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts";
|
||||
import { Intents } from "https://x.nest.land/Discordeno@9.0.1/src/types/options.ts";
|
||||
import StartBot, { sendMessage, Intents } from "https://x.nest.land/Discordeno@9.0.1/mod.ts";
|
||||
import config from "./config.ts";
|
||||
|
||||
StartBot({
|
||||
|
||||
2
deps.ts
2
deps.ts
@@ -12,4 +12,4 @@ export {
|
||||
isWebSocketPongEvent,
|
||||
} from "https://deno.land/std@0.67.0/ws/mod.ts";
|
||||
export type { WebSocket } from "https://deno.land/std@0.67.0/ws/mod.ts";
|
||||
export { inflate } from "https://deno.land/x/zlib.es@v1.0.0/mod.ts";
|
||||
export { decompress_with as inflate } from "https://unpkg.com/@evan/wasm@0.0.11/target/zlib/deno.js";
|
||||
|
||||
@@ -54,43 +54,44 @@ const nekosEndpoints = [
|
||||
{ name: "gecg", path: "/img/gecg", nsfw: false },
|
||||
{ name: "avatar", path: "/img/avatar", nsfw: false },
|
||||
{ name: "waifu", path: "/img/waifu", nsfw: false },
|
||||
{ name: "randomHentaiGif", path: "/img/Random_hentai_gif", nsfw: true },
|
||||
{ name: "pussy", path: "/img/pussy", nsfw: true },
|
||||
{ name: "nekoGif", path: "/img/nsfw_neko_gif", nsfw: true },
|
||||
{ name: "neko", path: "/img/lewd", nsfw: true },
|
||||
{ name: "lesbian", path: "/img/les", nsfw: true },
|
||||
{ name: "kuni", path: "/img/kuni", nsfw: true },
|
||||
{ name: "cumsluts", path: "/img/cum", nsfw: true },
|
||||
{ name: "classic", path: "/img/classic", nsfw: true },
|
||||
{ name: "boobs", path: "/img/boobs", nsfw: true },
|
||||
{ name: "bJ", path: "/img/bj", nsfw: true },
|
||||
{ name: "anal", path: "/img/anal", nsfw: true },
|
||||
{ name: "avatar", path: "/img/nsfw_avatar", nsfw: true },
|
||||
{ name: "yuri", path: "/img/yuri", nsfw: true },
|
||||
{ name: "trap", path: "/img/trap", nsfw: true },
|
||||
{ name: "tits", path: "/img/tits", nsfw: true },
|
||||
{ name: "girlSoloGif", path: "/img/solog", nsfw: true },
|
||||
{ name: "girlSolo", path: "/img/solo", nsfw: true },
|
||||
{ name: "pussyWankGif", path: "/img/pwankg", nsfw: true },
|
||||
{ name: "pussyArt", path: "/img/pussy_jpg", nsfw: true },
|
||||
{ name: "kemonomimi", path: "/img/lewdkemo", nsfw: true },
|
||||
{ name: "kitsune", path: "/img/lewdk", nsfw: true },
|
||||
{ name: "keta", path: "/img/keta", nsfw: true },
|
||||
{ name: "holo", path: "/img/hololewd", nsfw: true },
|
||||
{ name: "holoEro", path: "/img/holoero", nsfw: true },
|
||||
{ name: "hentai", path: "/img/hentai", nsfw: true },
|
||||
{ name: "futanari", path: "/img/futanari", nsfw: true },
|
||||
{ name: "femdom", path: "/img/femdom", nsfw: true },
|
||||
{ name: "feetGif", path: "/img/feetg", nsfw: true },
|
||||
{ name: "eroFeet", path: "/img/erofeet", nsfw: true },
|
||||
{ name: "feet", path: "/img/feet", nsfw: true },
|
||||
{ name: "ero", path: "/img/ero", nsfw: true },
|
||||
{ name: "eroKitsune", path: "/img/erok", nsfw: true },
|
||||
{ name: "eroKemonomimi", path: "/img/erokemo", nsfw: true },
|
||||
{ name: "eroNeko", path: "/img/eron", nsfw: true },
|
||||
{ name: "eroYuri", path: "/img/eroyuri", nsfw: true },
|
||||
{ name: "cumArts", path: "/img/cum_jpg", nsfw: true },
|
||||
{ name: "blowJob", path: "/img/blowjob", nsfw: true },
|
||||
// The follow name and paths have been hidden for this guide as they are NSFW.
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
];
|
||||
|
||||
nekosEndpoints.forEach((endpoint) => {
|
||||
|
||||
@@ -73,8 +73,10 @@ Alternatively, you can use boilerplate template repositories that were created b
|
||||
| -------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| Official Boilerplate | Skillz4Killz#4500 | [GitHub](https://github.com/Skillz4Killz/Discordeno-bot-template), [Support Server](https://discord.gg/J4NqJ72) | This is a very minimalistic design for a boilerplate for your bot to get you started. |
|
||||
| DenoBot | NTM Nathan#0001 | [GitHub](https://github.com/ntm-development/DenoBot), [Support Server](https://discord.com/invite/G2rb53z) | Another boilerplate example of the first one, with more commands and improvements. |
|
||||
| Discordeno Helper | Suyashtnt | [Github](https://github.com/Suyashtnt/discordeno-helper-template/) | A reimplementation of DenoBot using the [discordeno-helper](https://github.com/Suyashtnt/discordeno-helper) framework
|
||||
|
||||
**Open Sourced Bots:**
|
||||
|
||||
Open Sourced Bots:
|
||||
| Bot Name | Developer | Links |
|
||||
| ----------------- | ---------- | ---------------------------------------------------------- |
|
||||
| discordeno-mattis | Mattis6666 | [Github](https://github.com/Mattis6666/discordeno-mattis/) |
|
||||
|
||||
@@ -19,6 +19,12 @@ Discordeno is a Third Party Deno Library for interacting with the Discord API.
|
||||
- Latest and Greatest JavaScript
|
||||
- Actively Maintained!
|
||||
|
||||
### User Reviews
|
||||
|
||||
If you wish to leave a review for other users, please send a PR adding your review to this section!
|
||||
|
||||
Using the Discord API with types but such a simple language like TypeScript is so easy now. Discordeno is A W E S O M E! -[LukasDoesDev](https://github.com/LukasDoesDev)
|
||||
|
||||
## Read me first...
|
||||
Discordeno is cool right? You could make the next big bot! Who knows, but before we get right into developing our Bot. We want to get started with learning the basics...
|
||||
|
||||
|
||||
6
egg.yml
6
egg.yml
@@ -2,9 +2,9 @@ name: Discordeno
|
||||
description: >-
|
||||
Discord Deno TypeScript API library wrapper(Officially vetted library by
|
||||
Discord Team) https://discordeno.netlify.app
|
||||
version: 9.0.1
|
||||
version: 9.0.5
|
||||
stable: true
|
||||
entry: /mod.ts
|
||||
entry: mod.ts
|
||||
repository: 'https://github.com/Skillz4Killz/Discordeno'
|
||||
files:
|
||||
- ./src/**/*
|
||||
@@ -13,7 +13,5 @@ files:
|
||||
- README.md
|
||||
- tsconfig.json
|
||||
- ./deps.ts
|
||||
- ./mod.ts
|
||||
- ./mod.ts
|
||||
checkAll: false
|
||||
unlisted: false
|
||||
|
||||
@@ -41,11 +41,6 @@ export async function handleInternalGuildMemberRemove(data: DiscordPayload) {
|
||||
member || payload.user,
|
||||
);
|
||||
|
||||
eventHandlers.guildMemberRemove?.(
|
||||
guild,
|
||||
member || payload.user,
|
||||
);
|
||||
|
||||
guild.members.delete(payload.user.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ export async function handleInternalGuildRoleDelete(data: DiscordPayload) {
|
||||
const cachedRole = guild.roles.get(payload.role_id)!;
|
||||
guild.roles.delete(payload.role_id);
|
||||
eventHandlers.roleDelete?.(guild, cachedRole);
|
||||
|
||||
// For bots without GUILD_MEMBERS member.roles is never updated breaking permissions checking.
|
||||
guild.members.forEach(member => {
|
||||
member.roles = member.roles.filter(id => id !== payload.role_id);
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleInternalGuildRoleUpdate(data: DiscordPayload) {
|
||||
|
||||
@@ -7,33 +7,23 @@ import type { Guild } from "../structures/guild.ts";
|
||||
import type { Member } from "../structures/member.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import {
|
||||
ChannelCreatePayload,
|
||||
ChannelTypes,
|
||||
} from "../types/channel.ts";
|
||||
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type {
|
||||
BannedUser,
|
||||
BanOptions,
|
||||
|
||||
ChannelCreateOptions,
|
||||
CreateEmojisOptions,
|
||||
|
||||
CreateRoleOptions,
|
||||
|
||||
CreateServerOptions,
|
||||
EditEmojisOptions,
|
||||
|
||||
EditIntegrationOptions,
|
||||
FetchMembersOptions,
|
||||
GetAuditLogsOptions,
|
||||
|
||||
GuildEditOptions,
|
||||
PositionSwap,
|
||||
|
||||
PruneOptions,
|
||||
PrunePayload,
|
||||
|
||||
UserPayload,
|
||||
} from "../types/guild.ts";
|
||||
import type { MemberCreatePayload } from "../types/member.ts";
|
||||
|
||||
@@ -14,17 +14,16 @@ import type {
|
||||
} from "../types/discord.ts";
|
||||
import { GatewayOpcode } from "../types/discord.ts";
|
||||
import type { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import type { BotStatusRequest } from "../utils/utils.ts";
|
||||
import type { IdentifyPayload } from "./client.ts";
|
||||
import {
|
||||
botGatewayData,
|
||||
eventHandlers,
|
||||
} from "./client.ts";
|
||||
import { botGatewayData, eventHandlers } from "./client.ts";
|
||||
import { handleDiscordPayload } from "./shardingManager.ts";
|
||||
|
||||
export const basicShards = new Collection<number, BasicShard>();
|
||||
const heartbeating = new Set<number>();
|
||||
const basicShards = new Map<number, BasicShard>();
|
||||
const heartbeating = new Map<number, boolean>();
|
||||
const utf8decoder = new TextDecoder();
|
||||
const RequestMembersQueue: RequestMemberQueuedRequest[] = [];
|
||||
let processQueue = false;
|
||||
|
||||
export interface BasicShard {
|
||||
id: number;
|
||||
@@ -35,9 +34,6 @@ export interface BasicShard {
|
||||
needToResume: boolean;
|
||||
}
|
||||
|
||||
const RequestMembersQueue: RequestMemberQueuedRequest[] = [];
|
||||
let processQueue = false;
|
||||
|
||||
interface RequestMemberQueuedRequest {
|
||||
guildID: string;
|
||||
shardID: number;
|
||||
@@ -110,7 +106,11 @@ export async function createBasicShard(
|
||||
}
|
||||
|
||||
if (message instanceof Uint8Array) {
|
||||
message = new TextDecoder().decode(inflate(message as Uint8Array));
|
||||
message = inflate(
|
||||
message,
|
||||
0,
|
||||
(slice: Uint8Array) => utf8decoder.decode(slice),
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof message === "string") {
|
||||
@@ -122,9 +122,13 @@ export async function createBasicShard(
|
||||
heartbeat(
|
||||
basicShard,
|
||||
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
|
||||
identifyPayload,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case GatewayOpcode.HeartbeatACK:
|
||||
heartbeating.set(shardID, true);
|
||||
break;
|
||||
case GatewayOpcode.Reconnect:
|
||||
eventHandlers.debug?.(
|
||||
{ type: "reconnect", data: { shardID: basicShard.id } },
|
||||
@@ -199,17 +203,39 @@ function resume(shard: BasicShard, payload: IdentifyPayload) {
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO: If a client does not receive a heartbeat ack between its attempts at sending heartbeats, it should immediately terminate the connection with a non-1000 close code, reconnect, and attempt to resume.
|
||||
async function heartbeat(
|
||||
shard: BasicShard,
|
||||
interval: number,
|
||||
payload: IdentifyPayload,
|
||||
) {
|
||||
// We lost socket connection between heartbeats, resume connection
|
||||
if (shard.socket.isClosed) {
|
||||
shard.needToResume = true;
|
||||
resumeConnection(botGatewayData, payload, shard.id);
|
||||
heartbeating.delete(shard.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!heartbeating.has(shard.id)) heartbeating.add(shard.id);
|
||||
if (heartbeating.has(shard.id)) {
|
||||
const receivedACK = heartbeating.get(shard.id);
|
||||
// If a ACK response was not received since last heartbeat, issue invalid session close
|
||||
if (!receivedACK) {
|
||||
eventHandlers.debug?.(
|
||||
{
|
||||
type: "heartbeatStopped",
|
||||
data: {
|
||||
interval,
|
||||
previousSequenceNumber: shard.previousSequenceNumber,
|
||||
shardID: shard.id,
|
||||
},
|
||||
},
|
||||
);
|
||||
return shard.socket.send(JSON.stringify({ op: 4009 }));
|
||||
}
|
||||
}
|
||||
|
||||
// Set it to false as we are issuing a new heartbeat
|
||||
heartbeating.set(shard.id, false);
|
||||
|
||||
shard.socket.send(
|
||||
JSON.stringify(
|
||||
@@ -227,7 +253,7 @@ async function heartbeat(
|
||||
},
|
||||
);
|
||||
await delay(interval);
|
||||
heartbeat(shard, interval);
|
||||
heartbeat(shard, interval, payload);
|
||||
}
|
||||
|
||||
async function resumeConnection(
|
||||
|
||||
@@ -15,12 +15,7 @@ import {
|
||||
requestGuildMembers,
|
||||
} from "./basicShard.ts";
|
||||
import type { IdentifyPayload } from "./client.ts";
|
||||
import {
|
||||
botGatewayData,
|
||||
eventHandlers,
|
||||
|
||||
identifyPayload,
|
||||
} from "./client.ts";
|
||||
import { botGatewayData, eventHandlers, identifyPayload } from "./client.ts";
|
||||
|
||||
let shardCounter = 0;
|
||||
let basicSharding = false;
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function createChannel(
|
||||
const channel = {
|
||||
...rest,
|
||||
/** The guild id of the channel if it is a guild channel. */
|
||||
guildID: guildID || rawGuildID,
|
||||
guildID: guildID || rawGuildID || "",
|
||||
/** The id of the last message sent in this channel */
|
||||
lastMessageID,
|
||||
/** The amount of users allowed in this voice channel. */
|
||||
|
||||
@@ -18,4 +18,6 @@ export enum ActivityType {
|
||||
Listening,
|
||||
/** Example: ":smiley: I am cool" */
|
||||
Custom = 4,
|
||||
/** Example: "Competing in Arena World Champions" */
|
||||
Competing,
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import type { Role } from "../structures/role.ts";
|
||||
import type {
|
||||
DiscordPayload,
|
||||
Emoji,
|
||||
|
||||
PresenceUpdatePayload,
|
||||
Properties,
|
||||
|
||||
TypingStartPayload,
|
||||
VoiceStateUpdatePayload,
|
||||
} from "./discord.ts";
|
||||
@@ -17,10 +15,8 @@ import type { UserPayload } from "./guild.ts";
|
||||
import type {
|
||||
Attachment,
|
||||
BaseMessageReactionPayload,
|
||||
|
||||
Embed,
|
||||
MessageReactionRemoveEmojiPayload,
|
||||
|
||||
MessageReactionUncachedPayload,
|
||||
PartialMessage,
|
||||
ReactionPayload,
|
||||
@@ -68,6 +64,7 @@ export interface DebugArg {
|
||||
| "requestManagerFetched"
|
||||
| "requestMembersProcessing"
|
||||
| "heartbeat"
|
||||
| "heartbeatStopped"
|
||||
| "createShard"
|
||||
| "invalidSession"
|
||||
| "reconnect"
|
||||
|
||||
@@ -58,6 +58,8 @@ export async function botHasPermission(
|
||||
|
||||
const permissionBits = member.roles
|
||||
.map((id) => guild.roles.get(id)!)
|
||||
// Remove any edge case undefined
|
||||
.filter((r) => r)
|
||||
.reduce((bits, data) => {
|
||||
bits |= BigInt(data.permissions);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user