From d37f124317525d27a98b875025e9c2101c13a8ec Mon Sep 17 00:00:00 2001 From: Androz Date: Tue, 26 May 2020 15:36:20 +0200 Subject: [PATCH 1/5] Add a simple ping command example to README --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 2b886ca89..7759474e1 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,32 @@ If you are just starting out, you can use the Discordeno Template repo to get th | 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. | +If you would like to start from scratch without any boilerplate/framework: + +```ts +import Client from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/module/client.ts"; +import { Intents } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/types/options.ts"; +import { Message } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/structures/message.ts"; + +const BotOptions = { + token: "token", + botID: "client ID", + intents: [Intents.GUILD_MESSAGES], + eventHandlers: { + ready: () => { + console.log(`Logged!`); + }, + messageCreate: (message: Message) => { + if(message.content === "!ping"){ + message.channel.sendMessage("Pong!"); + } + } + } +}; + +Client(BotOptions); +``` + ## Open Source Bots Using Discordeno | Bot Name | Developer | Links | From f43c348b6c59d5e4ad95e83679454bfe86c67ebb Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Tue, 26 May 2020 09:45:40 -0400 Subject: [PATCH 2/5] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7759474e1..a008183b3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ If you would like to start from scratch without any boilerplate/framework: ```ts import Client from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/module/client.ts"; import { Intents } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/types/options.ts"; -import { Message } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/structures/message.ts"; const BotOptions = { token: "token", @@ -28,7 +27,7 @@ const BotOptions = { ready: () => { console.log(`Logged!`); }, - messageCreate: (message: Message) => { + messageCreate: (message) => { if(message.content === "!ping"){ message.channel.sendMessage("Pong!"); } From e87dff66ac0e3702a5d65eb7049e69422648f04d Mon Sep 17 00:00:00 2001 From: Androz2091 Date: Tue, 26 May 2020 16:00:31 +0200 Subject: [PATCH 3/5] Replace Map with Collection --- structures/guild.ts | 11 +- utils/cache.ts | 17 ++-- utils/collection.ts | 239 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 utils/collection.ts diff --git a/structures/guild.ts b/structures/guild.ts index dd0c95287..45df63a94 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,3 +1,4 @@ +import Collection from "../utils/collection.ts"; import { identifyPayload } from "../module/client.ts"; import { endpoints } from "../constants/discord.ts"; import { formatImageURL } from "../utils/cdn.ts"; @@ -66,21 +67,21 @@ export const createGuild = (data: CreateGuildPayload, shardID: number) => { preferredLocale: data.preferred_locale, /** The roles in the guild */ - roles: new Map(data.roles.map((r) => [r.id, createRole(r)])), + roles: new Collection(data.roles.map((r) => [r.id, createRole(r)])), /** When this guild was joined at. */ joinedAt: Date.parse(data.joined_at), /** The users in this guild. */ - members: new Map(), + members: new Collection(), /** The channels in the guild */ - channels: new Map( + channels: new Collection( data.channels.map((c) => [c.id, createChannel(c, data.id)]), ), /** The presences of all the users in the guild. */ - presences: new Map(data.presences.map((p) => [p.user.id, p])), + presences: new Collection(data.presences.map((p) => [p.user.id, p])), /** The total number of members in this guild. This value is updated as members leave and join the server. However, if you do not have the intent enabled to be able to listen to these events, then this will not be accurate. */ memberCount: data.member_count || 0, /** The Voice State data for each user in a voice channel in this server. */ - voiceStates: new Map(data.voice_states.map((vs) => [vs.user_id, { + voiceStates: new Collection(data.voice_states.map((vs) => [vs.user_id, { ...vs, guildID: vs.guild_id, channelID: vs.channel_id, diff --git a/utils/cache.ts b/utils/cache.ts index c2d576b79..ab01cfe63 100644 --- a/utils/cache.ts +++ b/utils/cache.ts @@ -1,17 +1,18 @@ +import Collection from "./collection.ts"; import { Message } from "../structures/message.ts"; import { Guild } from "../structures/guild.ts"; import { Channel } from "../structures/channel.ts"; export interface CacheData { - guilds: Map; - channels: Map; - messages: Map; - unavailableGuilds: Map; + guilds: Collection; + channels: Collection; + messages: Collection; + unavailableGuilds: Collection; } export const cache: CacheData = { - guilds: new Map(), - channels: new Map(), - messages: new Map(), - unavailableGuilds: new Map(), + guilds: new Collection(), + channels: new Collection(), + messages: new Collection(), + unavailableGuilds: new Collection(), }; diff --git a/utils/collection.ts b/utils/collection.ts new file mode 100644 index 000000000..e23a107cf --- /dev/null +++ b/utils/collection.ts @@ -0,0 +1,239 @@ +class Collection extends Map { + private _array!: V[] | null; + public static readonly default: typeof Collection = Collection; + public ["constructor"]: typeof Collection; + + public constructor(entries?: ReadonlyArray | null) { + super(entries); + + Object.defineProperty( + this, + "_array", + { value: null, writable: true, configurable: true }, + ); + + Object.defineProperty( + this, + "_keyArray", + { value: null, writable: true, configurable: true }, + ); + } + + public get(key: K): V | undefined { + return super.get(key); + } + + public set(key: K, value: V): this { + this._array = null; + return super.set(key, value); + } + + public has(key: K): boolean { + return super.has(key); + } + + public delete(key: K): boolean { + this._array = null; + return super.delete(key); + } + + public clear(): void { + return super.clear(); + } + + public array(): V[] { + if (!this._array || this._array.length !== this.size) { + this._array = [...this.values()]; + } + return this._array; + } + + public first(): V | undefined; + public first(amount: number): V[]; + public first(amount?: number): V | V[] | undefined { + if (typeof amount === "undefined") return this.values().next().value; + if (amount < 0) return this.last(amount * -1); + amount = Math.min(this.size, amount); + const iter = this.values(); + return Array.from({ length: amount }, (): V => iter.next().value); + } + + public last(): V | undefined; + public last(amount: number): V[]; + public last(amount?: number): V | V[] | undefined { + const arr = this.array(); + if (typeof amount === "undefined") return arr[arr.length - 1]; + if (amount < 0) return this.first(amount * -1); + if (!amount) return []; + return arr.slice(-amount); + } + + public random(): V; + public random(amount: number): V[]; + public random(amount?: number): V | V[] { + let arr = this.array(); + if ( + typeof amount === "undefined" + ) { + return arr[Math.floor(Math.random() * arr.length)]; + } + if (arr.length === 0 || !amount) return []; + arr = arr.slice(); + return Array.from( + { length: amount }, + (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], + ); + } + + public find( + fn: (value: V, key: K, collection: this) => boolean, + ): V | undefined; + public find( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T, + ): V | undefined; + public find( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown, + ): V | undefined { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) return val; + } + return undefined; + } + + public filter(fn: (value: V, key: K, collection: this) => boolean): this; + public filter( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T, + ): this; + public filter( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown, + ): this { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const results = new this.constructor[Symbol.species]() as this; + for (const [key, val] of this) { + if (fn(val, key, this)) results.set(key, val); + } + return results; + } + + public map(fn: (value: V, key: K, collection: this) => T): T[]; + public map( + fn: (this: This, value: V, key: K, collection: this) => T, + thisArg: This, + ): T[]; + public map( + fn: (value: V, key: K, collection: this) => T, + thisArg?: unknown, + ): T[] { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const iter = this.entries(); + return Array.from({ length: this.size }, (): T => { + const [key, value] = iter.next().value; + return fn(value, key, this); + }); + } + + public some(fn: (value: V, key: K, collection: this) => boolean): boolean; + public some( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T, + ): boolean; + public some( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown, + ): boolean { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) return true; + } + return false; + } + + public every(fn: (value: V, key: K, collection: this) => boolean): boolean; + public every( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T, + ): boolean; + public every( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown, + ): boolean { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (!fn(val, key, this)) return false; + } + return true; + } + + public reduce( + fn: (accumulator: T, value: V, key: K, collection: this) => T, + initialValue?: T, + ): T { + let accumulator!: T; + + if (typeof initialValue !== "undefined") { + accumulator = initialValue; + for (const [key, val] of this) { + accumulator = fn(accumulator, val, key, this); + } + return accumulator; + } + let first = true; + for (const [key, val] of this) { + if (first) { + accumulator = val as unknown as T; + first = false; + continue; + } + accumulator = fn(accumulator, val, key, this); + } + + // No items iterated. + if (first) { + throw new TypeError("Reduce of empty collection with no initial value"); + } + + return accumulator; + } + + public each(fn: (value: V, key: K, collection: this) => void): this; + public each( + fn: (this: T, value: V, key: K, collection: this) => void, + thisArg: T, + ): this; + public each( + fn: (value: V, key: K, collection: this) => void, + thisArg?: unknown, + ): this { + this.forEach(fn as (value: V, key: K, map: Map) => void, thisArg); + return this; + } + + public sort( + compareFunction: ( + firstValue: V, + secondValue: V, + firstKey: K, + secondKey: K, + ) => number = (x, y): number => Number(x > y) || Number(x === y) - 1, + ): this { + const entries = [...this.entries()]; + entries.sort((a, b): number => compareFunction(a[1], b[1], a[0], b[0])); + + // Perform clean-up + super.clear(); + this._array = null; + + // Set the new entries + for (const [k, v] of entries) { + super.set(k, v); + } + return this; + } +} + +export default Collection; From 941155045bddd54cf8b83adaee4120640fd014e1 Mon Sep 17 00:00:00 2001 From: Androz2091 Date: Tue, 26 May 2020 16:17:34 +0200 Subject: [PATCH 4/5] some fmt fixes --- structures/message.ts | 4 +--- utils/cdn.ts | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/structures/message.ts b/structures/message.ts index 0e4ee6196..4ebd7145e 100644 --- a/structures/message.ts +++ b/structures/message.ts @@ -27,9 +27,7 @@ export function createMessage(data: MessageCreateOptions) { guild: () => data.guild_id ? cache.guilds.get(data.guild_id) : undefined, member: () => message.guild()?.members.get(data.author.id)!, mentions: () => - data.mentions.map((mention) => - message.guild()?.members.get(mention.id)! - ), + data.mentions.map((mention) => message.guild()?.members.get(mention.id)!), /** Delete a message */ delete: (reason?: string) => { diff --git a/utils/cdn.ts b/utils/cdn.ts index beebb1344..d93c5e1fe 100644 --- a/utils/cdn.ts +++ b/utils/cdn.ts @@ -5,5 +5,6 @@ export const formatImageURL = ( size: ImageSize = 128, format?: ImageFormats, ) => { - return `${url}.${format || (url.includes("/a_") ? "gif" : "jpg")}?size=${size}`; + return `${url}.${format || + (url.includes("/a_") ? "gif" : "jpg")}?size=${size}`; }; From 2bc3edf8e56c623466abcb76f610cf103bbcd775 Mon Sep 17 00:00:00 2001 From: Androz2091 Date: Tue, 26 May 2020 17:52:19 +0200 Subject: [PATCH 5/5] collection rewrite --- utils/collection.ts | 249 ++++++++------------------------------------ utils/utils.ts | 4 + 2 files changed, 48 insertions(+), 205 deletions(-) diff --git a/utils/collection.ts b/utils/collection.ts index e23a107cf..98d4cf96f 100644 --- a/utils/collection.ts +++ b/utils/collection.ts @@ -1,239 +1,78 @@ -class Collection extends Map { - private _array!: V[] | null; - public static readonly default: typeof Collection = Collection; - public ["constructor"]: typeof Collection; +import { chooseRandom } from "./utils.ts"; - public constructor(entries?: ReadonlyArray | null) { - super(entries); - - Object.defineProperty( - this, - "_array", - { value: null, writable: true, configurable: true }, - ); - - Object.defineProperty( - this, - "_keyArray", - { value: null, writable: true, configurable: true }, - ); +export default class Collection extends Map { + array() { + return [...this.values()]; } - public get(key: K): V | undefined { - return super.get(key); + first() { + return this.values().next().value; } - public set(key: K, value: V): this { - this._array = null; - return super.set(key, value); + last() { + return [...this.values()][this.size - 1]; } - public has(key: K): boolean { - return super.has(key); + random() { + return chooseRandom([...this.values()]); } - public delete(key: K): boolean { - this._array = null; - return super.delete(key); - } - - public clear(): void { - return super.clear(); - } - - public array(): V[] { - if (!this._array || this._array.length !== this.size) { - this._array = [...this.values()]; + find(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (callback(value, key)) return value; } - return this._array; + // If nothing matched + return; } - public first(): V | undefined; - public first(amount: number): V[]; - public first(amount?: number): V | V[] | undefined { - if (typeof amount === "undefined") return this.values().next().value; - if (amount < 0) return this.last(amount * -1); - amount = Math.min(this.size, amount); - const iter = this.values(); - return Array.from({ length: amount }, (): V => iter.next().value); + filter(callback: (value: V, key: K) => boolean) { + const relevant = new Collection(); + this.forEach((value, key) => { + if (callback(value, key)) relevant.set(key, value); + }); + + return relevant; } - public last(): V | undefined; - public last(amount: number): V[]; - public last(amount?: number): V | V[] | undefined { - const arr = this.array(); - if (typeof amount === "undefined") return arr[arr.length - 1]; - if (amount < 0) return this.first(amount * -1); - if (!amount) return []; - return arr.slice(-amount); - } - - public random(): V; - public random(amount: number): V[]; - public random(amount?: number): V | V[] { - let arr = this.array(); - if ( - typeof amount === "undefined" - ) { - return arr[Math.floor(Math.random() * arr.length)]; - } - if (arr.length === 0 || !amount) return []; - arr = arr.slice(); - return Array.from( - { length: amount }, - (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], - ); - } - - public find( - fn: (value: V, key: K, collection: this) => boolean, - ): V | undefined; - public find( - fn: (this: T, value: V, key: K, collection: this) => boolean, - thisArg: T, - ): V | undefined; - public find( - fn: (value: V, key: K, collection: this) => boolean, - thisArg?: unknown, - ): V | undefined { - if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (fn(val, key, this)) return val; - } - return undefined; - } - - public filter(fn: (value: V, key: K, collection: this) => boolean): this; - public filter( - fn: (this: T, value: V, key: K, collection: this) => boolean, - thisArg: T, - ): this; - public filter( - fn: (value: V, key: K, collection: this) => boolean, - thisArg?: unknown, - ): this { - if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); - const results = new this.constructor[Symbol.species]() as this; - for (const [key, val] of this) { - if (fn(val, key, this)) results.set(key, val); + map(callback: (value: V, key: K) => T) { + const results = []; + for (const key of this.keys()) { + const value = this.get(key)!; + results.push(callback(value, key)); } return results; } - public map(fn: (value: V, key: K, collection: this) => T): T[]; - public map( - fn: (this: This, value: V, key: K, collection: this) => T, - thisArg: This, - ): T[]; - public map( - fn: (value: V, key: K, collection: this) => T, - thisArg?: unknown, - ): T[] { - if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); - const iter = this.entries(); - return Array.from({ length: this.size }, (): T => { - const [key, value] = iter.next().value; - return fn(value, key, this); - }); - } - - public some(fn: (value: V, key: K, collection: this) => boolean): boolean; - public some( - fn: (this: T, value: V, key: K, collection: this) => boolean, - thisArg: T, - ): boolean; - public some( - fn: (value: V, key: K, collection: this) => boolean, - thisArg?: unknown, - ): boolean { - if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (fn(val, key, this)) return true; + some(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (callback(value, key)) return true; } + return false; } - public every(fn: (value: V, key: K, collection: this) => boolean): boolean; - public every( - fn: (this: T, value: V, key: K, collection: this) => boolean, - thisArg: T, - ): boolean; - public every( - fn: (value: V, key: K, collection: this) => boolean, - thisArg?: unknown, - ): boolean { - if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (!fn(val, key, this)) return false; + every(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (!callback(value, key)) return false; } + return true; } - public reduce( - fn: (accumulator: T, value: V, key: K, collection: this) => T, + reduce( + callback: (accumulator: T, value: V, key: K) => T, initialValue?: T, ): T { - let accumulator!: T; + let accumulator: T = initialValue!; - if (typeof initialValue !== "undefined") { - accumulator = initialValue; - for (const [key, val] of this) { - accumulator = fn(accumulator, val, key, this); - } - return accumulator; - } - let first = true; - for (const [key, val] of this) { - if (first) { - accumulator = val as unknown as T; - first = false; - continue; - } - accumulator = fn(accumulator, val, key, this); - } - - // No items iterated. - if (first) { - throw new TypeError("Reduce of empty collection with no initial value"); + for (const key of this.keys()) { + const value = this.get(key)!; + accumulator = callback(accumulator, value, key); } return accumulator; } - - public each(fn: (value: V, key: K, collection: this) => void): this; - public each( - fn: (this: T, value: V, key: K, collection: this) => void, - thisArg: T, - ): this; - public each( - fn: (value: V, key: K, collection: this) => void, - thisArg?: unknown, - ): this { - this.forEach(fn as (value: V, key: K, map: Map) => void, thisArg); - return this; - } - - public sort( - compareFunction: ( - firstValue: V, - secondValue: V, - firstKey: K, - secondKey: K, - ) => number = (x, y): number => Number(x > y) || Number(x === y) - 1, - ): this { - const entries = [...this.entries()]; - entries.sort((a, b): number => compareFunction(a[1], b[1], a[0], b[0])); - - // Perform clean-up - super.clear(); - this._array = null; - - // Set the new entries - for (const [k, v] of entries) { - super.set(k, v); - } - return this; - } } - -export default Collection; diff --git a/utils/utils.ts b/utils/utils.ts index 6dd3098a0..e3b5f4cca 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -13,3 +13,7 @@ export function editBotsStatus( ) { sendGatewayCommand("EDIT_BOTS_STATUS", { status, game: { name, type } }); } + +export function chooseRandom(array: T[]) { + return array[Math.floor(Math.random() * array.length)]; +}