Merge branch 'master' of https://github.com/Skillz4Killz/Discordeno into unit-tests

This commit is contained in:
chroventer
2020-10-26 09:41:10 -07:00
15 changed files with 104 additions and 87 deletions

View File

@@ -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({

View File

@@ -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";

View File

@@ -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) => {

View File

@@ -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/) |

View File

@@ -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...

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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";

View File

@@ -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(

View File

@@ -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;

View File

@@ -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. */

View File

@@ -18,4 +18,6 @@ export enum ActivityType {
Listening,
/** Example: ":smiley: I am cool" */
Custom = 4,
/** Example: "Competing in Arena World Champions" */
Competing,
}

View File

@@ -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"

View File

@@ -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);