mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 19:28:17 +00:00
update docs guide
This commit is contained in:
+2
-2
@@ -18,7 +18,7 @@ A Third Party Deno library for interacting with the Discord API.
|
|||||||
|
|
||||||
### Best TypeScript Support!
|
### Best TypeScript Support!
|
||||||
|
|
||||||
First class support for Typescript! Never compile your code again in order to run it. Automated typings so they are never inaccurate or out of date.
|
First class support for Typescript! Never compile your code again in order to run it.
|
||||||
|
|
||||||
### Stable Library
|
### Stable Library
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ Several bot boilerplates are available to get up and running very quickly. The O
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
Checks all missing permissions necessary before sending a request to the API so that your bot's token do not get globally banned by Discord. Discordeno does not support self-bot functionality like other libraries either.
|
Checks all missing permissions necessary before sending a request to the API so that your bot's token do not get globally banned by Discord. Discordeno does not support self-bot functionality like other libraries either. Even goes so far as to prevent self-bot farms!
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
|
|||||||
+85
-83
@@ -12,7 +12,7 @@ For the purposes of this guide, I will be using the current [latest commit](http
|
|||||||
|
|
||||||
## Preparations
|
## Preparations
|
||||||
|
|
||||||
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/Skillz4Killz/Discordeno-bot-template) I will name it Zodiac.
|
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template) I will name it Zodiac.
|
||||||
|
|
||||||
- Then `git clone https://github.com/Skillz4Killz/Zodiac.git`
|
- Then `git clone https://github.com/Skillz4Killz/Zodiac.git`
|
||||||
|
|
||||||
@@ -100,54 +100,49 @@ client.login(config.env.TOKEN)
|
|||||||
Discordeno Version:
|
Discordeno Version:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import Client, {
|
import { botCache, Intents } from "./deps.ts";
|
||||||
updateEventHandlers,
|
|
||||||
} from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts";
|
|
||||||
import { configs } from "./configs.ts";
|
import { configs } from "./configs.ts";
|
||||||
import { Intents } from "https://deno.land/x/discordeno@9.4.0/src/types/options.ts";
|
import { importDirectory } from "./src/utils/helpers.ts";
|
||||||
import { eventHandlers } from "./src/events/eventHandlers.ts";
|
import { loadLanguages } from "./src/utils/i18next.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://deno.land/x/discordeno@9.4.0/src/structures/guild.ts";
|
|
||||||
|
|
||||||
export const botCache = {
|
console.info(
|
||||||
commands: new Map<string, Command>(),
|
"Beginning Bot Startup Process. This can take a little bit depending on your system. Loading now...",
|
||||||
commandAliases: new Map<string, string>(),
|
);
|
||||||
guildPrefixes: new Map<string, string>(),
|
|
||||||
inhibitors: new Map<
|
|
||||||
string,
|
|
||||||
(message: Message, command: Command, guild?: Guild) => boolean
|
|
||||||
>(),
|
|
||||||
eventHandlers: {} as EventHandlers
|
|
||||||
};
|
|
||||||
|
|
||||||
const importDirectory = async (path: string) => {
|
// Always require these files be processed before anything else
|
||||||
const files = Deno.readDirSync(Deno.realPathSync(path));
|
await Promise.all([
|
||||||
|
"./src/customizations/structures",
|
||||||
for (const file of files) {
|
].map(
|
||||||
if (!file.name) continue;
|
(path) => importDirectory(Deno.realPathSync(path)),
|
||||||
|
));
|
||||||
const currentPath = `${path}/${file.name}`;
|
|
||||||
if (file.isFile) {
|
|
||||||
await import(currentPath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
importDirectory(currentPath);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Forces deno to read all the files which will fill the commands/inhibitors cache etc.
|
// Forces deno to read all the files which will fill the commands/inhibitors cache etc.
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
["./src/commands", "./src/inhibitors", "./src/events"].map((path) => importDirectory(path)),
|
[
|
||||||
|
"./src/commands",
|
||||||
|
"./src/inhibitors",
|
||||||
|
"./src/events",
|
||||||
|
"./src/arguments",
|
||||||
|
"./src/monitors",
|
||||||
|
"./src/tasks",
|
||||||
|
"./src/permissionLevels",
|
||||||
|
"./src/events",
|
||||||
|
].map(
|
||||||
|
(path) => importDirectory(Deno.realPathSync(path)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Loads languages
|
||||||
|
await loadLanguages();
|
||||||
|
await import("./src/database/database.ts");
|
||||||
|
|
||||||
Client({
|
startBot({
|
||||||
token: configs.token,
|
token: configs.token,
|
||||||
// Pick the intents you wish to have for your bot.
|
// Pick the intents you wish to have for your bot.
|
||||||
intents: [Intents.GUILDS, Intents.GUILD_MESSAGES],
|
// For instance, to work with guild message reactions, you will have to pass the Intents.GUILD_MESSAGE_REACTIONS intent to the array.
|
||||||
eventHandlers: botCache.eventHandlers
|
intents: [Intents.GUILDS, Intents.GUILD_MESSAGES],
|
||||||
|
// These are all your event handler functions. Imported from the events folder
|
||||||
|
eventHandlers: botCache.eventHandlers,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -156,15 +151,35 @@ Something we haven't converted yet from the `main.js` files is the event listene
|
|||||||
In our `ready.ts` file we can add the `ready` event listener.
|
In our `ready.ts` file we can add the `ready` event listener.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import {
|
||||||
import { configs } from "../../configs.ts";
|
botCache,
|
||||||
import { cache } from "https://deno.land/x/discordeno@9.4.0/src/utils/cache.ts";
|
cache,
|
||||||
import { editBotsStatus, chooseRandom } from "https://deno.land/x/discordeno@9.4.0/src/utils/utils.ts";
|
editBotsStatus,
|
||||||
import { StatusType } from "https://deno.land/x/discordeno@9.4.0/src/types/discord.ts";
|
StatusTypes,
|
||||||
import { ActivityType } from "https://deno.land/x/discordeno@9.4.0/src/types/activity.ts";
|
ActivityType,
|
||||||
|
chooseRandom
|
||||||
|
} from "../../deps.ts";
|
||||||
|
import { registerTasks } from "./../utils/taskHelper.ts";
|
||||||
|
|
||||||
botCache.eventHandlers.ready = function () {
|
botCache.eventHandlers.ready = function () {
|
||||||
console.log(`[READY] Bot is online and ready in ${cache.guilds.size} guild(s)!`);
|
editBotsStatus(
|
||||||
|
StatusTypes.DoNotDisturb,
|
||||||
|
"Discordeno Best Lib",
|
||||||
|
ActivityType.Game
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Loaded ${botCache.arguments.size} Argument(s)`);
|
||||||
|
console.log(`Loaded ${botCache.commands.size} Command(s)`);
|
||||||
|
console.log(`Loaded ${Object.keys(botCache.eventHandlers).length} Event(s)`);
|
||||||
|
console.log(`Loaded ${botCache.inhibitors.size} Inhibitor(s)`);
|
||||||
|
console.log(`Loaded ${botCache.monitors.size} Monitor(s)`);
|
||||||
|
console.log(`Loaded ${botCache.tasks.size} Task(s)`);
|
||||||
|
|
||||||
|
registerTasks();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[READY] Bot is online and ready in ${cache.guilds.size} guild(s)!`
|
||||||
|
);
|
||||||
|
|
||||||
// list of activities that the bot goes through
|
// list of activities that the bot goes through
|
||||||
const activityArray = [`${configs.prefix}help | `];
|
const activityArray = [`${configs.prefix}help | `];
|
||||||
@@ -236,34 +251,32 @@ module.exports = class addRoleCommand extends Command {
|
|||||||
|
|
||||||
This is how to do it with Discordeno:
|
This is how to do it with Discordeno:
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import { createCommand } from "./../../utils/helpers.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`, {
|
createCommand({
|
||||||
|
name: "role",
|
||||||
|
// Oher ways to call the command
|
||||||
|
aliases: ["addrole"],
|
||||||
// Is the description used for 'help' command
|
// Is the description used for 'help' command
|
||||||
description: "Adds mentioned role to mentioned user.",
|
description: "Adds mentioned role to mentioned user.",
|
||||||
// Prevents it from being used in dms
|
// Prevents it from being used in dms
|
||||||
guildOnly: true,
|
guildOnly: true,
|
||||||
botServerPermissions: ["ADMINISTRATOR", "MANAGE_ROLES"],
|
botServerPermissions: ["ADMINISTRATOR", "MANAGE_ROLES"],
|
||||||
userServerPermissions: ["MANAGE_ROLES"],
|
userServerPermissions: ["MANAGE_ROLES"],
|
||||||
execute: (message, _args, guild) => {
|
arguments: [
|
||||||
const [member] = message.mentions();
|
{ name: "member", type: "member" },
|
||||||
const [roleIDToAdd] = message.mentionRoles;
|
{ name: "role", type: "role" }
|
||||||
const role = guild?.roles.get(roleIDToAdd)
|
],
|
||||||
|
execute: (message, args) => {
|
||||||
// checking to see if the user has the role or not
|
// checking to see if the user has the role or not
|
||||||
if (!member.roles.includes(roleIDToAdd)) {
|
if (!args.member.roles.includes(args.role.id)) {
|
||||||
addRole(guild!, member.user.id, roleIDToAdd)
|
args.member.addRole(message.guildID, args.role.id)
|
||||||
sendAlertResponse(message, `has been given the role: ${role!.name}`, 5);
|
message.sendResponse(`${args.member.mention} has been given the role: ${args.role.name}`, 5);
|
||||||
} else {
|
} else {
|
||||||
sendResponse(message, `already has the role: ${role!.name}`)
|
message.sendResponse(`${args.member.mention} already has the role: ${args.role.name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// other ways to call the command
|
|
||||||
createCommandAliases("role", "addrole");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome, that is a full command converted from Discord.JS to Discordeno. See how easy it is! Let's convert one more command to see how to really take full advantage of Discordeno boilerplate and have something amazing.
|
Awesome, that is a full command converted from Discord.JS to Discordeno. See how easy it is! Let's convert one more command to see how to really take full advantage of Discordeno boilerplate and have something amazing.
|
||||||
@@ -304,7 +317,6 @@ module.exports = class kickCommand extends Command {
|
|||||||
userPermissions: ['KICK_MEMBERS'],
|
userPermissions: ['KICK_MEMBERS'],
|
||||||
// Prevents anyone other than owner to use the command
|
// Prevents anyone other than owner to use the command
|
||||||
ownerOnly: false
|
ownerOnly: false
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,17 +353,11 @@ module.exports = class kickCommand extends Command {
|
|||||||
|
|
||||||
Discordeno Version
|
Discordeno Version
|
||||||
```ts
|
```ts
|
||||||
import { sendMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/channel.ts";
|
import { createCommand } from "./../../utils/helpers.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";
|
|
||||||
import { Args } from "../types/commands.ts";
|
|
||||||
|
|
||||||
botCache.commands.set(`kick`, {
|
createCommand({
|
||||||
name: `kick`,
|
name: `kick`,
|
||||||
|
aliases: ["boot", "tempban"],
|
||||||
description: "Kick command.",
|
description: "Kick command.",
|
||||||
// adds cooldowns to the command
|
// adds cooldowns to the command
|
||||||
cooldown: {
|
cooldown: {
|
||||||
@@ -369,7 +375,7 @@ botCache.commands.set(`kick`, {
|
|||||||
name: "member",
|
name: "member",
|
||||||
type: "member",
|
type: "member",
|
||||||
missing: function (message) {
|
missing: function (message) {
|
||||||
sendResponse(message, `User cannot be found.`);
|
message.sendResponse(`User cannot be found.`);
|
||||||
},
|
},
|
||||||
// By default this is true but for the purpose of the guide so you can see this exists.
|
// By default this is true but for the purpose of the guide so you can see this exists.
|
||||||
required: true,
|
required: true,
|
||||||
@@ -383,40 +389,36 @@ botCache.commands.set(`kick`, {
|
|||||||
lowercase: true,
|
lowercase: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
execute: function (message, args: KickArgs, guild) {
|
execute: function (message, args: KickArgs) {
|
||||||
if (!guild) return;
|
|
||||||
// setting up the embed for report/log
|
// setting up the embed for report/log
|
||||||
const embed = new Embed()
|
const embed = new Embed()
|
||||||
.setDescription(`Report: ${args.member.mention} Kick`)
|
.setDescription(`Report: ${args.member.mention} Kick`)
|
||||||
.addField("Reason >", args.reason)
|
.addField("Reason >", args.reason)
|
||||||
.addField("Time", message.timestamp.toString());
|
.addField("Time", message.timestamp.toString());
|
||||||
|
|
||||||
const reportchannel = guild.channels.find((channel) =>
|
const reportchannel = message.guild?.channels.find((channel) =>
|
||||||
channel.name === "report"
|
channel.name === "report"
|
||||||
);
|
);
|
||||||
if (!reportchannel) {
|
if (!reportchannel) {
|
||||||
return sendResponse(message, "*`Report channel cannot be found!`*");
|
return message.sendResponse("*`Report channel cannot be found!`*");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the message command
|
// Delete the message command
|
||||||
deleteMessage(message, "Remove kick command trigger.");
|
message.delete("Remove kick command trigger.");
|
||||||
// Kick the user with reason
|
// Kick the user with reason
|
||||||
kick(guild, args.member.user.id, args.reason);
|
args.member.kick(message.guildID, args.reason);
|
||||||
// sends the kick report into log/report
|
// sends the kick report into log/report
|
||||||
sendMessage(reportchannel, embed);
|
reporchannel.send({ embed });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// other ways to call the command, must be in lowercase
|
|
||||||
createCommandAliases("kick", ["boot", "tempban"]);
|
|
||||||
|
|
||||||
interface KickArgs {
|
interface KickArgs {
|
||||||
member: Member;
|
member: Member;
|
||||||
reason: string;
|
reason: string;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's take a minute and explain the differences here. The first thing you will probably notice is different is the `arguments` property. Discordeno provides the `arguments` property because it provides argument handling/parsing/validating internally. You don't need to be splitting the message content or going through and validating it yourself. All you do is tell Discordeno that you want a member and a reason. It will do the magic and hard work to get you that data before you even run the command. You just do `args.member` and you have access to the full member object. You can also see that aliases are created slightly different but it's not that huge a impact. The end functionality is the same. There are a lot more powerful aspects to Discordeno like arguments. Keep diving in and you will find all the wonderful tools available to give you the best developer experience possible.
|
Let's take a minute and explain the differences here. The first thing you will probably notice is different is the `arguments` property. Discordeno provides the `arguments` property because it provides argument handling/parsing/validating internally. You don't need to be splitting the message content or going through and validating it yourself. All you do is tell Discordeno that you want a member and a reason. It will do the magic and hard work to get you that data before you even run the command. You just do `args.member` and you have access to the full member object. There are a lot more powerful aspects to Discordeno like arguments. Keep diving in and you will find all the wonderful tools available to give you the best developer experience possible.
|
||||||
|
|
||||||
### Need More Examples/Help
|
### Need More Examples/Help
|
||||||
|
|
||||||
|
|||||||
+4
-33
@@ -4,41 +4,13 @@
|
|||||||
|
|
||||||
Discordeno provides first class support for TypeScript! Since Deno provides support for TypeScript, that also comes into Discordeno. This means you don't need to compile TypeScript before you use it. However, this isn't really why Discordeno is the best library for TypeScript developers. When I developed this library, I was experimenting with a lot of different things and one of them was automated typings.
|
Discordeno provides first class support for TypeScript! Since Deno provides support for TypeScript, that also comes into Discordeno. This means you don't need to compile TypeScript before you use it. However, this isn't really why Discordeno is the best library for TypeScript developers. When I developed this library, I was experimenting with a lot of different things and one of them was automated typings.
|
||||||
|
|
||||||
Whenever I used other libraries, I was always seeing typings being inaccurate or problematic. This is because in any Discord API library, the majority is not used by the library itself so TypeScript doesn't warn the library developers. This makes it extremely likely that those typings become inaccurate or out of date because of simple mistakes like forgetting to update typings. Sometimes libraries will add a property and forget to add that on their typings. This makes it usable for JavaScript developers but not for TypeScript devs. For TypeScript developers, typings are everything! So I asked myself how could I solve this in my own library because I didn't want to have to suffer these problems again. The best solution was to not have any typings for the module at all.
|
Whenever I used other libraries, I was always seeing typings being inaccurate or problematic. This is because in any Discord API library, the majority is not used by the library itself so TypeScript doesn't warn the library developers. This makes it extremely likely that those typings become inaccurate or out of date because of simple mistakes like forgetting to update typings. Sometimes libraries will add a property and forget to add that on their typings. This makes it usable for JavaScript developers but not for TypeScript devs. For TypeScript developers, typings are everything! Discordeno treats typings as part of it's code! A breaking change in typings is a breaking change for the library!
|
||||||
|
|
||||||
In Discordeno, there are no typings created/maintained manually. It is all done **automatically** by TypeScript because of the design decisions of the code itself. **When the code is changed, the typings are automatically updated.** Never again will you suffer the problems of other libraries forgetting to keep their typings up to date properly.
|
|
||||||
|
|
||||||
## If Discordeno Doesn't Have Typings, What Is The Types Folder?
|
|
||||||
|
|
||||||
The types folder is typings built for Discord API Payload not for this lib. Discordeno provides these typings to provide the best developer experience possible when you code.
|
|
||||||
|
|
||||||
## How Stable Is Discordeno?
|
## How Stable Is Discordeno?
|
||||||
|
|
||||||
One of the biggest issues with almost every library(I have used) is stability. None of the libraries gave much love and attention to TypeScript developers the way it deserves. Sometimes TypeScript projects would break because breaking changes to typings did not make a MAJOR bump so TypeScript bots in production would break. Sometimes I was personally maintaing the typings because no one else was for that lib. Some libs were pre 1.0 and didn't even have a stable branch/version where I would not have to worry about breaking changes.
|
One of the biggest issues with almost every library(I have used) is stability. None of the libraries gave much love and attention to TypeScript developers the way it deserves. Sometimes TypeScript projects would break because breaking changes to typings did not make a MAJOR bump so TypeScript bots in production would break. Sometimes I was personally maintaing the typings because no one else was for that lib. Some libs were pre 1.0 and didn't even have a stable branch/version where I would not have to worry about breaking changes.
|
||||||
|
|
||||||
This is why I made it one of my foundational goals of this library to have the best stability for TypeScript developers. No matter how small, a breaking change is a breaking change when it affects the public API. I could care less if we end up at version 500. Being afraid to bump a MAJOR because it's a small change or a typing change is a terrible decision as a library maintainer and destroys the experience for end users. Discordeno provides 2 separate versioning systems to provide you as much flexibility and stability as you like.
|
This is why I made it one of my foundational goals of this library to have the best stability for TypeScript developers. No matter how small, a breaking change is a breaking change when it affects the public API. I could care less if we end up at version 500. Being afraid to bump a MAJOR because it's a small change or a typing change is a terrible decision as a library maintainer and destroys the experience for end users.
|
||||||
|
|
||||||
## What Do You Mean By 2 Separate Versioning Systems?
|
|
||||||
|
|
||||||
Discordeno will have releases that comply with SemVer. To use this system you will simply use the `v2.0.0` system in your version.
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
This means for every tiny bug fix/change you need to manually update the code every time. So if a new feature is added, you would need to bump the version in your code.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Each version is also available through a specific branch. For example v2 branch holds all the version 2 code. This branch is always updated whenever a MINOR or PATCH update is made that will NOT break your bots.
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
This means you never have to update your code EXCEPT when you are ready to bump to next MAJOR version. So if a new feature is added, it will be added automatically. If a small bug is fixed it will be automatic.
|
|
||||||
:::
|
|
||||||
|
|
||||||
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 `..../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 `.../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.
|
|
||||||
|
|
||||||
## Why Doesn't Discordeno Use Classes or EventEmitter?
|
## Why Doesn't Discordeno Use Classes or EventEmitter?
|
||||||
|
|
||||||
@@ -55,7 +27,7 @@ EventEmitter.emit('guildCreate', guild);
|
|||||||
// Discordeno Example
|
// Discordeno Example
|
||||||
eventHandlers.guildCreate?.(guild);
|
eventHandlers.guildCreate?.(guild);
|
||||||
```
|
```
|
||||||
There isn't really any difference especially for users when they use it. One bad thing about EventEmitter is that if misused it can cause memory leaks. It is very easy to open yourself up to these memory leak issues. It has happened to me when I started coding as well. This is why I wanted Discordeno's implementation to help devs avoid the issues I had. It prevents anyone from having this as a potential issue. Another issue with EventEmitter is trying to update the code in those functions without having to deal with headaches left and right of removing and adding listeners.
|
There isn't really any difference especially for users when they use it. One bad thing about EventEmitter is that if misused it can easily cause memory leaks. It is very easy to open yourself up to these memory leak issues. It has happened to me when I started coding as well. This is why I wanted Discordeno's implementation to help devs avoid the issues I had. It prevents anyone from having this as a potential issue. Another issue with EventEmitter is trying to update the code in those functions without having to deal with headaches left and right of removing and adding listeners. You don't need to worry about binding or not binding events. They are just pure functions
|
||||||
|
|
||||||
In Discordeno, this is extremely simple, you just simply give it the new event handlers.
|
In Discordeno, this is extremely simple, you just simply give it the new event handlers.
|
||||||
|
|
||||||
@@ -80,8 +52,7 @@ 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.
|
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
|
```typescript
|
||||||
import { Errors } from "https://raw.githubusercontent.com/discordeno/discordeno/v4/types/errors.ts";
|
import { Errors, Message } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
import { Message } from "https://raw.githubusercontent.com/discordeno/discordeno/v4/structures/message.ts";
|
|
||||||
|
|
||||||
export function handleCommandError(message: Message, type: Errors) {
|
export function handleCommandError(message: Message, type: Errors) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|||||||
@@ -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:
|
You can install Discordeno by importing:
|
||||||
```ts
|
```ts
|
||||||
import Client from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts";
|
import { startBot } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
@@ -40,10 +40,10 @@ import Client from "https://deno.land/x/discordeno@9.4.0/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:
|
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
|
```ts
|
||||||
import StartBot, { sendMessage, Intents } from "https://deno.land/x/discordeno@9.4.0/src/module/client.ts";
|
import { startBot, Intents } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
import config from "./config.ts";
|
import config from "./config.ts";
|
||||||
|
|
||||||
StartBot({
|
startBot({
|
||||||
token: config.token,
|
token: config.token,
|
||||||
intents: [Intents.GUILD_MESSAGES, Intents.GUILDS],
|
intents: [Intents.GUILD_MESSAGES, Intents.GUILDS],
|
||||||
eventHandlers: {
|
eventHandlers: {
|
||||||
@@ -52,7 +52,7 @@ StartBot({
|
|||||||
},
|
},
|
||||||
messageCreate: (message) => {
|
messageCreate: (message) => {
|
||||||
if (message.content === "!ping") {
|
if (message.content === "!ping") {
|
||||||
sendMessage(message.channelID, "Pong");
|
message.reply("Pong");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ Discordeno will help make Discord bot development much easier. Don't worry, as y
|
|||||||
|
|
||||||
> This guide is going to assume you already have the basic requirements to make a bot ready. This includes github, git, a code editor like Visual Studio Code. If you don't have these yet please prepare them first before going forward.
|
> This guide is going to assume you already have the basic requirements to make a bot ready. This includes github, git, a code editor like Visual Studio Code. If you don't have these yet please prepare them first before going forward.
|
||||||
|
|
||||||
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/Skillz4Killz/Discordeno-bot-template/generate). Give it any name you like. For the purpose of this guide we will call it, Stargate.
|
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template/generate). Give it any name you like. For the purpose of this guide we will call it, Stargate.
|
||||||
|
|
||||||
- Then `git clone https://github.com/Skillz4Killz/Stargate.git` Replace **Stargate** with the name you chose.
|
- Then `git clone https://github.com/Skillz4Killz/Stargate.git` Replace **Stargate** with the name you chose.
|
||||||
|
|
||||||
- When that is done, go ahead and open up the folder with VSC.
|
- When that is done, go ahead and open up the folder with VSC.
|
||||||
- Create a new file called `configs.ts`. Open the `configs.example.ts` file and copy everything over.
|
- Create a new file called `configs.ts`. Open the `configs.example.ts` file and copy everything over.
|
||||||
|
|
||||||
@@ -109,9 +108,13 @@ Oh my god! You now have a bot with a bunch of features already! You don't believ
|
|||||||
3. Run the script below:
|
3. Run the script below:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno run --allow-net --allow-read --no-check --config tsconfig.json mod.ts
|
deno run -A --no-check mod.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> The `-A` flag will grant it all permissions to run the bot. If you would like to specify specific ones you may do so!
|
||||||
|
|
||||||
|
> The `--no-check` flag is used for module augmentation support as Deno still does not provide a clean way to have it. If you don't use custom structures, this is not needed.
|
||||||
|
|
||||||
The first time you run it, you may see a lot of files being loaded. This is preparing all the magic behind the scene. Once it is ready, you will see something like this:
|
The first time you run it, you may see a lot of files being loaded. This is preparing all the magic behind the scene. Once it is ready, you will see something like this:
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -7,24 +7,21 @@ Really great job. Now, lets dive into trying to use some of the commands and try
|
|||||||
Let's first start by taking an existing command and slightly modifying it to your needs. Let's use the `Invite` command as our example. When you open the command, you will see something like this:
|
Let's first start by taking an existing command and slightly modifying it to your needs. Let's use the `Invite` command as our example. When you open the command, you will see something like this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import { botCache } from "../../deps.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"
|
import { createCommand } from "../utils/helpers.ts"
|
||||||
|
|
||||||
createCommand({
|
createCommand({
|
||||||
name: "invite",
|
name: "invite",
|
||||||
execute: function (message) {
|
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.
|
// Replace the permission number at the end to request the permissions you wish to request. By default, this will request Admin perms.
|
||||||
sendMessage(
|
message.reply(
|
||||||
message.channelID,
|
|
||||||
`https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=8`,
|
`https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=8`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
Let's break this down. The first two 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
|
```ts
|
||||||
createCommand({
|
createCommand({
|
||||||
@@ -61,7 +58,7 @@ 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.
|
🎉 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.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno run --allow-net --allow-read --no-check --config tsconfig.json mod.ts
|
deno run -A --no-check mod.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
To access this easily, most likely all you need to do is press the **UP ARROW** key. Feel free to copy paste this if it doesn't work.
|
To access this easily, most likely all you need to do is press the **UP ARROW** key. Feel free to copy paste this if it doesn't work.
|
||||||
@@ -80,8 +77,6 @@ aliases: ["inv", "join"],
|
|||||||
description: "Like the bot? Use this link to add it to your server!",
|
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.
|
|
||||||
|
|
||||||
Notice, I added 2 aliases here. You can add as many aliases as you like. If you see a lot of users typing it wrong by accident you can add those typos as aliases as well.
|
Notice, I added 2 aliases here. You can add as many aliases as you like. If you see a lot of users typing it wrong by accident you can add those typos as aliases as well.
|
||||||
|
|
||||||
Let's start testing this out. But first, let's understand something important.
|
Let's start testing this out. But first, let's understand something important.
|
||||||
@@ -113,10 +108,9 @@ If you get stuck, don't worry. When you are ready, let's continue to the next st
|
|||||||
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
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
import { sendMessage } from "https://deno.land/x/discordeno@9.4.0/src/handlers/channel.ts";
|
|
||||||
import { PermissionLevels } from "../types/commands.ts";
|
import { PermissionLevels } from "../types/commands.ts";
|
||||||
import { createCommandAliases } from "../utils/helpers";
|
import { createCommand } from "../utils/helpers.ts";
|
||||||
|
|
||||||
createCommand({
|
createCommand({
|
||||||
name: "commandname",
|
name: "commandname",
|
||||||
@@ -139,8 +133,6 @@ createCommand({
|
|||||||
// The code for your command goes here
|
// The code for your command goes here
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// createCommandAliases("commandname", ["alias"])
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Understanding Command Options
|
## Understanding Command Options
|
||||||
@@ -151,8 +143,6 @@ Woah! We just added a massive file but most of that stuff is new to us. Don't wo
|
|||||||
|
|
||||||
Before we start, quickly update the command name and description.
|
Before we start, quickly update the command name and description.
|
||||||
|
|
||||||
> **Note:** Once you highlight the `commandname`, press **CTRL + SHIFT + L** to select ALL the `commandname` on the file and you can easily replace them at once. Another useful shortcut you may use when coding your bot is **CTRL + D** after highlighting. This shortcut selects the next place where the same text exists as thing in the file that has the same thing as you highlighted.
|
|
||||||
|
|
||||||
## dmOnly & guildOnly Options
|
## dmOnly & guildOnly Options
|
||||||
|
|
||||||
The `dmOnly` option is available if you want to make sure this command only runs in a direct message. If this command is ran in a server, the command will immediately exit out.
|
The `dmOnly` option is available if you want to make sure this command only runs in a direct message. If this command is ran in a server, the command will immediately exit out.
|
||||||
@@ -169,7 +159,9 @@ For the purpose of this guide, we want our `role` comamnd to only be run in a se
|
|||||||
|
|
||||||
NSFW stands for **Not Safe For Work**. One of Discord's rules is that you enforce that NSFW content is sent only in NSFW channels. Discordeno has this built in. You simply tell Discordeno, that you want a command to be considered `nsfw` or not.
|
NSFW stands for **Not Safe For Work**. One of Discord's rules is that you enforce that NSFW content is sent only in NSFW channels. Discordeno has this built in. You simply tell Discordeno, that you want a command to be considered `nsfw` or not.
|
||||||
|
|
||||||
If this option is enabled, this command will only be able to be used in a `nsfw` channel on a server. Discord does not consider Direct Messages as nsfw safe!
|
If this option is enabled, this command will only be able to be used in a `nsfw` channel on a server.
|
||||||
|
|
||||||
|
> Discord does not consider Direct Messages as nsfw safe!
|
||||||
|
|
||||||
## Permission Level Option
|
## Permission Level Option
|
||||||
|
|
||||||
@@ -250,14 +242,14 @@ arguments: [
|
|||||||
name: "member",
|
name: "member",
|
||||||
type: "member",
|
type: "member",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
sendResponse(message, `you did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
message.sendResponse(`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "role",
|
name: "role",
|
||||||
type: "role",
|
type: "role",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
sendResponse(message, `you did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
message.sendResponse(`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -320,7 +312,7 @@ To do this, we are going to want something like this:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
if (args.role.id === message.guildID) {
|
if (args.role.id === message.guildID) {
|
||||||
return sendResponse(message, "The everyone role can not be given to anyone because everyone has the everyone role already. *Keep calm and let Carter figure it out*!");
|
return message.sendResponse("The everyone role can not be given to anyone because everyone has the everyone role already. *Keep calm and let Carter figure it out*!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -334,7 +326,7 @@ import { Role, Member } from "../../deps.ts";
|
|||||||
execute: function (message, args: RoleArgs, guild) {
|
execute: function (message, args: RoleArgs, guild) {
|
||||||
// If this was the everyone role alert with a silly error
|
// If this was the everyone role alert with a silly error
|
||||||
if (args.role.id === message.guildID) {
|
if (args.role.id === message.guildID) {
|
||||||
return sendResponse(message, "Are you trying to make this person a super hero? Everyone has the everyone role. I can't give the everyone role to another user.");
|
return message.sendResponse("Are you trying to make this person a super hero? Everyone has the everyone role. I can't give the everyone role to another user.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lots of comments here hidden so you can see the changes easily.
|
// Lots of comments here hidden so you can see the changes easily.
|
||||||
@@ -355,55 +347,54 @@ Awesome! Let's keep going.
|
|||||||
execute: async function (message, args: RoleArgs, guild) {
|
execute: async function (message, args: RoleArgs, guild) {
|
||||||
// If this was the everyone role alert with a silly error
|
// If this was the everyone role alert with a silly error
|
||||||
if (args.role.id === message.guildID) {
|
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.");
|
return message.sendResponse("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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 alert with silly error
|
||||||
if (args.role.managed) {
|
if (args.role.managed) {
|
||||||
return sendResponse(message, "Dammit man, just 'cause I'm Scottish doesn't mean I can give your people managed roles.")
|
return message.sendResponse("Dammit man, just 'cause I'm Scottish doesn't mean I can give your people managed roles.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the bots highest 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.
|
// Check if the bot has a role higher than the role that it will try to give.
|
||||||
const botIsHigher = higherRolePosition(message.guildID, botsHighestRole.id, args.role.id)
|
const botIsHigher = await higherRolePosition(message.guildID, botsHighestRole.id, args.role.id)
|
||||||
|
|
||||||
// If the role is too high alert the user.
|
// If the role is too high alert the user.
|
||||||
if (!botIsHigher) {
|
if (!botIsHigher) {
|
||||||
return sendResponse(message, "Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.")
|
return message.sendResponse("Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the command author's highest 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 the author does not have a role high enough to give this role alert
|
||||||
if (!higherRolePosition(message.guildID, membersHighestRole.id, args.role.id)) {
|
if (!(await higherRolePosition(message.guildID, membersHighestRole.id, args.role.id))) {
|
||||||
return sendResponse(message, "In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.")
|
return message.sendResponse("In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has this role already we should remove it
|
// If the user has this role already we should remove it
|
||||||
if (message.member().roles.includes(args.role.id)) {
|
if (message.member?.guilds.get(message.guildID)?.roles.includes(args.role.id)) {
|
||||||
removeRole(message.guildID, args.member.user.id, args.role.id, `${message.author.tag} used the role command to remove this role.`)
|
message.member.removeRole(message.guildID, args.role.id, `${message.author.tag} used the role command to remove this role.`)
|
||||||
// Alert the user that used the command that the user has lost the role.
|
// Alert the user that used the command that the user has lost the role.
|
||||||
return sendResponse(message, `The role **${args.role.name}** has been removed from **${args.member.tag}**.`)
|
return message.sendResponse(`The role **${args.role.name}** has been removed from **${args.member.tag}**.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the role to the user.
|
// Add the role to the user.
|
||||||
addRole(guildID, memberID, roleID)
|
message.member?.addRole(guildID, roleID)
|
||||||
|
|
||||||
// Alert the user that used the command that the user has been give the role.
|
// Alert the user that used the command that the user has been give the role.
|
||||||
return sendResponse(message, `The role **${args.role.name}** has been added to **${args.member.tag}**.`)
|
return message.sendResponse(`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:
|
The final version of the command should look something like this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache, highestRole, higherRolePosition, botID, removeRole, addRole, Role, Member } from "../../deps.ts";
|
import { botCache, highestRole, higherRolePosition, botID, Role, Member } from "../../deps.ts";
|
||||||
import { PermissionLevels } from "../types/commands.ts";
|
import { PermissionLevels } from "../types/commands.ts";
|
||||||
import { createCommand, sendResponse } from "../utils/helpers.ts";
|
import { createCommand } from "../utils/helpers.ts";
|
||||||
|
|
||||||
createCommand({
|
createCommand({
|
||||||
name: "role",
|
name: "role",
|
||||||
@@ -419,30 +410,28 @@ createCommand({
|
|||||||
name: "member",
|
name: "member",
|
||||||
type: "member",
|
type: "member",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
sendResponse(message, `you did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
message.sendResponse(`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "role",
|
name: "role",
|
||||||
type: "role",
|
type: "role",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
sendResponse(message, `you did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
message.sendResponse(`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
execute: async function (message, args: RoleArgs) {
|
execute: async function (message, args: RoleArgs) {
|
||||||
// If this was the everyone role alert with a silly error
|
// If this was the everyone role alert with a silly error
|
||||||
if (args.role.id === message.guildID) {
|
if (args.role.id === message.guildID) {
|
||||||
return sendResponse(
|
return message.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.",
|
"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.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 alert with silly error
|
||||||
if (args.role.managed) {
|
if (args.role.managed) {
|
||||||
return sendResponse(
|
return message.sendResponse(
|
||||||
message,
|
|
||||||
"Dammit man, just 'cause I'm Scottish doesn't mean I can give your people managed roles.",
|
"Dammit man, just 'cause I'm Scottish doesn't mean I can give your people managed roles.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -451,13 +440,12 @@ createCommand({
|
|||||||
const botsHighestRole = await 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.
|
// 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(
|
if (!botsHighestRole || !(await higherRolePosition(
|
||||||
message.guildID,
|
message.guildID,
|
||||||
botsHighestRole.id,
|
botsHighestRole.id,
|
||||||
args.role.id,
|
args.role.id,
|
||||||
)) {
|
))) {
|
||||||
return sendResponse(
|
return message.sendResponse(
|
||||||
message,
|
|
||||||
"Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.",
|
"Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -467,35 +455,31 @@ createCommand({
|
|||||||
|
|
||||||
// If the author does not have a role high enough to give this role alert
|
// If the author does not have a role high enough to give this role alert
|
||||||
if (!membersHighestRole ||
|
if (!membersHighestRole ||
|
||||||
!higherRolePosition(message.guildID, membersHighestRole.id, args.role.id)
|
!(await higherRolePosition(message.guildID, membersHighestRole.id, args.role.id))
|
||||||
) {
|
) {
|
||||||
return sendResponse(
|
return message.sendResponse(
|
||||||
message,
|
|
||||||
"In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.",
|
"In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has this role already we should remove it
|
// If the user has this role already we should remove it
|
||||||
if (message.member?.roles.includes(args.role.id)) {
|
if (message.member?.guilds.get(message.guildID)?.roles.includes(args.role.id)) {
|
||||||
removeRole(
|
message.member.removeRole(
|
||||||
message.guildID,
|
message.guildID,
|
||||||
args.member.user.id,
|
|
||||||
args.role.id,
|
args.role.id,
|
||||||
`${message.author.username} used the role command to remove this role.`,
|
`${message.author.username} used the role command to remove this role.`,
|
||||||
);
|
);
|
||||||
// Alert the user that used the command that the user has lost the role.
|
// Alert the user that used the command that the user has lost the role.
|
||||||
return sendResponse(
|
return message.sendResponse(
|
||||||
message,
|
|
||||||
`The role **${args.role.name}** has been removed from **${args.member.tag}**.`,
|
`The role **${args.role.name}** has been removed from **${args.member.tag}**.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the role to the user.
|
// Add the role to the user.
|
||||||
addRole(message.guildID, args.member.user.id, args.role.id, `${message.author.username} used the role command to give this role.`);
|
message.member?.addRole(message.guildID, args.role.id, `${message.author.username} used the role command to give this role.`);
|
||||||
|
|
||||||
// Alert the user that used the command that the user has been give the role.
|
// Alert the user that used the command that the user has been give the role.
|
||||||
return sendResponse(
|
return message.sendResponse(
|
||||||
message,
|
|
||||||
`The role **${args.role.name}** has been added to **${args.member.tag}**.`,
|
`The role **${args.role.name}** has been added to **${args.member.tag}**.`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -548,6 +532,7 @@ const funCommandData = [
|
|||||||
funCommandData.forEach((data) => {
|
funCommandData.forEach((data) => {
|
||||||
botCache.commands.set({
|
botCache.commands.set({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
aliases: data.aliases,
|
||||||
botChannelPermissions: ["SEND_MESSAGES", "EMBED_LINKS"],
|
botChannelPermissions: ["SEND_MESSAGES", "EMBED_LINKS"],
|
||||||
cooldown: {
|
cooldown: {
|
||||||
seconds: 2,
|
seconds: 2,
|
||||||
@@ -585,10 +570,6 @@ funCommandData.forEach((data) => {
|
|||||||
return sendEmbed(message.channelID, embed);
|
return sendEmbed(message.channelID, embed);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.aliases?.length) {
|
|
||||||
createCommandAliases(data.name, data.aliases);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
interface FunArgs {
|
interface FunArgs {
|
||||||
@@ -596,9 +577,7 @@ 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.
|
> **Note:** This is only an example of dynamic command creation and won't work if you try using this code since you won't have the configs necessary. 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)
|
||||||
|
|
||||||
> **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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ An event in Discordeno is a function that can be called when a specific event oc
|
|||||||
Go ahead and open up the `src/events/ready.ts` file. When you open this file, you will see the code that is triggered on the `ready` event. Whenever the bot completely starts up, Discordeno emits the `ready` event. This is when this code will be run allowing you to log these messages.
|
Go ahead and open up the `src/events/ready.ts` file. When you open this file, you will see the code that is triggered on the `ready` event. Whenever the bot completely starts up, Discordeno emits the `ready` event. This is when this code will be run allowing you to log these messages.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import { botCache, cache } from "../../deps.ts";
|
||||||
import { cache } from "https://deno.land/x/discordeno@9.4.0/src/utils/cache.ts";
|
|
||||||
|
|
||||||
botCache.eventHandlers.ready = function () {
|
botCache.eventHandlers.ready = function () {
|
||||||
console.log(`Loaded ${botCache.arguments.size} Argument(s)`);
|
console.log(`Loaded ${botCache.arguments.size} Argument(s)`);
|
||||||
@@ -71,10 +70,8 @@ botCache.eventHandlers.discordLog = function (error) {
|
|||||||
].join("\n"))
|
].join("\n"))
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
// Get the channel we need to send this error to
|
|
||||||
const errorChannel = configs.channelIDs.errorChannelID;
|
|
||||||
// If the channel is not found cancel out
|
// If the channel is not found cancel out
|
||||||
if (!errorChannel) return;
|
if (!configs.channelIDs.errorChannelID) return;
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
return sendEmbed(errorChannel, embed);
|
return sendEmbed(errorChannel, embed);
|
||||||
|
|||||||
@@ -43,18 +43,18 @@ if (!command.vipOnly) return false;
|
|||||||
// The bot's support server
|
// The bot's support server
|
||||||
const guild = cache.guilds.get(configs.supportServerID);
|
const guild = cache.guilds.get(configs.supportServerID);
|
||||||
// If the command author is not in the server they won't have the vip role
|
// If the command author is not in the server they won't have the vip role
|
||||||
const member = guild.members.get(message.author.id) || await getMember(guild.id, message.author.id);
|
const member = message.member || await getMember(guild.id, message.author.id).catch(console.error);
|
||||||
// Member doesn't exist so cancel the command
|
// Member doesn't exist so cancel the command
|
||||||
if (!member) {
|
if (!member) {
|
||||||
sendResponse(message, `sorry, but you can not use this command until you become VIP. **Close the IRIS!!!**`)
|
message.sendResponse(`Sorry, but you can not use this command until you become VIP. **Close the IRIS!!!**`)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has the vip role on the support server given by patreon allow the command
|
// If the user has the vip role on the support server given by patreon allow the command
|
||||||
if (member.roles.includes(configs.roleIDs.patreonVIPRoleID)) return false;
|
if (member.guilds.get(message.guildID)?.roles.includes(configs.roleIDs.patreonVIPRoleID)) return false;
|
||||||
|
|
||||||
// Alert the user they don't have vip and can't use the command
|
// Alert the user they don't have vip and can't use the command
|
||||||
sendResponse(message, `sorry, but you can not use this command until you become a VIP. I'm sorry, Teal'c. We'll go to Disneyland next year. I promise.`)
|
message.sendResponse(`Sorry, but you can not use this command until you become a VIP. I'm sorry, Teal'c. We'll go to Disneyland next year. I promise.`)
|
||||||
// Cancel the command since the user does not have vip
|
// Cancel the command since the user does not have vip
|
||||||
return true;
|
return true;
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ The monitor options are very similar in functionality with the command options.
|
|||||||
Similar to the command name, we will specify a unique name for the monitors. In this case let's call it inviteFilter
|
Similar to the command name, we will specify a unique name for the monitors. In this case let's call it inviteFilter
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botCache.monitors.set("monitorname", {
|
botCache.monitors.set("inviteFilter", {
|
||||||
name: "monitorname",
|
name: "inviteFilter",
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ignore Options
|
### Ignore Options
|
||||||
@@ -72,7 +72,6 @@ botChannelPermissions: ["MANAGE_MESSAGES"]
|
|||||||
```ts
|
```ts
|
||||||
import { botCache, deleteMessage } from "../../deps.ts";
|
import { botCache, deleteMessage } from "../../deps.ts";
|
||||||
import { translate } from "../utils/i18next.ts";
|
import { translate } from "../utils/i18next.ts";
|
||||||
import { sendAlertResponse } from "../utils/helpers.ts";
|
|
||||||
|
|
||||||
botCache.monitors.set("inviteFilter", {
|
botCache.monitors.set("inviteFilter", {
|
||||||
name: "inviteFilter",
|
name: "inviteFilter",
|
||||||
@@ -89,12 +88,11 @@ botCache.monitors.set("inviteFilter", {
|
|||||||
// This message has an invite link, so delete the message.
|
// This message has an invite link, so delete the message.
|
||||||
try {
|
try {
|
||||||
// Delete the invite link
|
// Delete the invite link
|
||||||
deleteMessage(
|
message.delete(
|
||||||
message,
|
|
||||||
translate(message.guildID, `monitors/invitefilter:DELETE_REASON`),
|
translate(message.guildID, `monitors/invitefilter:DELETE_REASON`),
|
||||||
);
|
);
|
||||||
// Send a message to the user so they know why the message was deleted. Then delete the response after 5 seconds to prevent spam.
|
// Send a message to the user so they know why the message was deleted. Then delete the response after 5 seconds to prevent spam.
|
||||||
sendAlertResponse(message, translate(message.guildID, "monitors/invitefilter:DELETE_ALERT_MESSAGE"), 5)
|
message.alertResponse(translate(message.guildID, "monitors/invitefilter:DELETE_ALERT_MESSAGE"), 5)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return botCache.eventHandlers.discordLog(error)
|
return botCache.eventHandlers.discordLog(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
import {
|
import {
|
||||||
ChannelCreatePayload,
|
ChannelCreatePayload,
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
MessageContent,
|
||||||
RawOverwrite,
|
RawOverwrite,
|
||||||
} from "../../types/mod.ts";
|
} from "../../types/mod.ts";
|
||||||
import { cache } from "../../util/cache.ts";
|
import { cache } from "../../util/cache.ts";
|
||||||
import { Collection } from "../../util/collection.ts";
|
import { Collection } from "../../util/collection.ts";
|
||||||
import { createNewProp } from "../../util/utils.ts";
|
import { createNewProp } from "../../util/utils.ts";
|
||||||
import { cacheHandlers } from "../controllers/cache.ts";
|
import { cacheHandlers } from "../controllers/cache.ts";
|
||||||
|
import { sendMessage } from "../handlers/channel.ts";
|
||||||
import { Guild } from "./guild.ts";
|
import { Guild } from "./guild.ts";
|
||||||
import { Message } from "./message.ts";
|
import { Message } from "./message.ts";
|
||||||
|
|
||||||
const baseChannel: any = {
|
const baseChannel: Partial<Channel> = {
|
||||||
get guild() {
|
get guild() {
|
||||||
return cache.guilds.get(this.guildID);
|
return cache.guilds.get(this.guildID!);
|
||||||
},
|
},
|
||||||
get messages() {
|
get messages() {
|
||||||
return cache.messages.filter((m) => m.channelID === this.id);
|
return cache.messages.filter((m) => m.channelID === this.id!);
|
||||||
},
|
},
|
||||||
get mention() {
|
get mention() {
|
||||||
return `<#${this.id}>`;
|
return `<#${this.id!}>`;
|
||||||
},
|
},
|
||||||
|
send(content) {
|
||||||
|
return sendMessage(this.id!, content);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function createChannel(
|
export async function createChannel(
|
||||||
@@ -107,4 +112,9 @@ export interface Channel {
|
|||||||
messages: Collection<string, Message>;
|
messages: Collection<string, Message>;
|
||||||
/** The mention of the channel */
|
/** The mention of the channel */
|
||||||
mention: string;
|
mention: string;
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||||
|
send(content: string | MessageContent): Promise<Message>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,9 +93,9 @@ const baseMessage: Partial<Message> = {
|
|||||||
return sendMessage(this.channelID!, content);
|
return sendMessage(this.channelID!, content);
|
||||||
},
|
},
|
||||||
alert(content, timeout = 10, reason = "") {
|
alert(content, timeout = 10, reason = "") {
|
||||||
return sendMessage(this.channelID!, content).then((response) =>
|
return sendMessage(this.channelID!, content).then((response) => {
|
||||||
response.delete(reason, timeout * 1000).catch(console.error)
|
response.delete(reason, timeout * 1000).catch(console.error);
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
alertResponse(content, timeout = 10, reason = "") {
|
alertResponse(content, timeout = 10, reason = "") {
|
||||||
return this.sendResponse!(content).then((response) =>
|
return this.sendResponse!(content).then((response) =>
|
||||||
@@ -150,7 +150,7 @@ export async function createMessage(data: MessageCreateOptions) {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
return message;
|
return message as Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Message {
|
export interface Message {
|
||||||
|
|||||||
Reference in New Issue
Block a user