diff --git a/.github/workflows/nestland.yml b/.github/workflows/nestland.yml deleted file mode 100644 index fbb97d5d9..000000000 --- a/.github/workflows/nestland.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Ship Nest.Land - -on: - create: - ref_type: "tag" - -jobs: - release: - runs-on: ubuntu-latest - steps: - - name: Setup repo - uses: actions/checkout@v2 - - - name: Setup Deno - uses: denolib/setup-deno@v2 - - - name: Publish module - run: | - deno install -A --unstable https://x.nest.land/eggs@0.3.2/eggs.ts - eggs link ${{ secrets.NESTAPIKEY }} - eggs publish --yes --no-check --version $(git describe --tags $(git rev-list --tags --max-count=1)) diff --git a/LICENSE b/LICENSE index b0b3aac1f..83eeab559 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Skillz4Killz +Copyright (c) 2020 discordeno Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b1a409b7a..a930679de 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Discordeno -> Discord API library wrapper in Deno +> Discord API library for Deno -[![Discord](https://img.shields.io/discord/223909216866402304?color=7289da&logo=discord&logoColor=dark)](https://discord.gg/J4NqJ72) -![Lint](https://github.com/Skillz4Killz/Discordeno/workflows/Lint/badge.svg) -![Test](https://github.com/Skillz4Killz/Discordeno/workflows/Test/badge.svg) -[![nest badge](https://nest.land/badge.svg)](https://nest.land/package/Discordeno) +[![Discord](https://img.shields.io/discord/785384884197392384?color=7289da&logo=discord&logoColor=dark)](https://discord.com/invite/5vBgXk3UcZ) +![Lint](https://github.com/discordeno/discordeno/workflows/Lint/badge.svg) +![Test](https://github.com/discordeno/discordeno/workflows/Test/badge.svg) - First-class TypeScript & JavaScript support - Security & stable @@ -32,7 +31,7 @@ If you do not wish to use a boilerplate, you may continue reading. Here's a minimal example to get started with: ```typescript -import StartBot, { sendMessage, Intents } from "https://x.nest.land/Discordeno@9.4.0/mod.ts"; +import StartBot, { sendMessage, Intents } from "https://deno.land/x/discordeno@9.4.0/mod.ts"; StartBot({ token: "BOT TOKEN", @@ -51,21 +50,21 @@ StartBot({ ## Documentation - [Website](https://discordeno.mod.land) -- [Support server](https://discord.gg/J4NqJ72) -- [Contributing Guide](https://github.com/Skillz4Killz/Discordeno/blob/master/.github/CONTRIBUTING.md) +- [Support server](https://discord.com/invite/5vBgXk3UcZ) +- [Contributing Guide](https://github.com/discordeno/discordeno/blob/master/.github/CONTRIBUTING.md) ## Contributing ## Code of Conduct -Discordeno expects participants to adhere to our [Code of Conduct](https://github.com/Skillz4Killz/Discordeno/blob/master/.github/CODE_OF_CONDUCT.md). +Discordeno expects participants to adhere to our [Code of Conduct](https://github.com/discordeno/discordeno/blob/master/.github/CODE_OF_CONDUCT.md). ## Contributing Guide We appreciate your help! -Before contributing, please read the [Contributing Guide](https://github.com/Skillz4Killz/Discordeno/blob/master/.github/CONTRIBUTING.md). +Before contributing, please read the [Contributing Guide](https://github.com/discordeno/discordeno/blob/master/.github/CONTRIBUTING.md). ### License -[MIT © Skillz4Killz](https://github.com/Skillz4Killz/Discordeno/blob/master/LICENSE) +[MIT © discordeno](https://github.com/discordeno/discordeno/blob/master/LICENSE) diff --git a/docs/package.json b/docs/package.json index 0fa01815e..bfa9ea143 100755 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "name": "discordeno", "version": "0.0.1", "description": "Docs for Discordeno", - "repository": "https://github.com/Skillz4Killz/Discordeno", + "repository": "https://github.com/discordeno/discordeno", "scripts": { "docs:dev": "vuepress dev src --host localhost", "docs:build": "vuepress build src" diff --git a/docs/src/.vuepress/config.js b/docs/src/.vuepress/config.js index 65fb00817..8f99c7e8e 100755 --- a/docs/src/.vuepress/config.js +++ b/docs/src/.vuepress/config.js @@ -1,7 +1,7 @@ const { name, description, repository } = require("../../package"); const title = name[0].toUpperCase() + name.slice(1); -const discordLink = "https://discord.com/invite/J4NqJ72"; +const discordLink = "https://discord.com/invite/5vBgXk3UcZ"; module.exports = { base: `/`, @@ -28,7 +28,7 @@ module.exports = { theme: "yuu", themeConfig: { repo: repository, - docsDir: "src", + docsDir: "docs/src", editLinks: true, lastUpdated: true, sidebarDepth: 0, @@ -65,9 +65,9 @@ module.exports = { "/stepbystep/createbot", "/stepbystep/createcommand", "/stepbystep/createevent", - "/stepbystep/createinhibitor", "/stepbystep/createlanguage", "/stepbystep/createmonitor", + "/stepbystep/createinhibitor", "/stepbystep/createtask", "/stepbystep/hostingbot", ], diff --git a/docs/src/advanced/arguments.md b/docs/src/advanced/arguments.md index f5a212694..4c0dc6291 100644 --- a/docs/src/advanced/arguments.md +++ b/docs/src/advanced/arguments.md @@ -16,6 +16,8 @@ Discordeno comes with the most useful command arguments already built for you. T - `member` When you want a member object and the user can provide a member id, @mention or their username/nickname. - `number` When you want a number. - `role` When you want a role object. The user can provide the role id, mention or role name. +- `snowflake` When you just want a snowflake. A snowflake is an ID generation format from Twitter that Discord uses to make their IDs unique. These IDs do never change and are used to identify users, guilds, emojis, and more. +- `...snowflakes` When you want to check if the mentioned ID is a valid snowflake. Note that this is similar to ...string, it will take all coming arguments and check them. This should always be your FINAL argument. - `subcommand` When your command has subcommands. @@ -39,7 +41,7 @@ Suppose you wanted to make it possible so that the boolean argument could accept What if we also wanted to support, `yes` and `no`? Let's open up the `boolean.ts` file in the arguments folder and get started. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.arguments.set("boolean", { name: "boolean", @@ -55,7 +57,7 @@ botCache.arguments.set("boolean", { Simply update the code so it looks like the following: ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.arguments.set("boolean", { name: "boolean", @@ -80,7 +82,7 @@ First, we need to update the **CommandArgument** interface in `src/types/command - Once that's done, we can go and create the code for it. Now, lets create a new file in `src/arguments` folder called `url.ts` and paste the base snippet for a command argument below. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.arguments.set("argumentname", { name: "argumentname", @@ -93,7 +95,7 @@ botCache.arguments.set("argumentname", { First let's change the `argumentname` to be `url`. Then we can start adding the pseudo-code. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.arguments.set("url", { name: "url", @@ -111,7 +113,7 @@ botCache.arguments.set("url", { Now we can get started. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.arguments.set("url", { name: "url", @@ -163,6 +165,6 @@ arguments: [ ] ``` -We already covered using the arguments in a command in our guide when we created the role command, so we can skip that here. To re-read that you can check it out [here](https://discordeno.netlify.app/stepbystep/createcommand#arguments). +We already covered using the arguments in a command in our guide when we created the role command, so we can skip that here. To re-read that you can check it out [here](https://discordeno.mod.land/stepbystep/createcommand.html#arguments). Command arguments are an extremely powerful feature that can help make creating bots a lot easier. Discordeno provides extremely flexible command arguments. As a side benefit, command arguments are designed to be hot reloadable from the reload command. 🎉 diff --git a/docs/src/advanced/dockerhosting.md b/docs/src/advanced/dockerhosting.md index d9b187a8f..c899ae9de 100644 --- a/docs/src/advanced/dockerhosting.md +++ b/docs/src/advanced/dockerhosting.md @@ -2,11 +2,11 @@ Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code quickly, you can significantly reduce the delay between writing code and running it in production. -Learn more at https://docs.docker.com/get-started/ +Learn more [here](https://docs.docker.com/get-started/) ### Installing Docker -Installing Docker is very simple and supported on nearly every major operating system! Just follow the instructions at https://docs.docker.com/get-docker/ to get started. +Installing Docker is very simple and supported on nearly every major operating system! Just follow the instructions [here](https://docs.docker.com/get-docker/) to get started. ### Building The Bot's Docker Image @@ -40,4 +40,4 @@ docker stop CONTAINER_NAME ```bash docker restart CONTAINER_NAME -``` \ No newline at end of file +``` diff --git a/docs/src/advanced/dynamiccommands.md b/docs/src/advanced/dynamiccommands.md index 209e31b9e..bb52cba04 100644 --- a/docs/src/advanced/dynamiccommands.md +++ b/docs/src/advanced/dynamiccommands.md @@ -15,12 +15,9 @@ Let's create all the commands for the entire Nekos.Life API in a very short time - Create a file in the commands folder called `nekos.ts` which will create all our fun commands. ```ts -import { botCache } from "../../mod.ts"; -import { sendMessage } from "../../deps.ts"; +import { botCache,sendMessage } from "../../deps.ts"; const nekosEndpoints = [ - { name: "spank", path: "/img/spank", nsfw: true }, - { name: "gasm", path: "/img/gasm", nsfw: true }, { name: "tickle", path: "/img/tickle", nsfw: false }, { name: "slap", path: "/img/slap", nsfw: false }, { name: "poke", path: "/img/poke", nsfw: false }, @@ -50,43 +47,7 @@ const nekosEndpoints = [ { name: "gecg", path: "/img/gecg", nsfw: false }, { name: "avatar", path: "/img/avatar", nsfw: false }, { name: "waifu", path: "/img/waifu", nsfw: false }, - // 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 }, + // All other paths have been removed for this guide as they are NSFW. { name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true }, ]; @@ -98,13 +59,15 @@ nekosEndpoints.forEach((endpoint) => { execute: async function (message) { const url = `https://nekos.life/api/v2${endpoint.path}`; const result = await fetch(url).then((res) => res.json()); - sendMessage(message.channelID, result?.url); + sendMessage(message.channelID, result?.url || result[endpoint.name]); }, }); }); ``` -Take a minute to realize what just happened. This has made 68 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this. +> **Note:** We have removed the endpoints that were leading to NSFW content. With them, we would just have created 68 different commands. + +Take a minute to realize what just happened. This has made 29 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this. **That ladies and gentleman is the power and magic of Discordeno!** @@ -113,23 +76,13 @@ Take a minute to realize what just happened. This has made 68 different unique c If your still a little confused, don't worry. Let's break it down. ```ts -nekosEndpoints.forEach((endpoint) => { - botCache.commands.set(endpoint.name, { - name: endpoint.name, - nsfw: endpoint.nsfw, - botChannelPermissions: ["SEND_MESSAGES", "EMBED_LINKS"], - execute: async function (message) { - const url = `https://nekos.life/api/v2${endpoint.path}`; - const result = await fetch(url).then((res) => res.json()); - sendMessage(message.channelID, result?.url); - }, - }); -}); +const nekosEndpoints = [ + { name: "tickle", path: "/img/tickle", nsfw: false }, + // ... +] ``` -This is the part where we are doing the magical stuff. So let's take a look at this a bit. Above this we had an array of all the endpoints on the API giving a name, a path, and a boolean to say if it is NSFW or not. - -We ran a loop `nekosEndpoints.forEach()` on that array and for each item in that array, we created a command. +This is just an array of all the endpoints on the API giving a name, a path, and a boolean to say if it is NSFW or not. ```ts botCache.commands.set(endpoint.name, { @@ -139,14 +92,17 @@ botCache.commands.set(endpoint.name, { execute: async function (message) { const url = `https://nekos.life/api/v2${endpoint.path}`; const result = await fetch(url).then((res) => res.json()); - sendMessage(message.channelID, result?.url); + sendMessage(message.channelID, result?.url || result[endpoint.name]); }, }); ``` -For the command name, we used the `endpoint.name` property. For the `nsfw` property we used the `endpoint.nsfw` property. Since all of these commands, send a message that is a URL we simply required those permissions in all 68 of these commands. +This is the part where we are doing the magical stuff. So let's take a look at this a bit. + +We ran a loop `nekosEndpoints.forEach()` on that array and for each item in that array, we created a command. + +For the command name, we used the `endpoint.name` property. For the `nsfw` property we used the `endpoint.nsfw` property. Since all of these commands, send a message that is a URL we simply required those permissions in all 29 of these commands. Then we simply write the code for the commands and it all just works. If you were to reload/restart your bot now with this. You will see that you have access to 68 new commands. Take a minute and try out some of the commands and how they function. - diff --git a/docs/src/advanced/permlevels.md b/docs/src/advanced/permlevels.md index 7870aacc0..be95119f2 100644 --- a/docs/src/advanced/permlevels.md +++ b/docs/src/advanced/permlevels.md @@ -21,7 +21,7 @@ Any of these can be easily modified, in their files. Let's go ahead and try and If you have a bot support server, you might also have a role setup for your bot's support team. So to make life easier, it would be nice if someone with that role could use the command without having to modify the configs file every time. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { PermissionLevels } from "../types/commands.ts"; import { configs } from "../../configs.ts"; @@ -38,17 +38,17 @@ Let's add some pseudo-code first. botCache.permissionLevels.set( PermissionLevels.BOT_SUPPORT, async (message) => { - // If the user id exists in the configs allow the command - if (configs.userIDs.botSupporters.includes(message.author.id)) return true - - // The users id was not in the configs, check if they have the role in bot server - - // Get the bots support server - + // If the user id exists in the configs allow the command + if (configs.userIDs.botSupporters.includes(message.author.id)) return true + + // The users id was not in the configs, check if they have the role in bot server + + // Get the bots support server + // If the user is not a member of the support server they can't be one of the support staff. // If they have the role allow the command otherwise it will be false and block the command. - }, + }, ); ``` @@ -58,12 +58,12 @@ Awesome, now that the plan is in place, let's add the code. botCache.permissionLevels.set( PermissionLevels.BOT_SUPPORT, async (message) => { - // If the user id exists in the configs allow the command - if (configs.userIDs.botSupporters.includes(message.author.id)) return true - - // The users id was not in the configs, check if they have the role in bot server - - // Get the bots support server + // If the user id exists in the configs allow the command + if (configs.userIDs.botSupporters.includes(message.author.id)) return true + + // The users id was not in the configs, check if they have the role in bot server + + // Get the bots support server const guild = cache.guilds.get(configs.supportServerID) if (!guild) return false @@ -73,7 +73,7 @@ botCache.permissionLevels.set( // If they have the role allow the command otherwise it will be false and block the command. return member.roles.includes('BOT_SUPPORT_ROLE_ID_HERE') - }, + }, ); ``` @@ -101,8 +101,8 @@ Let's add a new one for a Nitro Booster role. You can add it in any order here y ```ts export enum PermissionLevels { - MEMBER, - NITRO_BOOSTER, + MEMBER, + NITRO_BOOSTER, MODERATOR, ADMIN, SERVER_OWNER, @@ -115,19 +115,19 @@ export enum PermissionLevels { Once that's done, we can go and create the code for it. Now, lets create a new file in `permissionLevels` folder called `booster.ts` and paste the base snippet for a permission level below. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { PermissionLevels } from "../types/commands.ts"; import { configs } from "../../configs.ts"; botCache.permissionLevels.set( PermissionLevels, async (message) => { - // Code goes here - } + // Code goes here + } ); ``` -**NOTE:** You will see an error in the `PermissionLevels,` line because we need to select one of the permission levels. In this case we want the NITRO_BOOSTER level we just created above. +> **NOTE:** You will see an error in the `PermissionLevels,` line because we need to select one of the permission levels. In this case we want the NITRO_BOOSTER level we just created above. ```ts PermissionLevels.NITRO_BOOSTER @@ -136,7 +136,7 @@ PermissionLevels.NITRO_BOOSTER Next we can add the code in place. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { PermissionLevels } from "../types/commands.ts"; import { configs } from "../../configs.ts"; @@ -164,7 +164,7 @@ Awesome! You just created your very own permission level. Now let's check out ho There are two ways to use permission levels. You can provide an array of PermissionLevels or you can provide a custom function. ```ts -botCache.commands.set(`reload`, { +createCommand({ name: `reload`, permissionLevels: [PermissionLevels.BOT_OWNER], botChannelPermissions: ["SEND_MESSAGES"], @@ -185,7 +185,7 @@ If you provide an array of permission levels, only one of these is necessary to There is another way to use permission levels. You can provide a custom function that must return a boolean. For example, ```ts -botCache.commands.set(`example`, { +createCommand({ name: `example`, permissionLevels: (message, command, guild) => { // Anything you'd like to check here and return a boolean. Must return true or false. diff --git a/docs/src/advanced/subcommands.md b/docs/src/advanced/subcommands.md index 7ab7ef288..93def018c 100644 --- a/docs/src/advanced/subcommands.md +++ b/docs/src/advanced/subcommands.md @@ -9,14 +9,14 @@ A subcommand can have it's own settings for example, you can allow 1 subcommand Let's start by understanding the `prefix` command which uses subcommands. It is a good basic example, to help us understand how to create a subcommand. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { PermissionLevels } from "../types/commands.ts"; -import { sendResponse, sendEmbed, createSubcommand } from "../utils/helpers.ts"; +import { sendResponse, sendEmbed, createSubcommand, createCommand } from "../utils/helpers.ts"; import { parsePrefix } from "../monitors/commandHandler.ts"; import { Embed } from "../utils/Embed.ts"; // This command will only execute if there was no valid sub command: !prefix -botCache.commands.set("prefix", { +createCommand({ name: "prefix", arguments: [ { @@ -31,7 +31,7 @@ botCache.commands.set("prefix", { const embed = new Embed() .setTitle("Prefix Information") .setDescription(` - **Guild**: \`${message.guild()?.name}\` + **Guild**: \`${message.guild?.name}\` **Current Prefix**: \`${parsePrefix(message.guildID)}\` `) .setTimestamp(); @@ -49,7 +49,7 @@ createSubcommand("prefix", { type: "string", required: true, missing: (message) => { - sendResponse(message, `${message.member()} please provid a prefix`); + sendResponse(message, `${message.member} please provid a prefix`); }, }, ], @@ -78,7 +78,7 @@ createSubcommand("prefix", { Let's separate this to understand what is happening here. The first half of the code is the main command. This is like any other command. However, the important thing is in the arguments we requested a subcommand! ```ts -botCache.commands.set("prefix", { +createCommand({ name: "prefix", arguments: [ { @@ -93,7 +93,7 @@ botCache.commands.set("prefix", { const embed = new Embed() .setTitle("Prefix Information") .setDescription(` - **Guild**: \`${message.guild()?.name}\` + **Guild**: \`${message.guild?.name}\` **Current Prefix**: \`${parsePrefix(message.guildID)}\` `) .setTimestamp(); @@ -115,7 +115,7 @@ createSubcommand("prefix", { type: "string", required: true, missing: (message) => { - sendResponse(message, `${message.member()} please provid a prefix`); + sendResponse(message, `${message.member} please provid a prefix`); }, }, ], diff --git a/docs/src/djs.md b/docs/src/djs.md index 94e6a5dbf..abb8f3e98 100644 --- a/docs/src/djs.md +++ b/docs/src/djs.md @@ -102,13 +102,13 @@ Discordeno Version: ```ts import Client, { updateEventHandlers, -} from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts"; +} from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts"; import { configs } from "./configs.ts"; -import { Intents } from "https://x.nest.land/Discordeno@9.0.1/src/types/options.ts"; +import { Intents } from "https://deno.land/x/discordeno@9.4.0/src/types/options.ts"; import { eventHandlers } from "./src/events/eventHandlers.ts"; -import { Message } from "https://x.nest.land/Discordeno@9.0.1/src/structures/message.ts"; +import { Message } from "https://deno.land/x/discordeno@9.4.0/src/structures/message.ts"; import { Command } from "./src/types/commands.ts"; -import { Guild } from "https://x.nest.land/Discordeno@9.0.1/src/structures/guild.ts"; +import { Guild } from "https://deno.land/x/discordeno@9.4.0/src/structures/guild.ts"; export const botCache = { commands: new Map(), @@ -158,10 +158,10 @@ In our `ready.ts` file we can add the `ready` event listener. ```ts import { botCache } from "../../mod.ts"; import { configs } from "../../configs.ts"; -import { cache } from "https://x.nest.land/Discordeno@9.0.1/src/utils/cache.ts"; -import { editBotsStatus, chooseRandom } from "https://x.nest.land/Discordeno@9.0.1/src/utils/utils.ts"; -import { StatusType } from "https://x.nest.land/Discordeno@9.0.1/src/types/discord.ts"; -import { ActivityType } from "https://x.nest.land/Discordeno@9.0.1/src/types/activity.ts"; +import { cache } from "https://deno.land/x/discordeno@9.4.0/src/utils/cache.ts"; +import { editBotsStatus, chooseRandom } from "https://deno.land/x/discordeno@9.4.0/src/utils/utils.ts"; +import { StatusType } from "https://deno.land/x/discordeno@9.4.0/src/types/discord.ts"; +import { ActivityType } from "https://deno.land/x/discordeno@9.4.0/src/types/activity.ts"; botCache.eventHandlers.ready = function () { console.log(`[READY] Bot is online and ready in ${cache.guilds.size} guild(s)!`); @@ -237,7 +237,7 @@ module.exports = class addRoleCommand extends Command { This is how to do it with Discordeno: ```ts import { botCache } from "../../mod.ts"; -import { addRole } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/member.ts"; +import { addRole } from "https://deno.land/x/discordeno@9.4.0/src/handlers/member.ts"; import { sendAlertResponse, sendResponse } from "../utils/helpers.ts"; botCache.commands.set(`addrole`, { @@ -341,10 +341,10 @@ module.exports = class kickCommand extends Command { Discordeno Version ```ts -import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts"; -import { Member } from "https://x.nest.land/Discordeno@9.0.1/src/structures/member.ts"; -import { kick } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/member.ts"; -import { deleteMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/message.ts"; +import { sendMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/channel.ts"; +import { Member } from "https://deno.land/x/discordeno@9.4.0/src/structures/member.ts"; +import { kick } from "https://deno.land/x/discordeno@9.4.0/src/handlers/member.ts"; +import { deleteMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/message.ts"; import { botCache } from "../../mod.ts"; import { createCommandAliases, sendResponse } from "../utils/helpers.ts"; import { Embed } from "../utils/Embed.ts"; @@ -420,4 +420,4 @@ Let's take a minute and explain the differences here. The first thing you will p ### Need More Examples/Help -If you still need more help converting other aspects of your bot please contact me at [Discord](https://discord.gg/J4NqJ72). I will continue adding more examples to this guide as more people request them. +If you still need more help converting other aspects of your bot please contact me at [Discord](https://discord.com/invite/5vBgXk3UcZ). I will continue adding more examples to this guide as more people request them. diff --git a/docs/src/faq.md b/docs/src/faq.md index bd31f7dea..140a7de4d 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -34,9 +34,9 @@ This means you never have to update your code EXCEPT when you are ready to bump SemVer means more manual work for you to update code but a more secure module. Automated means almost no manual work for you to update code but a less secure module. -To understand that, SemVer makes it so you are using specific Release versions. In your code, you would do this by targeting the `..../Skillz4Killz/Discordeno/v4.0.0/...` in order to use it. Whenever I make a small bug fix or new feature that does not break your code it would be released in a new release such as today's release of v4.0.1. This means you have to manually update your code to get these latest improvements. Until you do, you may have bugs or possibly missing features. The good part about SemVer is that if I make a mistake that could potentially make the code worse, it's a lot easier to move back to a proper version with SemVer. +To understand that, SemVer makes it so you are using specific Release versions. In your code, you would do this by targeting the `..../discordeno/discordeno/v4.0.0/...` in order to use it. Whenever I make a small bug fix or new feature that does not break your code it would be released in a new release such as today's release of v4.0.1. This means you have to manually update your code to get these latest improvements. Until you do, you may have bugs or possibly missing features. The good part about SemVer is that if I make a mistake that could potentially make the code worse, it's a lot easier to move back to a proper version with SemVer. -The automated version would just simply be installed as soon as you reloaded cache for deno because it uses the branch itself as its url `.../Skillz4Killz/Discordeno/v4/...` For example, when i start bots I use a script that reloads cache and restarts the bot making it so i am always using the latest code. Deno makes this possible because it can pull the latest code from any URL even github. So using Github branches to it's peak I create a branch for each version. These versions simply update automatically and you dont have to worry about updating. The only time you need to update is when you bump a MAJOR version like from v4 to v5. Because these may need you to make changes in your code. Note, even the good part about SemVer can be slightly removed by just locking a certain commit as well using this method. +The automated version would just simply be installed as soon as you reloaded cache for deno because it uses the branch itself as its url `.../discordeno/discordeno/v4/...` For example, when i start bots I use a script that reloads cache and restarts the bot making it so i am always using the latest code. Deno makes this possible because it can pull the latest code from any URL even github. So using Github branches to it's peak I create a branch for each version. These versions simply update automatically and you dont have to worry about updating. The only time you need to update is when you bump a MAJOR version like from v4 to v5. Because these may need you to make changes in your code. Note, even the good part about SemVer can be slightly removed by just locking a certain commit as well using this method. At the end of the day, I think both systems can work and I am curious how everyone feels about them. I will be trying my best to maintain both systems. @@ -44,8 +44,8 @@ At the end of the day, I think both systems can work and I am curious how everyo This is a design decision for the lib itself. You can still use class if you want on your bot. In fact, I hope someone makes a framework/boilerplate for this lib one day using classes so that devs have a choice on which style they prefer. Without trying to write an entire thesis statement on the reasons why I avoided Classes in this lib, I will just link to the best resources I believe help explain it. -- Really good article: https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/ -- Lecture by one of the developers who makes JavaScript: https://www.youtube.com/watch?v=PSGEjv3Tqo0 +- [Really good article](https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/) +- [Lecture by one of the developers who makes JavaScript](https://www.youtube.com/watch?v=PSGEjv3Tqo0) In regards to EventEmitter, I believe a functional event API was a much better choice. EventEmitter at it's core is simply just functions that run when a certain event is emitted. In Discordeno, that function is executed instead of emitting some event to trigger that function. @@ -80,8 +80,8 @@ Discordeno is the only library(that I have used), that has built in permission h Discordeno provides you specific keywords that you can use to send a clean response to the end user of your choosing. I have even seen some bots have hundreds of thousands of Missing Permission or Missing Access errors because libraries don't handle it. IMO, this is a crucial part of any good library as much as it is to handle rate limiting. ```typescript -import { Errors } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/types/errors.ts"; -import { Message } from "https://raw.githubusercontent.com/Skillz4Killz/Discordeno/v4/structures/message.ts"; +import { Errors } from "https://raw.githubusercontent.com/discordeno/discordeno/v4/types/errors.ts"; +import { Message } from "https://raw.githubusercontent.com/discordeno/discordeno/v4/structures/message.ts"; export function handleCommandError(message: Message, type: Errors) { switch (type) { diff --git a/docs/src/gettingstarted.md b/docs/src/gettingstarted.md index 28e322b88..1bd8b1ee4 100644 --- a/docs/src/gettingstarted.md +++ b/docs/src/gettingstarted.md @@ -7,7 +7,7 @@ This website serves as the purpose for introducing Discordeno to developers. The [View Documentation on Deno](https://doc.deno.land/https/deno.land/x/discordeno/mod.ts) ## Useful Links -- [GitHub Repository](https://github.com/Skillz4Killz/Discordeno) +- [GitHub Repository](https://github.com/discordeno/discordeno) - [Deno Page](https://deno.land/x/discordeno) - [Website](https://discordeno.mod.land) @@ -32,7 +32,7 @@ Now you've created an Application but it will need some code in order for it to You can install Discordeno by importing: ```ts -import Client from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts"; +import Client from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts"; ``` ## Example Usage @@ -40,7 +40,7 @@ import Client from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts"; 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: ```ts -import StartBot, { sendMessage, Intents } from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts"; +import StartBot, { sendMessage, Intents } from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts"; import config from "./config.ts"; StartBot({ diff --git a/docs/src/stepbystep/createbot.md b/docs/src/stepbystep/createbot.md index 056100e45..75d72729b 100644 --- a/docs/src/stepbystep/createbot.md +++ b/docs/src/stepbystep/createbot.md @@ -68,7 +68,7 @@ export const configs = { #### Token -First, add your bot token. This is **required** for the bot to start. Review the [instructions](https://discordeno.netlify.app/gettingstarted#creatingyourfirstdiscordbotapplication) if you have not made your token yet. +First, add your bot token. This is **required** for the bot to start. Review the [instructions](https://discordeno.mod.land/gettingstarted.html#creating-your-first-discord-bot-application) if you have not made your token yet. #### Prefix diff --git a/docs/src/stepbystep/createcommand.md b/docs/src/stepbystep/createcommand.md index 999ec099a..d280882a8 100644 --- a/docs/src/stepbystep/createcommand.md +++ b/docs/src/stepbystep/createcommand.md @@ -8,10 +8,11 @@ Let's first start by taking an existing command and slightly modifying it to you ```ts import { botCache } from "../../mod.ts"; -import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts"; -import { botID } from "https://x.nest.land/Discordeno@9.0.1/src/module/client.ts"; +import { sendMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/channel.ts"; +import { botID } from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts"; +import { createCommand } from "../utils/helpers.ts" -botCache.commands.set("invite", { +createCommand({ name: "invite", execute: function (message) { // Replace the permission number at the end to request the permissions you wish to request. By default, this will request Admin perms. @@ -23,25 +24,23 @@ botCache.commands.set("invite", { }); ``` -Let's break this down. The first three lines are importing the necessary things from their files so we can use them in this command. Don't worry if this doesn't make sense, most of the time, this will all be done automatically for you if you use a good code editor like Visual Studio Code. We create a command by doing: +Let's break this down. The first four lines are importing the necessary things from their files so we can use them in this command. Don't worry if this doesn't make sense, most of the time, this will all be done automatically for you if you use a good code editor like Visual Studio Code. We create a command by doing: ```ts -botCache.commands.set('commandname', { - +createCommand({ + name: "commandname", }) ``` The command name here must be unique. If it is not unique, your commands will not work properly as you wish. For example, you can't have 2 commands with the `ping` name. In this case, our command is called `invite`. -The `name: 'invite'` is also written as it is useful for our help command and inhibitors! Normally, these two things will be the same. So far it's super easy right. - -Next is the `execute`. This is where the magic happens. This is the function that is triggered when the command is ran by a user. In this case, the bot simply sends a message to a channel where the command was ran and sends an invite link. Let's take a minute and optimize this for our case. We don't **NEED** admin permissions for our bot. Let's go to the [permission calculator](https://discordapi.com/permissions.html#0) and figure out the permissions we need for our bot. +Next is the `execute`. This is where the magic happens. This is the function that is triggered when the command is ran by a user. In this case, the bot simply sends a message to a channel where the command was ran and sends an invite link. Let's take a minute and optimize this for our case. We don't **NEED** admin permissions for our bot. Let's go to the [permission calculator](https://discordapi.com/permissions.html) and figure out the permissions we need for our bot. Once you have decided which permissions you want to request, you can copy the number and replace it here. For example, if we want `68640` as our permissions. We can modify our command to be: ```ts - `https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=68640`, +`https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=68640`, ``` Lastly, let's get rid of the comment there as now it is customized to our needs. @@ -55,8 +54,8 @@ Right now, if you were to go to Discord and type `!help invite`, you would see q Let's add a custom description to our invite command. ```ts - name: "invite", - description: "Like the bot? Use this link to add it to your server!" +name: "invite", +description: "Like the bot? Use this link to add it to your server!", ``` 🎉 It's that simple. So let's restart the bot and see how it changed. Use **CTRL + C** to shut down the bot. Then run the command from earlier. @@ -76,9 +75,9 @@ Now, let's make this a little bit easier. What if we wanted this command to also Let's add in the two aliases we wanted. ```ts - name: "invite", - aliases: ["inv", "join"], - description: "Like the bot? Use this link to add it to your server!" +name: "invite", +aliases: ["inv", "join"], +description: "Like the bot? Use this link to add it to your server!", ``` > **Note:** If you only want to add 1 alias, you can pass in a simple string instead of an array as well as the second argument. @@ -111,26 +110,26 @@ If you get stuck, don't worry. When you are ready, let's continue to the next st ## Creating A Command -Let's make a command that will allow guild admins to give or take roles from a member. Since, we are creating a `Moderation` command to give or take roles, let's go ahead and create a Category folder called `Moderation` and then create a file called `role.ts`. Once the file is made, you can paste this following base snippet to make our first command. +Let's make a command that will allow guild admins to give or take roles from a member. Since, we are creating a `Moderation` command to give or take roles, let's go ahead and create a category folder called `Moderation` and then create a file called `role.ts`. Once the file is made, you can paste this following base snippet to make our first command. ```ts import { botCache } from "../../mod.ts"; -import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts"; +import { sendMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/channel.ts"; import { PermissionLevels } from "../types/commands.ts"; import { createCommandAliases } from "../utils/helpers"; -botCache.commands.set("commandname", { +createCommand({ name: "commandname", aliases: [], dmOnly: false, guildOnly: false, nsfw: false, - permissionLevel: [PermissionLevels.MEMBER], + permissionLevels: [PermissionLevels.MEMBER], botServerPermissions: [], botChannelPermissions: [], userServerPermissions: [], userChannelPermissions: [], - description: string, + description: "description", cooldown: { seconds: 0, allowedUses: 0, @@ -184,7 +183,7 @@ Discordeno makes this pretty simple for us. permissionLevels: [PermissionLevels.MODERATOR, PermissionLevels.ADMIN] ``` -We're not going to cover this in detail, here because we will have an entire in depth guide for permission levels. If you want to pause and learn it now, feel free: [Permission Levels Advanced Guide](https://discordeno.netlify.app/advanced/permlevels) +We're not going to cover this in detail here because we will have an entire in depth guide for permission levels. If you want to pause and learn it now, feel free: [Permission Levels Advanced Guide](https://discordeno.mod.land/advanced/permlevels.html) ## Permissions Check Options @@ -214,7 +213,7 @@ The cooldown options go hand in hand together. - `seconds` is how long to make the user wait in seconds before they can use the command again. By default, there is no wait time aka 0 seconds. - `allowedUses` is how many times a user is allowed to use a command before they are placed on cooldown. -- + Let's use our command as an example. We don't want someone to start spamming the role command so we can add a cooldown of 60 seconds. However, this would mean every time you give a role to someone, you would need to wait a whole minute to give a role to someone else. This is where `allowedUses` shines! Let's set it to to 5 so that a user can use this command 5 times in a row before being asked to wait for 60 seconds. ```ts @@ -285,13 +284,13 @@ execute: function (message, args, guild) { > } > ``` -Let's start out by writing some pseudo-code(comments that will help us plan the code). +Let's start out by writing some pseudo-code (comments that will help us plan the code). ```ts execute: function (message, args, guild) { // If this was the everyone role alert with a silly error - // If this is a managed role(some bots role) we can't give/remove alert with silly error + // If this is a managed role (some bots role) we can't give/remove it alert with silly error // Get the bots highest role @@ -299,8 +298,6 @@ execute: function (message, args, guild) { // If the role is too high alert the user. - // If the user has this role already remove the role from them. - // Check the command author's highest role // If the author does not have a role high enough to give this role alert @@ -330,6 +327,10 @@ if (args.role.id === message.guildID) { You might be seeing an error since we didn't provide the accurate typings for the `args` parameter. The message and guild parameter is automated but the args is dynamic depending on your `arguments` option for your command. So let's do this real quick. ```ts +import { Role, Member } from "../../deps.ts"; + +// Lots of code here hidden so you can see the changes easily. + execute: function (message, args: RoleArgs, guild) { // If this was the everyone role alert with a silly error if (args.role.id === message.guildID) { @@ -339,6 +340,7 @@ execute: function (message, args: RoleArgs, guild) { // Lots of comments here hidden so you can see the changes easily. } +// Put this at the end of the file. interface RoleArgs { role: Role; member: Member; @@ -349,7 +351,8 @@ Awesome! Let's keep going. ```ts -execute: function (message, args: RoleArgs, guild) { +// Make the function asynchronous +execute: async function (message, args: RoleArgs, guild) { // If this was the everyone role alert with a silly error if (args.role.id === message.guildID) { return sendResponse(message, "I don't know if you noticed or not but I'm an extremely arrogant bot who tends to think all of his plans will work. But I can't give the everyone role to someone."); @@ -393,18 +396,18 @@ execute: function (message, args: RoleArgs, guild) { return sendResponse(message, `The role **${args.role.name}** has been added to **${args.member.tag}**.`) } ``` +> **Note:** Asynchronous functions are an advanced topic, and you do not need to worry about them now. Basically, we need to do that because checking the highest Role requires the use of await. The final version of the command should look something like this: ```ts -import { highestRole, higherRolePosition, botID, removeRole, addRole, Role, Member } from "../../deps.ts"; -import { botCache } from "../../mod.ts"; +import { botCache, highestRole, higherRolePosition, botID, removeRole, addRole, Role, Member } from "../../deps.ts"; import { PermissionLevels } from "../types/commands.ts"; -import { sendResponse } from "../utils/helpers.ts"; +import { createCommand, sendResponse } from "../utils/helpers.ts"; -botCache.commands.set("role", { +createCommand({ name: "role", - permissionLevel: [PermissionLevels.MODERATOR, PermissionLevels.ADMIN], + permissionLevels: [PermissionLevels.MODERATOR, PermissionLevels.ADMIN], botServerPermissions: ["MANAGE_ROLES"], botChannelPermissions: ["SEND_MESSAGES"], cooldown: { @@ -427,7 +430,7 @@ botCache.commands.set("role", { } } ], - execute: function (message, args: RoleArgs) { + execute: async function (message, args: RoleArgs) { // If this was the everyone role alert with a silly error if (args.role.id === message.guildID) { return sendResponse( @@ -445,7 +448,7 @@ botCache.commands.set("role", { } // Get the bots highest role - const botsHighestRole = highestRole(message.guildID, botID); + const botsHighestRole = await highestRole(message.guildID, botID); // Check if the bot has a role higher than the role that it will try to give. If the role is too high alert the user. if (!botsHighestRole || !higherRolePosition( @@ -460,7 +463,7 @@ botCache.commands.set("role", { } // Check the command author's highest role - const membersHighestRole = highestRole(message.guildID, message.author.id); + const membersHighestRole = await highestRole(message.guildID, message.author.id); // If the author does not have a role high enough to give this role alert if (!membersHighestRole || @@ -473,7 +476,7 @@ botCache.commands.set("role", { } // If the user has this role already we should remove it - if (message.member()?.roles.includes(args.role.id)) { + if (message.member?.roles.includes(args.role.id)) { removeRole( message.guildID, args.member.user.id, @@ -504,9 +507,9 @@ interface RoleArgs { } ``` -> **Note:** The response strings are a play on some funny Stargate(my favorite tv show) moments. Feel free to change as you wish to fit your bot's personality. +> **Note:** The response strings are a play on some funny Stargate (my favorite tv show) moments. Feel free to change as you wish to fit your bot's personality. -## Dynamic(Advanced Level) Command Creation +## Dynamic (Advanced Level) Command Creation This is some advanced level super magical command creation skills. Discordeno gives you the power to dynamically create commands. What that means is you can write the code for one command but dynamically create like 50 commands using that same code. @@ -515,9 +518,8 @@ For example, in my main bot I have a lot of "fun" commands like `.hug` or `.kiss - Create a file in the commands folder called `fun.ts` which will create all our fun commands. ```ts -import { Member, sendMessage, chooseRandom, avatarURL } from "../../deps.ts"; -import { botCache } from "../../mod.ts"; -import { createCommandAliases, sendEmbed } from "../utils/helpers.ts"; +import { botCache, Member, sendMessage, chooseRandom, avatarURL } from "../../deps.ts"; +import { createCommand, createCommandAliases, sendEmbed } from "../utils/helpers.ts"; import { configs } from "../../configs.ts"; import { Embed } from "../utils/Embed.ts"; import { translate } from "../utils/i18next.ts"; @@ -544,7 +546,7 @@ const funCommandData = [ ]; funCommandData.forEach((data) => { - botCache.commands.set(data.name, { + botCache.commands.set({ name: data.name, botChannelPermissions: ["SEND_MESSAGES", "EMBED_LINKS"], cooldown: { @@ -559,7 +561,7 @@ funCommandData.forEach((data) => { ], execute: function (message, args: FunArgs) { // If a member is provided use that otherwise set the member themself - const member = args.member || message.member()!; + const member = args.member || message.member!; const type = member.user.id === message.author.id // Silly response like if user tries to hug themself @@ -575,7 +577,7 @@ funCommandData.forEach((data) => { translate( message.guildID, `commands/fun/${data.name}:${type}`, - { mention: message.member()!.mention, user: member.mention }, + { mention: message.member!.mention, user: member.mention }, ), ) .setImage(chooseRandom(data.gifs)); @@ -596,7 +598,9 @@ interface FunArgs { > **Note:** The imports in this are a bit different. I did this to show you that you can also import everything grouped like this through the `deps.ts` which will make everything available to you at ease. -Take a minute to realize what just happened. This has made 18 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this. For example, if you wanted to add weeb(animated) versions of these commands. Then you are at 36 commands with 1 simple command file. +> **Note:** This is only an example of dynamic command creation and won't work if you try using this code. Since this is an advanced topic we're not going to cover this in more detail here, because we will have an entire in depth guide for dynamic command creation. If you want to pause and learn it now, feel free: [Dynamic Command Creation Advanced Guide](https://discordeno.mod.land/advanced/dynamiccommands.html) + +Take a minute to realize what just happened. This has made 18 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this. For example, if you wanted to add weeb (animated) versions of these commands. Then you are at 36 commands with 1 simple command file. **That ladies and gentleman is the power and magic of Discordeno!** diff --git a/docs/src/stepbystep/createevent.md b/docs/src/stepbystep/createevent.md index 70631a71c..ac6e00ef0 100644 --- a/docs/src/stepbystep/createevent.md +++ b/docs/src/stepbystep/createevent.md @@ -12,7 +12,7 @@ Go ahead and open up the `src/events/ready.ts` file. When you open this file, yo ```ts import { botCache } from "../../mod.ts"; -import { cache } from "https://x.nest.land/Discordeno@9.0.1/src/utils/cache.ts"; +import { cache } from "https://deno.land/x/discordeno@9.4.0/src/utils/cache.ts"; botCache.eventHandlers.ready = function () { console.log(`Loaded ${botCache.arguments.size} Argument(s)`); @@ -28,6 +28,8 @@ botCache.eventHandlers.ready = function () { }; ``` +> **Note:** Some of the code from the ready.ts file was removed here to make it easier to understand. + Overall, this code is pretty self-explanatory. When the bot is ready, it logs all these things to the console for you. ## Creating A Custom Event @@ -35,7 +37,7 @@ Overall, this code is pretty self-explanatory. When the bot is ready, it logs al Make a new file in the events folder called `discordLog.ts` that will send a message to a discord channel whenever we get an error so we don't need to always be watching the console to see errors. Once you made the file, go ahead and paste the base event snippet below. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.eventHandlers.eventname = function () { // Your code goes here @@ -48,18 +50,16 @@ botCache.eventHandlers.eventname = function () { ```ts // This interface is a placeholder that allows you to easily add on custom events for your need. export interface CustomEvents extends EventHandlers { - discordLog: () => unknown; + discordLog: (error: Error) => unknown; } ``` Awesome, now we can get started on adding the code. ```ts -import { botCache } from "../../mod.ts"; +import { botCache, cache, sendMessage } from "../../deps.ts"; import { Embed } from "../utils/Embed.ts"; -import { cache } from "../../deps.ts"; import { configs } from "../../configs.ts"; -import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts"; import { sendEmbed } from "../utils/helpers.ts"; botCache.eventHandlers.discordLog = function (error) { @@ -67,12 +67,12 @@ botCache.eventHandlers.discordLog = function (error) { .setDescription([ "```ts", error, - "```", + "```" ].join("\n")) .setTimestamp(); // Get the channel we need to send this error to - const errorChannel = cache.channels.get(configs.channelIDs.errorChannelID); + const errorChannel = configs.channelIDs.errorChannelID; // If the channel is not found cancel out if (!errorChannel) return; @@ -89,6 +89,7 @@ Now that we have fully covered events, it would be a good time to get some pract channelUpdate?: (channel: Channel, cachedChannel: Channel) => unknown; channelDelete?: (channel: Channel) => unknown; debug?: (args: DebugArg) => unknown; + dispatchRequirements?: (data: DiscordPayload, shardID: number) => unknown; guildBanAdd?: (guild: Guild, user: Member | UserPayload) => unknown; guildBanRemove?: (guild: Guild, user: Member | UserPayload) => unknown; guildCreate?: (guild: Guild) => unknown; diff --git a/docs/src/stepbystep/createinhibitor.md b/docs/src/stepbystep/createinhibitor.md index 940981cb8..591a22010 100644 --- a/docs/src/stepbystep/createinhibitor.md +++ b/docs/src/stepbystep/createinhibitor.md @@ -1,10 +1,10 @@ # Creating Inhibitors! -Woah! You are almost half way done with understanding all of Discordeno. Amazing isn't it? Something you may have noticed in the last section was there were some options that prevented some commands from running like `dmOnly` or the permission options. we created a setting to prevent the monitor from running in certain channels. What if we wanted to do prevent commands from happening? How would we prevent commands from running? +Woah! You are almost half way done with understanding all of Discordeno. Amazing isn't it? Something you may have noticed in the last section was there were some options that prevented some commands from running like `dmOnly` or the permission options. We created a setting to prevent the monitor from running in certain channels. What if we wanted to do prevent commands from happening? How would we prevent commands from running? ## What is an Inhibitor? -An Inhibitor is very similar to how monitors work. A monitor runs on every message but an inhibitor runs on every command. Remember all those command options like cooldown, permissions, permissionLevel, nsfw, etc... Each and every one of these options has an inhibitor in that checks commands for these settings. +Inhibitors are very similar to how monitors work. A monitor runs on every message but an inhibitor runs on every command. Remember all those command options like cooldown, permissions, permissionLevel, nsfw, etc... each and every one of these options has an inhibitor that checks commands for these settings. Let's create our own inhibitor that would prevent commands from being used if the user is not a VIP user. @@ -15,7 +15,7 @@ Let's create our own inhibitor that would prevent commands from being used if th Let's start by creating a file inside the inhibitors folder called `boosted.ts` and paste the following snippet of code: ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.inhibitors.set("inhibitorname", async function (message, command, guild) { // Your code goes here @@ -64,7 +64,7 @@ return true; Since we need a new option on our commands we need to add that. Open the `src/types/commands.ts` file and add in the following ```ts - vipOnly?: boolean; +vipOnly?: boolean; ``` Once that is added, you can go into any command and mark them as vip only commands. @@ -77,4 +77,4 @@ You can now try and get a little more practice with inhibitors by trying to chal Majority of Discordeno bots do not use many custom inhibitors because most of the necessary/important ones already come built for you in the inhibitors folder. -Next we will try our hands at creating a monitor. +Next we will try our hands on creating tasks. diff --git a/docs/src/stepbystep/createlanguage.md b/docs/src/stepbystep/createlanguage.md index ff7c93d82..75d78c3ce 100644 --- a/docs/src/stepbystep/createlanguage.md +++ b/docs/src/stepbystep/createlanguage.md @@ -1,6 +1,6 @@ # Creating Languages! -Woot! You have mastered Discordeno inhibitors already. Now it's time to finally make our bot multi-lingual. Vàmanos! +Woot! You have mastered Discordeno events already. Now it's time to finally make our bot multi-lingual. Vàmanos! ## What Is A Discordeno Language? @@ -8,7 +8,7 @@ A Discordeno language is a folder that will hold all our responses that the bot ## i18next -By default, Discordeno comes built with support for i18next(one of if not the best localization libraries). If you want to learn more, go to [i18next website](https://www.i18next.com/). For now, there is probably not going to be anything you will need to learn there. As most of the functionality has already been created for you right here in Discordeno. +By default, Discordeno comes built with support for i18next (one of if not the best localization libraries). If you want to learn more, go to [i18next website](https://www.i18next.com/). For now, there is probably not going to be anything you will need to learn there. As most of the functionality has already been created for you right here in Discordeno. ## Default Language @@ -16,7 +16,7 @@ The default language with Discordeno is American English which uses the name `en ## Understanding The Folder Structure -The first folder inside the languages folder must be a language folder following the name pattern like `en_US`. So for example, if we wanted to add a Spanish(Spain) language to our bot we would create a new folder called `es_ES`. +The first folder inside the languages folder must be a language folder following the name pattern like `en_US`. So for example, if we wanted to add a Spanish (Spain) language to our bot we would create a new folder called `es_ES`. You can have as many folder in here as you like. For example I can do something like `src/languages/en_US/commands/fun/hug.json`. Notice that I have created categories to help keep them categorized and easier to find. You can do it however you wish to have them. For now, just remember that files must always be `.json` files in these folders. **JSON is required.** @@ -60,7 +60,7 @@ i18next allows you to pass in variables that you can use when you want in your s translate( message.guildID, `commands/fun/${data.name}:${type}`, - { mention: message.member()!.mention, user: member.mention }, + { mention: message.member!.mention, user: member.mention }, ) ``` @@ -71,7 +71,9 @@ Here we can see that we passed in: ## Variables +::: v-pre Variables in i18next use the `{{}}` format. So the variable `mention` would be used by doing `{{mention}}` +::: ## Key Rules @@ -85,7 +87,7 @@ The first one is the only one that is mandatory. The other two are recommended f ## Missing Keys -Every developer forgets stuff sometimes. When you forget to translate a key, it will be marked as a missing key and you will be alerted. If you provided a channelID in the configs file, you be be sent a message there and it will also be logged in your console. +Every developer forgets stuff sometimes. When you forget to translate a key, it will be marked as a missing key and you would be alerted if you have provided a channelID in the configs file, and it will also be logged in your console. ## Spanish Version diff --git a/docs/src/stepbystep/createmonitor.md b/docs/src/stepbystep/createmonitor.md index 7f3ed5b59..3dad592d9 100644 --- a/docs/src/stepbystep/createmonitor.md +++ b/docs/src/stepbystep/createmonitor.md @@ -17,7 +17,7 @@ Let's try and create our own monitor so we can understand it better. Suppose we To start, we make a new file in the monitors folder called `inviteFilter.ts`. Then you can paste in the following base monitor snippet. ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; botCache.monitors.set("monitorname", { name: "monitorname", @@ -64,14 +64,13 @@ The default options were chosen for what the majority of monitors will use to he The permission options are the exact same from the commands guide. These options first make sure that either the bot or user has those necessary permissions to run this monitor. For example, our invite filter would mean we need **MANAGE MESSAGES** permission so we can delete messages sent with an invite URL. ```ts - botChannelPermissions: ["MANAGE_MESSAGES"] +botChannelPermissions: ["MANAGE_MESSAGES"] ``` ## Adding The Code ```ts -import { botCache } from "../../mod.ts"; -import { deleteMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/message.ts"; +import { botCache, deleteMessage } from "../../deps.ts"; import { translate } from "../utils/i18next.ts"; import { sendAlertResponse } from "../utils/helpers.ts"; @@ -104,3 +103,4 @@ botCache.monitors.set("inviteFilter", { ``` Nice! Now take some time and add these translation keys to their appropriate files. +Once, you are ready, let's jump into creating some command inhibitors. diff --git a/docs/src/stepbystep/createtask.md b/docs/src/stepbystep/createtask.md index f0372fddb..76053a56d 100644 --- a/docs/src/stepbystep/createtask.md +++ b/docs/src/stepbystep/createtask.md @@ -1,13 +1,13 @@ # Creating Tasks -Phenomenal! Now that you have mastered monitors, let's move on to one of the final parts, Tasks. A lot of times, you will want to do something over and over again. To do this, you can use tasks. You could technically just do this with `setInterval` but, with tasks you gain a little more advantage like having reloadability. The functions that are run can be easily reloaded meaning, you don't need to restart your whole bot just for little change. +Phenomenal! Now that you have mastered inhibitors, let's move on to one of the final parts, Tasks. A lot of times, you will want to do something over and over again. To do this, you can use tasks. You could technically just do this with `setInterval` but, with tasks you gain a little more advantage like having reloadability. The functions that are run can be easily reloaded meaning, you don't need to restart your whole bot just for little change. ## What is a Task? A task is used to conduct a function every so often. For example, Discordeno comes with a built in task called `botlists`. This task runs every so often and updates your data in all the bot lists apis. Let's create a new task to really understand the gist of it. Let's create a task that will run every 5 minutes which will keep our cache clean and minimal allowing your bot to grow and scale much larger without needing a much bigger server to host it on. Start by pasting the following snippet below: ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { Milliseconds } from "../utils/constants/time.ts"; botCache.tasks.set(`taskname`, { @@ -27,7 +27,7 @@ botCache.tasks.set(`taskname`, { ## What is Cache and Why Should We Sweep It? -Your bot's cache is the memory it is holding on to since it started. To understand this slightly easier, when you are buying a server to host your bot on you can check how much RAM(memory) it has. That amount of memory can be expensive $$$ so it is best to build a bot that does not use a ton of memory. You can use this task to tell your bot to delete any memory it is holding on to that is not necessary. +Your bot's cache is the memory it is holding on to since it started. To understand this slightly easier we have an example: when you buy a server to host your bot on you can check how much RAM (memory) it has. That amount of memory can be expensive $$$ so it is best to build a bot that does not use a ton of memory. You can use this task to tell your bot to delete any memory it is holding on to that is not necessary. - Suppose we don't want to keep any presence cached. - Remove any members from cache that have not been active in the last 30 minutes. @@ -36,7 +36,7 @@ Your bot's cache is the memory it is holding on to since it started. To understa ## Adding Pseudo Code ```ts -import { botCache } from "../../mod.ts"; +import { botCache } from "../../deps.ts"; import { Milliseconds } from "../utils/constants/time.ts"; botCache.tasks.set(`taskname`, { @@ -59,9 +59,8 @@ botCache.tasks.set(`taskname`, { Now that we have a plan in place, let's add the code. ```ts -import { botCache } from "../../mod.ts"; +import { botCache, cache } from "../../deps.ts"; import { Milliseconds } from "../utils/constants/time.ts"; -import { cache } from "../../deps.ts"; const MESSAGE_LIFETIME = Milliseconds.MINUTE * 10 const MEMBER_LIFETIME = Milliseconds.MINUTE * 30 @@ -95,7 +94,7 @@ Alrighty, we managed to add most of the code. But we hit a small obstacle. There ## Adding Member Activity Tracker -Let's go to the botCache in `mod.ts` file and add a `new Collection` that will hold the member's id + server id as the key since a member can be in multiple servers. The value for the map will be a timestamp showing when they were last active. We will be able to use this check, how long ago they were last active. +Let's go to the botCache in `cache.ts` file and add a `new Collection` that will hold the member's id + server id as the key since a member can be in multiple servers. The value for the map will be a timestamp showing when they were last active. We will be able to use this check, how long ago they were last active. ```ts memberLastActive: new Collection() @@ -135,7 +134,7 @@ botCache.tasks.set(`sweeper`, { guild.presences.clear(); // Delete any member who has not been active in the last 30 minutes and is not currently in a voice channel guild.members.forEach((member) => { - // The user is currently active in a voice channel + // The user is currently active in a voice channel if (guild.voiceStates.has(member.user.id)) return; const lastActive = botCache.memberLastActive.get(member.user.id); diff --git a/docs/src/stepbystep/hostingbot.md b/docs/src/stepbystep/hostingbot.md index 24619b404..90f667d0d 100644 --- a/docs/src/stepbystep/hostingbot.md +++ b/docs/src/stepbystep/hostingbot.md @@ -64,5 +64,5 @@ deno install --allow-read --allow-run --allow-write --allow-net -f -q --unstable Once, that is done you can simply run the bot. ```shell -denon run --allow-read --allow-net mod.ts +denon run --allow-net --allow-read --no-check mod.ts ``` diff --git a/egg.yml b/egg.yml deleted file mode 100644 index 6a7a432fa..000000000 --- a/egg.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Discordeno -description: >- - Discord API library for Deno (officially vetted library by - Discord). -homepage: https://discordeno.mod.land -stable: true -entry: ./mod.ts -repository: 'https://github.com/Skillz4Killz/Discordeno' -files: - - ./src/**/* - - LICENSE - - README.md - - tsconfig.json - - ./deps.ts - - ./mod.ts -ignore: - - .git*/** - - .vscode/** - - eggs-debug.log -checkAll: false -unlisted: false diff --git a/src/controllers/bans.ts b/src/controllers/bans.ts index 0d1c1cf81..fd1becf98 100644 --- a/src/controllers/bans.ts +++ b/src/controllers/bans.ts @@ -15,7 +15,7 @@ export async function handleInternalGuildBanAdd(data: DiscordPayload) { } export async function handleInternalGuildBanRemove(data: DiscordPayload) { - if (data.t !== "GUILD_BAN_ADD") return; + if (data.t !== "GUILD_BAN_REMOVE") return; const payload = data.d as GuildBanPayload; const guild = await cacheHandlers.get("guilds", payload.guild_id); diff --git a/src/controllers/guilds.ts b/src/controllers/guilds.ts index 61a2777c6..306ebedc5 100644 --- a/src/controllers/guilds.ts +++ b/src/controllers/guilds.ts @@ -37,7 +37,7 @@ export async function handleInternalGuildCreate( } export async function handleInternalGuildDelete(data: DiscordPayload) { - if (data.t !== "GUILD_CREATE") return; + if (data.t !== "GUILD_DELETE") return; const payload = data.d as GuildDeletePayload; cacheHandlers.forEach("messages", (message) => { diff --git a/src/handlers/guild.ts b/src/handlers/guild.ts index 91c07e5d8..877a9f2cb 100644 --- a/src/handlers/guild.ts +++ b/src/handlers/guild.ts @@ -44,8 +44,12 @@ import { botHasPermission, calculateBits } from "../utils/permissions.ts"; import { urlToBase64 } from "../utils/utils.ts"; /** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */ -export function createServer(options: CreateServerOptions) { - return RequestManager.post(endpoints.GUILDS, options); +export async function createServer(options: CreateServerOptions) { + const guild = await RequestManager.post( + endpoints.GUILDS, + options, + ) as CreateGuildPayload; + return structures.createGuild(guild, 0); } /** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event.