Merge branch 'dev' into renovate/lint-staged-13.x
This commit is contained in:
commit
188cf9eafc
197 changed files with 2752 additions and 3157 deletions
|
@ -1,5 +1,6 @@
|
||||||
# Custom Dictionary Words
|
# Custom Dictionary Words
|
||||||
Controlpanel
|
Controlpanel
|
||||||
|
cooldown
|
||||||
cpgg
|
cpgg
|
||||||
dagen
|
dagen
|
||||||
discordjs
|
discordjs
|
||||||
|
@ -22,6 +23,7 @@ pino
|
||||||
Poäng
|
Poäng
|
||||||
Profil
|
Profil
|
||||||
rando
|
rando
|
||||||
|
Repliable
|
||||||
satta
|
satta
|
||||||
senaste
|
senaste
|
||||||
Sifell
|
Sifell
|
||||||
|
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,32 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ""
|
|
||||||
labels: "bug"
|
|
||||||
assignees: "VermiumSifell"
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Environment (please complete the following information):**
|
|
||||||
|
|
||||||
- Commit: [git rev-parse HEAD]
|
|
||||||
- Branch: [git branch --show-current]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
name: 🐞 Bug
|
||||||
|
description: File a bug/issue
|
||||||
|
title: "[BUG]: <short_description_of_issue>"
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there an existing issue for this?
|
||||||
|
description: Please search to see if an issue already exists for the bug you encountered.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Current Behavior
|
||||||
|
description: A concise description of what you're experiencing.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps To Reproduce
|
||||||
|
description: Steps to reproduce the behavior.
|
||||||
|
placeholder: |
|
||||||
|
1. In this environment...
|
||||||
|
2. Run '...'
|
||||||
|
3. See error...
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: |
|
||||||
|
examples:
|
||||||
|
- **OS**: Ubuntu 20.04
|
||||||
|
- **Node**: 13.14.0
|
||||||
|
- **npm**: 7.6.3
|
||||||
|
- **xyter**: 7d02cf9
|
||||||
|
value: |
|
||||||
|
- OS: `lsb_release -d`
|
||||||
|
- Node: `node -v`
|
||||||
|
- npm: `npm -v`
|
||||||
|
- xyter: `git rev-parse --short HEAD`
|
||||||
|
render: markdown
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Anything else?
|
||||||
|
description: |
|
||||||
|
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||||
|
|
||||||
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
|
validations:
|
||||||
|
required: false
|
48
.github/ISSUE_TEMPLATE/command_request.yaml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/command_request.yaml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
name: 🤖 Request a new command
|
||||||
|
description: Suggest an command for this project
|
||||||
|
title: "[New Command]: /<command category> [command_group] <command_name> "
|
||||||
|
labels: ["enhancement", "new-command"]
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report! We will use `/manage credits give` as an example for our placeholders.
|
||||||
|
- type: input
|
||||||
|
id: category
|
||||||
|
attributes:
|
||||||
|
label: Category
|
||||||
|
description: Where should we put this command?
|
||||||
|
placeholder: ex. manage
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: group
|
||||||
|
attributes:
|
||||||
|
label: Group
|
||||||
|
description: Does this command belong to this group?
|
||||||
|
placeholder: ex. credits
|
||||||
|
- type: input
|
||||||
|
id: command
|
||||||
|
attributes:
|
||||||
|
label: Command
|
||||||
|
description: What should we call this command?
|
||||||
|
placeholder: ex. give
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: What should the command do?
|
||||||
|
description: Please tell us your concept, how it should work, and if there should be any additional features.
|
||||||
|
placeholder: "I would like to have a command to give users credits, that would make it easier for me to administrate credits! I would like it to add a specified amount of credits to specified user, without taking credits from the executer. Command should require Manage Guild permission. When successful, it should return something like: Added <amount> credits to <user>!"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: 'enhancement'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -5,9 +5,10 @@ config.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
|
||||||
**/config/*.ts
|
config/
|
||||||
!**/config/index.ts
|
|
||||||
!**/config/example.*.ts
|
# Build
|
||||||
|
build/
|
||||||
|
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
|
|
58
README.md
58
README.md
|
@ -4,7 +4,7 @@
|
||||||
<br>
|
<br>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<h3 align=center>An multi-purpose bot built with <a href=https://github.com/discordjs/discord.js>discord.js</a></h3>
|
<h3 align=center>A privacy-focused bot built with <a href=https://github.com/discordjs/discord.js>discord.js</a></h3>
|
||||||
|
|
||||||
<div align=center>
|
<div align=center>
|
||||||
|
|
||||||
|
@ -15,61 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="#about">About</a>
|
|
||||||
•
|
•
|
||||||
<a href="#Features">Features</a>
|
<a href="https://xyter.zyner.org">Documentation</a>
|
||||||
•
|
•
|
||||||
<a href="https://github.com/ZynerOrg/xyter/blob/master/docs/INSTALLATION.md">Installation</a>
|
|
||||||
•
|
|
||||||
<a href="#license">License</a>
|
|
||||||
•
|
|
||||||
<a href="#credits">Credits</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## ❓ About
|
|
||||||
|
|
||||||
Xyter is an open source, multi-purpose Discord bot that is develoepd by students. You can invite it to your Discord server using [this](https://bot.zyner.org) link! It comes packaged with a variety of commands and a multitude of settings that can be tailored to your server's specific needs.
|
|
||||||
|
|
||||||
**To run this on Pterodactyl** please set startup command to `./node_modules/.bin/ts-node src/index.ts`!
|
|
||||||
|
|
||||||
If you liked this repository, feel free to leave a star ⭐ to help promote Xyter!
|
|
||||||
|
|
||||||
**For a more updated documentation visit [this site](https://xyter.zyner.org/)!**
|
|
||||||
|
|
||||||
## ❗ Features
|
|
||||||
|
|
||||||
**10+** commands and counting across **13** different categories!
|
|
||||||
|
|
||||||
- 💰 **Credits**: `balance`, `gift`, `top`, `work`, `give`, `take`, `set` and `transfer`!
|
|
||||||
- 💬 **Counters**: `view`, `add`, `remove`!
|
|
||||||
- 🔨 **Settings**: `guild credits`, `guild pterodactyl`, `guild points` and `user appearence`!
|
|
||||||
- 👑 **Profile**: `view`!
|
|
||||||
- 🖼 **Reputation**: `give`!
|
|
||||||
- 💰 **Shop**: `roles buy`, `roles cancel` and `pterodactyl`!
|
|
||||||
- ❔ **Utilities**: `lookup`, `about` and `stats`!
|
|
||||||
- **Full list** of commands: [here](https://github.com/ZynerOrg/xyter/blob/master/docs/COMMANDS.md).
|
|
||||||
|
|
||||||
Xyter also comes packed with a variety of features, such as:
|
|
||||||
|
|
||||||
- **Slash Commands**
|
|
||||||
- **Multi-language support**.
|
|
||||||
- And much more! There are over **5+** settings to tweak!
|
|
||||||
|
|
||||||
## 📝 To-Do
|
|
||||||
|
|
||||||
- Bug fixes
|
|
||||||
- Code optimisation
|
|
||||||
- New discord features
|
|
||||||
- Suggestions we deem very good.
|
|
||||||
|
|
||||||
Some more is available in issues
|
|
||||||
|
|
||||||
## 📖 License
|
|
||||||
|
|
||||||
Released under the [GPL-3.0 License](https://github.com/ZynerOrg/xyter/blob/master/LICENSE) license.
|
|
||||||
|
|
||||||
## 📜 Credits
|
|
||||||
|
|
||||||
- **[Vermium#9649](https://github.com/VermiumSifell)** - Founder, creator, hoster.
|
|
||||||
- **[Mastergamer433#5762](https://github.com/Mastergamer433)** - Work command for credits.
|
|
||||||
- Want to be on this list, aswell? - Check out the [Contributing page](https://github.com/ZynerOrg/xyter/blob/master/docs/CONTRIBUTING.md).
|
|
||||||
|
|
13
package.json
13
package.json
|
@ -27,16 +27,16 @@
|
||||||
"email": "vermium@zyner.org"
|
"email": "vermium@zyner.org"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "^0.13.0",
|
"@discordjs/builders": "^0.15.0",
|
||||||
"@discordjs/rest": "^0.4.0",
|
"@discordjs/rest": "^0.5.0",
|
||||||
"@types/i18next-fs-backend": "^1.1.2",
|
"@types/i18next-fs-backend": "^1.1.2",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"chance": "^1.1.8",
|
"chance": "^1.1.8",
|
||||||
"common": "^0.2.5",
|
"common": "^0.2.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"discord-api-types": "^0.33.0",
|
"discord-api-types": "^0.32.0",
|
||||||
"discord.js": "^13.6.0",
|
"discord.js": "^13.6.0",
|
||||||
"i18n": "^0.14.2",
|
"i18n": "^0.15.0",
|
||||||
"i18next": "^21.6.13",
|
"i18next": "^21.6.13",
|
||||||
"i18next-async-backend": "^2.0.0",
|
"i18next-async-backend": "^2.0.0",
|
||||||
"i18next-fs-backend": "^1.1.4",
|
"i18next-fs-backend": "^1.1.4",
|
||||||
|
@ -56,15 +56,16 @@
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||||
"@typescript-eslint/parser": "^5.15.0",
|
"@typescript-eslint/parser": "^5.15.0",
|
||||||
"eslint": "8.15.0",
|
"eslint": "8.17.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.26.0",
|
||||||
"eslint-plugin-no-loops": "^0.3.0",
|
"eslint-plugin-no-loops": "^0.3.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"husky": "8.0.1",
|
"husky": "8.0.1",
|
||||||
"jest": "28.0.0",
|
"jest": "28.1.1",
|
||||||
"lint-staged": "13.0.1",
|
"lint-staged": "13.0.1",
|
||||||
|
"nodemon": "^2.0.16",
|
||||||
"prettier": "^2.6.0"
|
"prettier": "^2.6.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|
|
@ -8,4 +8,7 @@ export const guildId = "";
|
||||||
export const hosterName = "someone";
|
export const hosterName = "someone";
|
||||||
|
|
||||||
// Hoster Url
|
// Hoster Url
|
||||||
export const hosterUrl = "scheme://domain.tld";
|
export const hosterUrl = "https://xyter.zyner.org/customization/change-hoster";
|
||||||
|
|
||||||
|
// Winston log level
|
||||||
|
export const logLevel = "info";
|
|
@ -1,22 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import mongoose from "mongoose";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
import { url } from "@config/database";
|
|
||||||
|
|
||||||
export default async () => {
|
|
||||||
await mongoose.connect(url).then(async (connection) => {
|
|
||||||
logger.info(`Connected to database: ${connection.connection.name}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on("error", async (error) => {
|
|
||||||
logger.error(`${error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on("warn", async (warning) => {
|
|
||||||
logger.warn(warning);
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import { Guild } from "discord.js";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import updatePresence from "@helpers/updatePresence";
|
|
||||||
import fetchGuild from "@helpers/fetchGuild";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(guild: Guild) {
|
|
||||||
const { client } = guild;
|
|
||||||
|
|
||||||
logger?.silly(`Added to guild: ${guild.name} (${guild.id})`);
|
|
||||||
|
|
||||||
await fetchGuild(guild);
|
|
||||||
await updatePresence(client);
|
|
||||||
|
|
||||||
logger.silly(`guildCreate: ${guild}`);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import { Guild } from "discord.js";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import updatePresence from "@helpers/updatePresence";
|
|
||||||
import dropGuild from "@helpers/dropGuild";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(guild: Guild) {
|
|
||||||
const { client } = guild;
|
|
||||||
|
|
||||||
logger?.silly(`Deleted from guild: ${guild.name} (${guild.id})`);
|
|
||||||
|
|
||||||
await dropGuild(guild);
|
|
||||||
await updatePresence(client);
|
|
||||||
|
|
||||||
logger.silly(`guildDelete: ${guild}`);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
import logger from "@logger";
|
|
||||||
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
|
|
||||||
|
|
||||||
import guildSchema from "@schemas/guild";
|
|
||||||
|
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
execute: async (member: GuildMember) => {
|
|
||||||
const { footerText, footerIcon, successColor } = await getEmbedConfig(
|
|
||||||
member.guild
|
|
||||||
);
|
|
||||||
|
|
||||||
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
|
|
||||||
|
|
||||||
const { client } = member;
|
|
||||||
|
|
||||||
if (guildData === null) return;
|
|
||||||
|
|
||||||
if (guildData.audits.status !== true) return;
|
|
||||||
if (!guildData.audits.channelId) return;
|
|
||||||
|
|
||||||
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
|
|
||||||
|
|
||||||
if (channel === null) return;
|
|
||||||
|
|
||||||
(channel as TextChannel)
|
|
||||||
.send({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setColor(successColor)
|
|
||||||
.setAuthor({
|
|
||||||
name: "Member Joined",
|
|
||||||
iconURL: member.user.displayAvatarURL(),
|
|
||||||
})
|
|
||||||
.setDescription(`${member.user} ${member.user.tag}`)
|
|
||||||
.addFields([
|
|
||||||
{ name: "Account Age", value: `${member.user.createdAt}` },
|
|
||||||
])
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: footerText,
|
|
||||||
iconURL: footerIcon,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
logger.info(
|
|
||||||
`Audit log sent for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async () => {
|
|
||||||
logger.error(
|
|
||||||
`Audit log failed to send for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import { GuildMember } from "discord.js";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import updatePresence from "@helpers/updatePresence";
|
|
||||||
import fetchUser from "@helpers/fetchUser";
|
|
||||||
import logger from "@logger";
|
|
||||||
import joinMessage from "../guildMemberAdd/joinMessage";
|
|
||||||
import audits from "../guildMemberAdd/audits";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(member: GuildMember) {
|
|
||||||
const { client, user, guild } = member;
|
|
||||||
|
|
||||||
logger?.silly(
|
|
||||||
`New member: ${user.tag} (${user.id}) added to guild: ${guild.name} (${guild.id})`
|
|
||||||
);
|
|
||||||
|
|
||||||
await audits.execute(member);
|
|
||||||
await joinMessage.execute(member);
|
|
||||||
await fetchUser(user, guild);
|
|
||||||
await updatePresence(client);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,55 +0,0 @@
|
||||||
import logger from "@logger";
|
|
||||||
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
|
|
||||||
|
|
||||||
import guildSchema from "@schemas/guild";
|
|
||||||
|
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
execute: async (member: GuildMember) => {
|
|
||||||
const { footerText, footerIcon, errorColor } = await getEmbedConfig(
|
|
||||||
member.guild
|
|
||||||
);
|
|
||||||
|
|
||||||
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
|
|
||||||
|
|
||||||
const { client } = member;
|
|
||||||
|
|
||||||
if (guildData === null) return;
|
|
||||||
|
|
||||||
if (guildData.audits.status !== true) return;
|
|
||||||
if (!guildData.audits.channelId) return;
|
|
||||||
|
|
||||||
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
|
|
||||||
|
|
||||||
if (channel === null) return;
|
|
||||||
|
|
||||||
(channel as TextChannel)
|
|
||||||
.send({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setColor(errorColor)
|
|
||||||
.setAuthor({
|
|
||||||
name: "Member Left",
|
|
||||||
iconURL: member.user.displayAvatarURL(),
|
|
||||||
})
|
|
||||||
.setDescription(`${member.user} ${member.user.tag}`)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: footerText,
|
|
||||||
iconURL: footerIcon,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
logger.info(
|
|
||||||
`Audit log sent for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async () => {
|
|
||||||
logger.error(
|
|
||||||
`Audit log failed to send for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import { GuildMember } from "discord.js";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import updatePresence from "@helpers/updatePresence";
|
|
||||||
import dropUser from "@helpers/dropUser";
|
|
||||||
import logger from "@logger";
|
|
||||||
import leaveMessage from "./leaveMessage";
|
|
||||||
import audits from "./audits";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(member: GuildMember) {
|
|
||||||
const { client, user, guild } = member;
|
|
||||||
|
|
||||||
logger?.silly(
|
|
||||||
`Removed member: ${user.tag} (${user.id}) from guild: ${guild.name} (${guild.id})`
|
|
||||||
);
|
|
||||||
|
|
||||||
await audits.execute(member);
|
|
||||||
await leaveMessage.execute(member);
|
|
||||||
await dropUser(user, guild);
|
|
||||||
await updatePresence(client);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,22 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import mongoose from "mongoose";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
import { url } from "@config/database";
|
|
||||||
|
|
||||||
export default async () => {
|
|
||||||
await mongoose.connect(url).then(async (connection) => {
|
|
||||||
logger.info(`Connected to database: ${connection.connection.name}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on("error", async (error) => {
|
|
||||||
logger.error(`${error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on("warn", async (warning) => {
|
|
||||||
logger.warn(warning);
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,102 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
|
||||||
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
import deferReply from "@root/helpers/deferReply";
|
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
|
||||||
import getCommandMetadata from "@helpers/getCommandMetadata";
|
|
||||||
|
|
||||||
export default async (interaction: CommandInteraction) => {
|
|
||||||
if (!interaction.isCommand()) return;
|
|
||||||
if (interaction.guild == null) return;
|
|
||||||
|
|
||||||
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
|
|
||||||
interaction.guild
|
|
||||||
);
|
|
||||||
|
|
||||||
const { client, guild, commandName, user, memberPermissions } = interaction;
|
|
||||||
|
|
||||||
const currentCommand = client.commands.get(commandName);
|
|
||||||
|
|
||||||
if (currentCommand == null) {
|
|
||||||
logger.silly(`Command ${commandName} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = await getCommandMetadata(interaction, currentCommand);
|
|
||||||
|
|
||||||
await deferReply(interaction, metadata.ephemeral || false);
|
|
||||||
|
|
||||||
if (
|
|
||||||
metadata.permissions &&
|
|
||||||
metadata.guildOnly &&
|
|
||||||
!memberPermissions?.has(metadata.permissions)
|
|
||||||
) {
|
|
||||||
return interaction?.editReply({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setTitle("[:x:] Permission")
|
|
||||||
.setDescription(`You do not have the permission to manage the bot.`)
|
|
||||||
.setTimestamp(new Date())
|
|
||||||
.setColor(errorColor)
|
|
||||||
.setFooter({ text: footerText, iconURL: footerIcon }),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.guildOnly) {
|
|
||||||
if (!guild) {
|
|
||||||
logger.debug(`Guild is null`);
|
|
||||||
|
|
||||||
return interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setDescription("This command is only available for guild")
|
|
||||||
.setColor(errorColor)
|
|
||||||
.setTimestamp(new Date())
|
|
||||||
.setFooter({ text: footerText, iconURL: footerIcon }),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.dmOnly) {
|
|
||||||
if (guild) {
|
|
||||||
logger.silly(`Guild exist`);
|
|
||||||
|
|
||||||
return interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setDescription("This command is only available in DM.")
|
|
||||||
.setColor(errorColor)
|
|
||||||
.setTimestamp(new Date())
|
|
||||||
.setFooter({ text: footerText, iconURL: footerIcon }),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await currentCommand
|
|
||||||
.execute(interaction)
|
|
||||||
.then(async () => {
|
|
||||||
return logger?.silly(
|
|
||||||
`Command: ${commandName} executed in guild: ${guild?.name} (${guild?.id}) by user: ${user?.tag} (${user?.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (error: any) => {
|
|
||||||
logger?.error(`${error}`);
|
|
||||||
|
|
||||||
return interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setTitle("Error")
|
|
||||||
.setDescription(
|
|
||||||
`There was an error executing the command: **${currentCommand?.data?.name}**.`
|
|
||||||
)
|
|
||||||
.setColor(errorColor)
|
|
||||||
.setTimestamp(new Date())
|
|
||||||
.setFooter({ text: footerText, iconURL: footerIcon }),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
// 3rd party dependencies
|
|
||||||
import { CommandInteraction } from "discord.js";
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
import isCommand from "@root/events/interactionCreate/components/isCommand";
|
|
||||||
import logger from "@logger";
|
|
||||||
import audits from "./audits";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(interaction: CommandInteraction) {
|
|
||||||
const { guild, id } = interaction;
|
|
||||||
|
|
||||||
logger?.silly(
|
|
||||||
`New interaction: ${id} in guild: ${guild?.name} (${guild?.id})`
|
|
||||||
);
|
|
||||||
|
|
||||||
await audits.execute(interaction);
|
|
||||||
await isCommand(interaction);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { Message } from "discord.js";
|
|
||||||
import modules from "@events/messageCreate/modules";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(message: Message) {
|
|
||||||
await modules.credits.execute(message);
|
|
||||||
await modules.points.execute(message);
|
|
||||||
await modules.counters.execute(message);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,85 +0,0 @@
|
||||||
import logger from "@logger";
|
|
||||||
import timeouts from "@schemas/timeout";
|
|
||||||
import { Message } from "discord.js";
|
|
||||||
|
|
||||||
import fetchUser from "@helpers/fetchUser";
|
|
||||||
import fetchGuild from "@helpers/fetchGuild";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
execute: async (message: Message) => {
|
|
||||||
const { guild, author, content, channel } = message;
|
|
||||||
|
|
||||||
if (guild == null) return;
|
|
||||||
if (author.bot) return;
|
|
||||||
if (channel?.type !== "GUILD_TEXT") return;
|
|
||||||
|
|
||||||
const { id: guildId } = guild;
|
|
||||||
const { id: userId } = author;
|
|
||||||
|
|
||||||
const guildData = await fetchGuild(guild);
|
|
||||||
const userData = await fetchUser(author, guild);
|
|
||||||
|
|
||||||
if (content.length < guildData.credits.minimumLength) return;
|
|
||||||
|
|
||||||
const timeoutData = {
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
timeoutId: "2022-04-14-13-51-00",
|
|
||||||
};
|
|
||||||
|
|
||||||
const timeout = await timeouts.findOne(timeoutData);
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
logger.silly(
|
|
||||||
`User ${userId} in guild ${guildId} is on timeout 2022-04-14-13-51-00`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
userData.credits += guildData.credits.rate;
|
|
||||||
|
|
||||||
await userData
|
|
||||||
.save()
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`User ${userId} in guild ${guildId} has ${userData.credits} credits`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error saving credits for user ${userId} in guild ${guildId}`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await timeouts
|
|
||||||
.create(timeoutData)
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been created`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error creating timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
|
||||||
await timeouts
|
|
||||||
.deleteOne(timeoutData)
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been deleted`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error deleting timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}, guildData.credits.timeout);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,9 +0,0 @@
|
||||||
import counters from "@events/messageCreate/modules/counters";
|
|
||||||
import credits from "@events/messageCreate/modules/credits";
|
|
||||||
import points from "@events/messageCreate/modules/points";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
counters,
|
|
||||||
credits,
|
|
||||||
points,
|
|
||||||
};
|
|
|
@ -1,89 +0,0 @@
|
||||||
import logger from "@logger";
|
|
||||||
import timeouts from "@schemas/timeout";
|
|
||||||
|
|
||||||
import fetchUser from "@helpers/fetchUser";
|
|
||||||
import fetchGuild from "@helpers/fetchGuild";
|
|
||||||
|
|
||||||
import { Message } from "discord.js";
|
|
||||||
export default {
|
|
||||||
execute: async (message: Message) => {
|
|
||||||
const { guild, author, content, channel } = message;
|
|
||||||
|
|
||||||
if (guild == null) return;
|
|
||||||
if (author.bot) return;
|
|
||||||
if (channel?.type !== "GUILD_TEXT") return;
|
|
||||||
|
|
||||||
const { id: guildId } = guild;
|
|
||||||
const { id: userId } = author;
|
|
||||||
|
|
||||||
const guildData = await fetchGuild(guild);
|
|
||||||
const userData = await fetchUser(author, guild);
|
|
||||||
|
|
||||||
if (content.length < guildData.credits.minimumLength) return;
|
|
||||||
|
|
||||||
const timeoutData = {
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
timeoutId: "2022-04-14-14-15-00",
|
|
||||||
};
|
|
||||||
|
|
||||||
const timeout = await timeouts.findOne(timeoutData);
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
logger.silly(
|
|
||||||
`User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id} is on timeout 2022-04-14-14-15-00`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
userData.points += guildData.points.rate;
|
|
||||||
|
|
||||||
await userData
|
|
||||||
.save()
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Successfully saved user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error saving points for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.silly(
|
|
||||||
`User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id}) has ${userData.points} points`
|
|
||||||
);
|
|
||||||
|
|
||||||
await timeouts
|
|
||||||
.create(timeoutData)
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Successfully created timeout for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error creating timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
|
||||||
await timeouts
|
|
||||||
.deleteOne(timeoutData)
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Successfully deleted timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error deleting timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}, guildData.points.timeout);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { Message } from "discord.js";
|
|
||||||
import audits from "@events/messageDelete/audits";
|
|
||||||
import counter from "./modules/counter";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(message: Message) {
|
|
||||||
await audits.execute(message);
|
|
||||||
await counter(message);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { Message } from "discord.js";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Modules
|
|
||||||
import counter from "./modules/counter";
|
|
||||||
|
|
||||||
import audits from "./audits";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async execute(oldMessage: Message, newMessage: Message) {
|
|
||||||
const { author, guild } = newMessage;
|
|
||||||
|
|
||||||
await audits.execute(oldMessage, newMessage);
|
|
||||||
|
|
||||||
logger?.silly(
|
|
||||||
`Message update event fired by ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (author?.bot) return logger?.silly(`Message update event fired by bot`);
|
|
||||||
|
|
||||||
await counter(newMessage);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { Client } from "discord.js";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
import updatePresence from "@helpers/updatePresence";
|
|
||||||
import deployCommands from "@handlers/deployCommands";
|
|
||||||
import devMode from "@handlers/devMode";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
once: true,
|
|
||||||
async execute(client: Client) {
|
|
||||||
logger.info("Ready!");
|
|
||||||
|
|
||||||
await updatePresence(client);
|
|
||||||
await devMode(client);
|
|
||||||
await deployCommands(client);
|
|
||||||
|
|
||||||
client.guilds?.cache.forEach((guild) => {
|
|
||||||
logger.silly(
|
|
||||||
`${client.user?.tag} (${client.user?.id}) is in guild: ${guild.name} (${guild.id}) with member count of ${guild.memberCount}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,36 +0,0 @@
|
||||||
import fs from "fs"; // fs
|
|
||||||
import { Collection } from "discord.js"; // discord.js
|
|
||||||
import { Client } from "@root/types/common/discord";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
export default async (client: Client) => {
|
|
||||||
client.commands = new Collection();
|
|
||||||
|
|
||||||
fs.readdir("./src/plugins", async (error, plugins) => {
|
|
||||||
if (error) {
|
|
||||||
return logger.error(`Error reading plugins: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
plugins.map(async (pluginName, index) => {
|
|
||||||
const plugin = await import(`../plugins/${pluginName}`);
|
|
||||||
|
|
||||||
await client.commands.set(
|
|
||||||
plugin.default.builder.name,
|
|
||||||
plugin.default,
|
|
||||||
plugin.default.metadata
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.verbose(
|
|
||||||
`Loaded plugin ${index + 1}/${plugins.length}: ${pluginName}`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(async () => {
|
|
||||||
logger.info(`Started all ${plugins.length} plugins.`);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(`${err}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { token, clientId } from "@config/discord";
|
|
||||||
import { devMode, guildId } from "@config/other";
|
|
||||||
|
|
||||||
import logger from "../logger";
|
|
||||||
import { Client } from "@root/types/common/discord";
|
|
||||||
import { REST } from "@discordjs/rest";
|
|
||||||
import { Routes } from "discord-api-types/v9";
|
|
||||||
|
|
||||||
export default async (client: Client) => {
|
|
||||||
const pluginList = [] as string[];
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
client.commands.map(async (pluginData: any) => {
|
|
||||||
pluginList.push(pluginData.builder.toJSON());
|
|
||||||
logger.verbose(
|
|
||||||
`Plugin is ready for deployment: ${pluginData.builder.name}`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(async () => {
|
|
||||||
logger.info("All plugins are ready to be deployed.");
|
|
||||||
})
|
|
||||||
.catch(async (error) => {
|
|
||||||
logger.error(`${error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const rest = new REST({ version: "9" }).setToken(token);
|
|
||||||
|
|
||||||
await rest
|
|
||||||
.put(Routes.applicationCommands(clientId), {
|
|
||||||
body: pluginList,
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
logger.info(`Successfully deployed plugins to Discord's API`);
|
|
||||||
})
|
|
||||||
.catch(async (error) => {
|
|
||||||
logger.error(`${error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (devMode) {
|
|
||||||
await rest
|
|
||||||
.put(Routes.applicationGuildCommands(clientId, guildId), {
|
|
||||||
body: pluginList,
|
|
||||||
})
|
|
||||||
.then(async () =>
|
|
||||||
logger.info(`Successfully deployed guild plugins to Discord's API`)
|
|
||||||
)
|
|
||||||
.catch(async (error) => {
|
|
||||||
logger.error(`${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
58
src/handlers/deployCommands/index.ts
Normal file
58
src/handlers/deployCommands/index.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { token, clientId } from "../../config/discord";
|
||||||
|
import { devMode, guildId } from "../../config/other";
|
||||||
|
|
||||||
|
import logger from "../../logger";
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import { REST } from "@discordjs/rest";
|
||||||
|
import { Routes } from "discord-api-types/v9";
|
||||||
|
import { RESTPostAPIApplicationCommandsJSONBody } from "discord-api-types/v10";
|
||||||
|
|
||||||
|
import { ICommand } from "../../interfaces/Command";
|
||||||
|
|
||||||
|
export default async (client: Client) => {
|
||||||
|
const commandList: Array<RESTPostAPIApplicationCommandsJSONBody> = [];
|
||||||
|
|
||||||
|
if (!client.commands) {
|
||||||
|
throw new Error("client.commands is not defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Gathering command list");
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
client.commands.map(async (commandData: ICommand) => {
|
||||||
|
commandList.push(commandData.builder.toJSON());
|
||||||
|
|
||||||
|
logger.verbose(`${commandData.builder.name} pushed to list`);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(async () => {
|
||||||
|
logger.info(`Finished gathering command list.`);
|
||||||
|
})
|
||||||
|
.catch(async (error) => {
|
||||||
|
throw new Error(`Could not gather command list: ${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rest = new REST({ version: "9" }).setToken(token);
|
||||||
|
|
||||||
|
await rest
|
||||||
|
.put(Routes.applicationCommands(clientId), {
|
||||||
|
body: commandList,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.info(`Finished updating command list.`);
|
||||||
|
})
|
||||||
|
.catch(async (error) => {
|
||||||
|
logger.error(`${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (devMode) {
|
||||||
|
await rest
|
||||||
|
.put(Routes.applicationGuildCommands(clientId, guildId), {
|
||||||
|
body: commandList,
|
||||||
|
})
|
||||||
|
.then(async () => logger.info(`Finished updating guild command list.`))
|
||||||
|
.catch(async (error) => {
|
||||||
|
logger.error(`${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,10 +1,10 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { Client } from "discord.js";
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
import logger from "@logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
import { devMode, guildId } from "@config/other";
|
import { devMode, guildId } from "../../config/other";
|
||||||
|
|
||||||
export default async (client: Client) => {
|
export default async (client: Client) => {
|
||||||
if (!devMode) {
|
if (!devMode) {
|
|
@ -1,12 +1,13 @@
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
import { secretKey, algorithm } from "@config/encryption";
|
import { secretKey, algorithm } from "../../config/encryption";
|
||||||
|
|
||||||
|
import { IEncryptionData } from "../../interfaces/EncryptionData";
|
||||||
|
|
||||||
const iv = crypto.randomBytes(16);
|
const iv = crypto.randomBytes(16);
|
||||||
|
|
||||||
const encrypt = (text: any): { iv: any; content: any } => {
|
const encrypt = (text: crypto.BinaryLike): IEncryptionData => {
|
||||||
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
||||||
|
|
||||||
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -15,7 +16,7 @@ const encrypt = (text: any): { iv: any; content: any } => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const decrypt = (hash: any) => {
|
const decrypt = (hash: IEncryptionData) => {
|
||||||
const decipher = crypto.createDecipheriv(
|
const decipher = crypto.createDecipheriv(
|
||||||
algorithm,
|
algorithm,
|
||||||
secretKey,
|
secretKey,
|
|
@ -1,37 +0,0 @@
|
||||||
import fs from "fs"; // fs
|
|
||||||
import { Client } from "discord.js"; // discord.js
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
export default async (client: Client) => {
|
|
||||||
fs.readdir("./src/events", async (error, events) => {
|
|
||||||
if (error) {
|
|
||||||
return logger.error(`Error reading plugins: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
events.map(async (eventName, index) => {
|
|
||||||
const event = await import(`../events/${eventName}`);
|
|
||||||
|
|
||||||
logger.verbose(
|
|
||||||
`Loaded event ${index + 1}/${events.length}: ${eventName}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.once) {
|
|
||||||
return client.once(eventName, async (...args) =>
|
|
||||||
event.default.execute(...args)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.on(eventName, async (...args) =>
|
|
||||||
event.default.execute(...args)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(async () => {
|
|
||||||
logger.info(`Started all ${events.length} events.`);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(`${err}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { Client } from "discord.js";
|
|
||||||
import schedule from "node-schedule";
|
|
||||||
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Jobs
|
|
||||||
import shopRoles from "@jobs/shopRoles";
|
|
||||||
|
|
||||||
export default async (client: Client) => {
|
|
||||||
const expression = "*/5 * * * *";
|
|
||||||
|
|
||||||
schedule.scheduleJob(expression, async () => {
|
|
||||||
logger.info("Running jobs.");
|
|
||||||
|
|
||||||
await shopRoles(client)
|
|
||||||
.then(() => {
|
|
||||||
logger.info("Shop roles job finished.");
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error(`Shop roles job failed: ${err}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
4
src/helpers/addSeconds/index.ts
Normal file
4
src/helpers/addSeconds/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default async (seconds: number, date: Date) => {
|
||||||
|
date.setSeconds(date.getSeconds() + seconds);
|
||||||
|
return date;
|
||||||
|
};
|
3
src/helpers/capitalizeFirstLetter/index.ts
Normal file
3
src/helpers/capitalizeFirstLetter/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default (text: string): string => {
|
||||||
|
return text.charAt(0).toUpperCase() + text.slice(1);
|
||||||
|
};
|
157
src/helpers/cooldown/index.ts
Normal file
157
src/helpers/cooldown/index.ts
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// Dependencies
|
||||||
|
import { CommandInteraction, ButtonInteraction, Message } from "discord.js";
|
||||||
|
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
import getEmbedConfig from "../../helpers/getEmbedConfig";
|
||||||
|
import timeoutSchema from "../../models/timeout";
|
||||||
|
import addSeconds from "../../helpers/addSeconds";
|
||||||
|
|
||||||
|
export const command = async (i: CommandInteraction, cooldown: number) => {
|
||||||
|
const { guild, user, commandId } = i;
|
||||||
|
|
||||||
|
// Check if user has a timeout
|
||||||
|
const hasTimeout = await timeoutSchema.findOne({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: user.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: commandId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If user is not on timeout
|
||||||
|
if (hasTimeout) {
|
||||||
|
const { guildId, userId, timeoutId, createdAt } = hasTimeout;
|
||||||
|
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
|
||||||
|
|
||||||
|
if (!overDue) {
|
||||||
|
const diff = Math.round(
|
||||||
|
(new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`You must wait ${diff} seconds before using this command.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete timeout
|
||||||
|
await timeoutSchema
|
||||||
|
.deleteOne({
|
||||||
|
guildId,
|
||||||
|
userId,
|
||||||
|
timeoutId,
|
||||||
|
cooldown,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.debug(
|
||||||
|
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Create timeout
|
||||||
|
await timeoutSchema.create({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: user.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: commandId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const button = async (i: ButtonInteraction, cooldown: number) => {
|
||||||
|
const { guild, user, customId } = i;
|
||||||
|
|
||||||
|
// Check if user has a timeout
|
||||||
|
const hasTimeout = await timeoutSchema.findOne({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: user.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: customId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If user is not on timeout
|
||||||
|
if (hasTimeout) {
|
||||||
|
const { guildId, userId, timeoutId, createdAt } = hasTimeout;
|
||||||
|
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
|
||||||
|
|
||||||
|
if (!overDue) {
|
||||||
|
const diff = Math.round(
|
||||||
|
(new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`You must wait ${diff} seconds before using this command.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete timeout
|
||||||
|
await timeoutSchema
|
||||||
|
.deleteOne({
|
||||||
|
guildId,
|
||||||
|
userId,
|
||||||
|
timeoutId,
|
||||||
|
cooldown,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.debug(
|
||||||
|
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Create timeout
|
||||||
|
await timeoutSchema.create({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: user.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: customId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const message = async (msg: Message, cooldown: number, id: string) => {
|
||||||
|
const { guild, member } = msg;
|
||||||
|
if (!guild) throw new Error("Guild is undefined");
|
||||||
|
if (!member) throw new Error("Member is undefined");
|
||||||
|
|
||||||
|
// Check if user has a timeout
|
||||||
|
const hasTimeout = await timeoutSchema.findOne({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: member.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If user is not on timeout
|
||||||
|
if (hasTimeout) {
|
||||||
|
const { guildId, userId, timeoutId, createdAt } = hasTimeout;
|
||||||
|
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
|
||||||
|
|
||||||
|
if (!overDue) {
|
||||||
|
const diff = Math.round(
|
||||||
|
(new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`User: ${userId} on timeout-id: ${id} with cooldown: ${cooldown} secs with remaining: ${diff} secs.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete timeout
|
||||||
|
await timeoutSchema
|
||||||
|
.deleteOne({
|
||||||
|
guildId,
|
||||||
|
userId: member.id,
|
||||||
|
timeoutId: id,
|
||||||
|
cooldown,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.debug(
|
||||||
|
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Create timeout
|
||||||
|
await timeoutSchema.create({
|
||||||
|
guildId: guild?.id || "0",
|
||||||
|
userId: member.id,
|
||||||
|
cooldown: cooldown,
|
||||||
|
timeoutId: id,
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,28 +0,0 @@
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
|
||||||
|
|
||||||
export default async (interaction: CommandInteraction, ephemeral: boolean) => {
|
|
||||||
if (interaction.guild == null) return;
|
|
||||||
|
|
||||||
await interaction.deferReply({
|
|
||||||
ephemeral,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { waitColor, footerText, footerIcon } = await getEmbedConfig(
|
|
||||||
interaction.guild
|
|
||||||
);
|
|
||||||
|
|
||||||
await interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
new MessageEmbed()
|
|
||||||
.setFooter({
|
|
||||||
text: footerText,
|
|
||||||
iconURL: footerIcon,
|
|
||||||
})
|
|
||||||
.setTimestamp(new Date())
|
|
||||||
.setTitle("Processing your request")
|
|
||||||
.setColor(waitColor)
|
|
||||||
.setDescription("Please wait..."),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
27
src/helpers/deferReply/index.ts
Normal file
27
src/helpers/deferReply/index.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Interaction, MessageEmbed } from "discord.js";
|
||||||
|
import getEmbedConfig from "../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
|
export default async (interaction: Interaction, ephemeral: boolean) => {
|
||||||
|
if (!interaction.isRepliable())
|
||||||
|
throw new Error(`Cannot reply to an interaction that is not repliable`);
|
||||||
|
|
||||||
|
await interaction.deferReply({
|
||||||
|
ephemeral,
|
||||||
|
});
|
||||||
|
|
||||||
|
const embedConfig = await getEmbedConfig(interaction.guild);
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new MessageEmbed()
|
||||||
|
.setFooter({
|
||||||
|
text: embedConfig.footerText,
|
||||||
|
iconURL: embedConfig.footerIcon,
|
||||||
|
})
|
||||||
|
.setTimestamp(new Date())
|
||||||
|
.setTitle("Processing your request")
|
||||||
|
.setColor(embedConfig.waitColor)
|
||||||
|
.setDescription("Please wait..."),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,11 +1,11 @@
|
||||||
import guildSchema from "@schemas/guild";
|
import guildSchema from "../../models/guild";
|
||||||
import userSchema from "@schemas/user";
|
import userSchema from "../../models/user";
|
||||||
import apiSchema from "@schemas/api";
|
import apiSchema from "../../models/api";
|
||||||
import counterSchema from "@schemas/counter";
|
import counterSchema from "../../models/counter";
|
||||||
import shopRoleSchema from "@schemas/shopRole";
|
import shopRoleSchema from "../../models/shopRole";
|
||||||
import timeoutSchema from "@schemas/timeout";
|
import timeoutSchema from "../../models/timeout";
|
||||||
|
|
||||||
import logger from "@logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
import { Guild } from "discord.js";
|
import { Guild } from "discord.js";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import userSchema from "@schemas/user";
|
import userSchema from "../../models/user";
|
||||||
|
|
||||||
import logger from "@logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
import { Guild, User } from "discord.js";
|
import { Guild, User } from "discord.js";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { footerText, footerIcon } from "@config/embed";
|
import { footerText, footerIcon } from "../../config/embed";
|
||||||
import { MessageEmbed } from "discord.js";
|
import { MessageEmbed } from "discord.js";
|
||||||
|
|
||||||
export default new MessageEmbed()
|
export default new MessageEmbed()
|
|
@ -2,10 +2,10 @@
|
||||||
import { Guild } from "discord.js";
|
import { Guild } from "discord.js";
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
import guildSchema from "@schemas/guild";
|
import guildSchema from "../../models/guild";
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
import logger from "@logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
// Function
|
// Function
|
||||||
export default async (guild: Guild) => {
|
export default async (guild: Guild) => {
|
|
@ -2,10 +2,10 @@
|
||||||
import { Guild, User } from "discord.js";
|
import { Guild, User } from "discord.js";
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
import userSchema from "@schemas/user";
|
import userSchema from "../../models/user";
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
import logger from "@logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
// Function
|
// Function
|
||||||
export default async (user: User, guild: Guild) => {
|
export default async (user: User, guild: Guild) => {
|
|
@ -1,12 +0,0 @@
|
||||||
import { CommandInteraction } from "discord.js";
|
|
||||||
|
|
||||||
export default async (interaction: CommandInteraction, currentCommand: any) => {
|
|
||||||
const subcommand = interaction.options.getSubcommand();
|
|
||||||
const subcommandGroup = interaction.options.getSubcommandGroup(false);
|
|
||||||
|
|
||||||
if (!subcommandGroup) {
|
|
||||||
return currentCommand.modules[subcommand].metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentCommand.modules[subcommandGroup].modules[subcommand].metadata;
|
|
||||||
};
|
|
14
src/helpers/getCommandMetadata/index.ts
Normal file
14
src/helpers/getCommandMetadata/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import { ICommand } from "../../interfaces/Command";
|
||||||
|
|
||||||
|
export default async (
|
||||||
|
interaction: CommandInteraction,
|
||||||
|
currentCommand: ICommand
|
||||||
|
) => {
|
||||||
|
const subcommand = interaction.options.getSubcommand();
|
||||||
|
const subcommandGroup = interaction.options.getSubcommandGroup(false);
|
||||||
|
|
||||||
|
return subcommandGroup
|
||||||
|
? currentCommand.moduleData[subcommandGroup].moduleData[subcommand].metadata
|
||||||
|
: currentCommand.moduleData[subcommand].metadata;
|
||||||
|
};
|
|
@ -1,18 +0,0 @@
|
||||||
import guildSchema from "@schemas/guild";
|
|
||||||
|
|
||||||
import { ColorResolvable, Guild } from "discord.js";
|
|
||||||
|
|
||||||
export default async (guild: Guild) => {
|
|
||||||
const guildConfig = await guildSchema.findOne({ guildId: guild.id });
|
|
||||||
|
|
||||||
if (guildConfig == null)
|
|
||||||
return {
|
|
||||||
successColor: "#22bb33" as ColorResolvable,
|
|
||||||
waitColor: "#f0ad4e" as ColorResolvable,
|
|
||||||
errorColor: "#bb2124" as ColorResolvable,
|
|
||||||
footerIcon: "https://github.com/ZynerOrg.png",
|
|
||||||
footerText: "https://github.com/ZynerOrg/xyter",
|
|
||||||
};
|
|
||||||
|
|
||||||
return guildConfig.embeds;
|
|
||||||
};
|
|
18
src/helpers/getEmbedConfig/index.ts
Normal file
18
src/helpers/getEmbedConfig/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import guildSchema from "../../models/guild";
|
||||||
|
import * as embedConfig from "../../config/embed";
|
||||||
|
|
||||||
|
import { Guild } from "discord.js";
|
||||||
|
|
||||||
|
export default async (guild?: Guild | null) => {
|
||||||
|
if (!guild) {
|
||||||
|
return { ...embedConfig };
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildConfig = await guildSchema.findOne({ guildId: guild.id });
|
||||||
|
if (!guildConfig) {
|
||||||
|
return {
|
||||||
|
...embedConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return guildConfig.embeds;
|
||||||
|
};
|
8
src/helpers/listDir/index.ts
Normal file
8
src/helpers/listDir/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import fs from "fs";
|
||||||
|
const fsPromises = fs.promises;
|
||||||
|
|
||||||
|
export default async (path: string) => {
|
||||||
|
return fsPromises.readdir(path).catch(async (err) => {
|
||||||
|
throw new Error(`Could not list directory: ${path}`, err);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import logger from "@root/logger";
|
import logger from "../../logger";
|
||||||
|
|
||||||
export default (count: number, noun: string, suffix?: string): string => {
|
export default (count: number, noun: string, suffix?: string): string => {
|
||||||
const result = `${count} ${noun}${count !== 1 ? suffix || "s" : ""}`;
|
const result = `${count} ${noun}${count !== 1 ? suffix || "s" : ""}`;
|
|
@ -1,43 +0,0 @@
|
||||||
import sleep from "@helpers/sleep";
|
|
||||||
import logger from "@logger";
|
|
||||||
import Chance from "chance";
|
|
||||||
|
|
||||||
export default async function saveUser(data: any, data2: any) {
|
|
||||||
process.nextTick(
|
|
||||||
async () => {
|
|
||||||
// Chance module
|
|
||||||
const chance = new Chance();
|
|
||||||
|
|
||||||
await sleep(
|
|
||||||
chance.integer({
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
}) *
|
|
||||||
10 +
|
|
||||||
1 * 100
|
|
||||||
); // 100 - 1000 random Number generator
|
|
||||||
data.save((_: any) =>
|
|
||||||
_
|
|
||||||
? logger?.error(
|
|
||||||
`ERROR Occurred while saving data (saveUser) \n${"=".repeat(
|
|
||||||
50
|
|
||||||
)}\n${`${_}\n${"=".repeat(50)}`}`
|
|
||||||
)
|
|
||||||
: logger?.silly(`Saved user: ${data.id} (saveUser)`)
|
|
||||||
);
|
|
||||||
if (data2) {
|
|
||||||
data2.save((_: any) =>
|
|
||||||
_
|
|
||||||
? logger?.error(
|
|
||||||
`ERROR Occurred while saving data (saveUser) \n${"=".repeat(
|
|
||||||
50
|
|
||||||
)}\n${`${_}\n${"=".repeat(50)}`}`
|
|
||||||
)
|
|
||||||
: logger?.silly(`Saved user: ${data2.id} (saveUser)`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
data2
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
export default function sleep(milliseconds: any) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, milliseconds);
|
|
||||||
logger?.silly(`Sleeping for ${milliseconds} milliseconds`);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { Client } from "discord.js";
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Function
|
|
||||||
export default async (client: Client) => {
|
|
||||||
const status = `${client?.guilds?.cache?.size} guilds.`;
|
|
||||||
|
|
||||||
client?.user?.setPresence({
|
|
||||||
activities: [{ type: "WATCHING", name: status }],
|
|
||||||
status: "online",
|
|
||||||
});
|
|
||||||
logger?.debug(`Updated client presence to: ${status}`);
|
|
||||||
};
|
|
20
src/helpers/updatePresence/index.ts
Normal file
20
src/helpers/updatePresence/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Dependencies
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
// Function
|
||||||
|
export default async (client: Client) => {
|
||||||
|
if (!client?.user) throw new Error("Client's user is undefined.");
|
||||||
|
const { guilds } = client;
|
||||||
|
|
||||||
|
const memberCount = guilds.cache.reduce((a, g) => a + g.memberCount, 0);
|
||||||
|
const guildCount = guilds.cache.size;
|
||||||
|
|
||||||
|
const status = `${memberCount} users in ${guildCount} guilds.`;
|
||||||
|
client.user.setPresence({
|
||||||
|
activities: [{ type: "LISTENING", name: status }],
|
||||||
|
status: "online",
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Client's presence is set to "${status}"`);
|
||||||
|
};
|
19
src/index.ts
19
src/index.ts
|
@ -1,13 +1,10 @@
|
||||||
import "tsconfig-paths/register"; // Allows using tsconfig.json paths during runtime
|
import "tsconfig-paths/register"; // Allows using tsconfig.json paths during runtime
|
||||||
|
|
||||||
import { token, intents } from "@config/discord";
|
import { token, intents } from "./config/discord";
|
||||||
|
|
||||||
import { Client } from "discord.js"; // discord.js
|
import { Client } from "discord.js"; // discord.js
|
||||||
|
|
||||||
import database from "@database";
|
import * as managers from "./managers";
|
||||||
import schedules from "@handlers/schedules";
|
|
||||||
import events from "@handlers/events";
|
|
||||||
import commands from "@handlers/commands";
|
|
||||||
|
|
||||||
// Main process that starts all other sub processes
|
// Main process that starts all other sub processes
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
@ -16,17 +13,7 @@ const main = async () => {
|
||||||
intents,
|
intents,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start database manager
|
await managers.start(client);
|
||||||
await database();
|
|
||||||
|
|
||||||
// Start schedule manager
|
|
||||||
await schedules(client);
|
|
||||||
|
|
||||||
// Start command handler
|
|
||||||
await commands(client);
|
|
||||||
|
|
||||||
// Start event handler
|
|
||||||
await events(client);
|
|
||||||
|
|
||||||
// Authorize with Discord's API
|
// Authorize with Discord's API
|
||||||
await client.login(token);
|
await client.login(token);
|
||||||
|
|
7
src/interfaces/Command.ts
Normal file
7
src/interfaces/Command.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
|
||||||
|
export interface ICommand {
|
||||||
|
builder: SlashCommandBuilder;
|
||||||
|
moduleData: any;
|
||||||
|
execute: Promise<void>;
|
||||||
|
}
|
4
src/interfaces/EncryptionData.ts
Normal file
4
src/interfaces/EncryptionData.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface IEncryptionData {
|
||||||
|
iv: string;
|
||||||
|
content: string;
|
||||||
|
}
|
6
src/interfaces/Event.ts
Normal file
6
src/interfaces/Event.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { IEventOptions } from "./EventOptions";
|
||||||
|
|
||||||
|
export interface IEvent {
|
||||||
|
options: IEventOptions;
|
||||||
|
execute: (...args: Promise<void>[]) => Promise<void>;
|
||||||
|
}
|
3
src/interfaces/EventOptions.ts
Normal file
3
src/interfaces/EventOptions.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export interface IEventOptions {
|
||||||
|
type: "on" | "once";
|
||||||
|
}
|
8
src/interfaces/Job.ts
Normal file
8
src/interfaces/Job.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
|
export interface IJob {
|
||||||
|
options: {
|
||||||
|
schedule: string;
|
||||||
|
};
|
||||||
|
execute: (client: Client) => Promise<void>;
|
||||||
|
}
|
9
src/interfaces/ShopRole.ts
Normal file
9
src/interfaces/ShopRole.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Snowflake } from "discord.js";
|
||||||
|
import { Document } from "mongoose";
|
||||||
|
|
||||||
|
export interface IShopRole extends Document {
|
||||||
|
guildId: Snowflake;
|
||||||
|
userId: Snowflake;
|
||||||
|
roleId: Snowflake;
|
||||||
|
lastPayed: Date;
|
||||||
|
}
|
12
src/jobs/shop/index.ts
Normal file
12
src/jobs/shop/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Dependencies
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
|
import * as roles from "./modules/roles";
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
schedule: "*/5 * * * *", // https://crontab.guru/
|
||||||
|
};
|
||||||
|
|
||||||
|
export const execute = async (client: Client) => {
|
||||||
|
await roles.execute(client);
|
||||||
|
};
|
10
src/jobs/shop/modules/roles/components/dueForPayment.ts
Normal file
10
src/jobs/shop/modules/roles/components/dueForPayment.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
|
import { IShopRole } from "../../../../../interfaces/ShopRole";
|
||||||
|
|
||||||
|
export const execute = async (_client: Client, role: IShopRole) => {
|
||||||
|
const { roleId } = role;
|
||||||
|
|
||||||
|
logger.silly(`Shop role ${roleId} is not due for payment.`);
|
||||||
|
};
|
85
src/jobs/shop/modules/roles/components/overDueForPayment.ts
Normal file
85
src/jobs/shop/modules/roles/components/overDueForPayment.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
|
import { IShopRole } from "../../../../../interfaces/ShopRole";
|
||||||
|
import guildSchema from "../../../../../models/guild";
|
||||||
|
import userSchema from "../../../../../models/user";
|
||||||
|
import shopRoleSchema from "../../../../../models/shopRole";
|
||||||
|
|
||||||
|
export const execute = async (client: Client, role: IShopRole) => {
|
||||||
|
const { guildId, userId, roleId } = role;
|
||||||
|
if (!userId) throw new Error("User ID not found for shop role.");
|
||||||
|
|
||||||
|
const guildData = await guildSchema.findOne({ guildId });
|
||||||
|
if (!guildData) throw new Error("Guild not found.");
|
||||||
|
|
||||||
|
const userData = await userSchema.findOne({ guildId, userId });
|
||||||
|
if (!userData) throw new Error("User not found.");
|
||||||
|
|
||||||
|
const rGuild = client.guilds.cache.get(guildId);
|
||||||
|
if (!rGuild) throw new Error("Guild not found.");
|
||||||
|
|
||||||
|
const rMember = await rGuild.members.fetch(userId);
|
||||||
|
if (!rMember) throw new Error("Member not found.");
|
||||||
|
|
||||||
|
const rRole = rMember.roles.cache.get(roleId);
|
||||||
|
if (!rRole) throw new Error("Role not found.");
|
||||||
|
|
||||||
|
logger.debug(`Shop role ${roleId} is due for payment.`);
|
||||||
|
|
||||||
|
const { pricePerHour } = guildData.shop.roles;
|
||||||
|
|
||||||
|
if (userData.credits < pricePerHour) {
|
||||||
|
await rMember.roles
|
||||||
|
.remove(roleId)
|
||||||
|
.then(async () => {
|
||||||
|
await shopRoleSchema
|
||||||
|
.deleteOne({
|
||||||
|
userId,
|
||||||
|
roleId,
|
||||||
|
guildId,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.silly(
|
||||||
|
`Shop role document ${roleId} has been deleted from user ${userId}.`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
throw new Error(
|
||||||
|
`Error deleting shop role document ${roleId} from user ${userId}.`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
throw new Error(
|
||||||
|
`Error removing role ${roleId} from user ${userId}.`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error("User does not have enough credits.");
|
||||||
|
}
|
||||||
|
|
||||||
|
userData.credits -= pricePerHour;
|
||||||
|
await userData
|
||||||
|
.save()
|
||||||
|
.then(async () => {
|
||||||
|
logger.silly(`User ${userId} has been updated.`);
|
||||||
|
|
||||||
|
role.lastPayed = new Date();
|
||||||
|
await role
|
||||||
|
.save()
|
||||||
|
.then(async () => {
|
||||||
|
logger.silly(`Shop role ${roleId} has been updated.`);
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
throw new Error(`Error updating shop role ${roleId}.`, err);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug(`Shop role ${roleId} has been paid.`);
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
throw new Error(`Error updating user ${userId}.`, err);
|
||||||
|
});
|
||||||
|
};
|
32
src/jobs/shop/modules/roles/index.ts
Normal file
32
src/jobs/shop/modules/roles/index.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
|
import { IShopRole } from "../../../../interfaces/ShopRole";
|
||||||
|
import shopRoleSchema from "../../../../models/shopRole";
|
||||||
|
|
||||||
|
import * as overDueForPayment from "./components/overDueForPayment";
|
||||||
|
import * as dueForPayment from "./components/dueForPayment";
|
||||||
|
|
||||||
|
export const execute = async (client: Client) => {
|
||||||
|
const roles = await shopRoleSchema.find();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
roles.map(async (role: IShopRole) => {
|
||||||
|
const { lastPayed } = role;
|
||||||
|
const nextPayment = new Date(
|
||||||
|
lastPayed.setHours(lastPayed.getHours() + 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
if (nextPayment > now) {
|
||||||
|
await dueForPayment.execute(client, role);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPayment < now) {
|
||||||
|
await overDueForPayment.execute(client, role);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,134 +0,0 @@
|
||||||
// Dependencies
|
|
||||||
import { Client } from "discord.js";
|
|
||||||
|
|
||||||
import logger from "@logger";
|
|
||||||
|
|
||||||
// Schemas
|
|
||||||
import userSchema from "@schemas/user";
|
|
||||||
import shopRoleSchema from "@schemas/shopRole";
|
|
||||||
import guildSchema from "@schemas/guild";
|
|
||||||
|
|
||||||
export default async (client: Client) => {
|
|
||||||
const roles = await shopRoleSchema.find();
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
roles.map(async (role) => {
|
|
||||||
const { guildId, userId, roleId } = role;
|
|
||||||
|
|
||||||
const lastPayment = new Date(role.lastPayed);
|
|
||||||
|
|
||||||
const nextPayment = new Date(
|
|
||||||
lastPayment.setHours(lastPayment.getHours() + 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (new Date() < nextPayment) {
|
|
||||||
logger.silly(`Shop role ${roleId} is not due for payment.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const guildData = await guildSchema.findOne({ guildId });
|
|
||||||
|
|
||||||
if (!guildData) {
|
|
||||||
logger.error(`Guild ${guildId} not found.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
logger.error(`User ID not found for shop role ${roleId}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userData = await userSchema.findOne({ guildId, userId });
|
|
||||||
|
|
||||||
if (!userData) {
|
|
||||||
logger.error(`User ${userId} not found for shop role ${roleId}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rGuild = client?.guilds?.cache?.get(guildId);
|
|
||||||
|
|
||||||
const rMember = await rGuild?.members?.fetch(userId);
|
|
||||||
|
|
||||||
if (!rMember) {
|
|
||||||
logger.error(`Member ${userId} not found for shop role ${roleId}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rRole = rMember.roles.cache.get(roleId);
|
|
||||||
|
|
||||||
if (!rMember || !rRole) {
|
|
||||||
logger.error(`Member ${userId} not found for shop role ${roleId}.`);
|
|
||||||
await shopRoleSchema
|
|
||||||
.deleteOne({
|
|
||||||
userId,
|
|
||||||
roleId,
|
|
||||||
guildId,
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(
|
|
||||||
`Shop role document ${roleId} has been deleted from user ${userId}.`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (error) => {
|
|
||||||
logger.error(
|
|
||||||
`Error deleting shop role document ${roleId} from user ${userId}.`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new Date() > nextPayment) {
|
|
||||||
logger.silly(
|
|
||||||
`Shop role ${roleId} is due for payment. Withdrawing credits from user ${userId}.`
|
|
||||||
);
|
|
||||||
|
|
||||||
const { pricePerHour } = guildData.shop.roles;
|
|
||||||
|
|
||||||
if (userData.credits < pricePerHour) {
|
|
||||||
logger.error(
|
|
||||||
`User ${userId} does not have enough credits to pay for shop role ${roleId}.`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!rMember) {
|
|
||||||
logger.error(`Member ${userId} not found for shop role ${roleId}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rMember.roles.remove(roleId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
userData.credits -= pricePerHour;
|
|
||||||
|
|
||||||
await userData
|
|
||||||
.save()
|
|
||||||
.then(async () => {
|
|
||||||
role.lastPayed = new Date();
|
|
||||||
|
|
||||||
await role
|
|
||||||
.save()
|
|
||||||
.then(async () => {
|
|
||||||
logger.silly(`Shop role ${roleId} has been paid for.`);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error saving shop role ${roleId} last payed date.`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.silly(
|
|
||||||
`Shop role ${roleId} has been paid for. Keeping role ${roleId} for user ${userId}.`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(async (err) => {
|
|
||||||
logger.error(
|
|
||||||
`Error saving user ${userId} credits for shop role ${roleId}.`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
35
src/jobs/timeouts/index.ts
Normal file
35
src/jobs/timeouts/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
import timeoutSchema from "../../models/timeout";
|
||||||
|
|
||||||
|
import addSeconds from "../../helpers/addSeconds";
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
schedule: "*/30 * * * *", // https://crontab.guru/
|
||||||
|
};
|
||||||
|
|
||||||
|
export const execute = async () => {
|
||||||
|
const timeouts = await timeoutSchema.find();
|
||||||
|
await Promise.all(
|
||||||
|
timeouts.map(async (timeout) => {
|
||||||
|
const { guildId, userId, timeoutId, cooldown, createdAt } = timeout;
|
||||||
|
|
||||||
|
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
|
||||||
|
|
||||||
|
if (overDue) {
|
||||||
|
timeoutSchema
|
||||||
|
.deleteOne({
|
||||||
|
guildId,
|
||||||
|
userId,
|
||||||
|
timeoutId,
|
||||||
|
cooldown,
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
logger.debug(
|
||||||
|
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,10 +1,13 @@
|
||||||
import winston from "winston";
|
import winston from "winston";
|
||||||
import "winston-daily-rotate-file";
|
import "winston-daily-rotate-file";
|
||||||
|
|
||||||
const { combine, timestamp, printf, colorize, align, json } = winston.format;
|
import { logLevel } from "../config/other";
|
||||||
|
|
||||||
|
const { combine, timestamp, printf, errors, colorize, align, json } =
|
||||||
|
winston.format;
|
||||||
|
|
||||||
export default winston.createLogger({
|
export default winston.createLogger({
|
||||||
level: process.env.LOG_LEVEL || "silly",
|
level: logLevel || "info",
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.DailyRotateFile({
|
new winston.transports.DailyRotateFile({
|
||||||
filename: "logs/combined-%DATE%.log",
|
filename: "logs/combined-%DATE%.log",
|
||||||
|
@ -14,6 +17,7 @@ export default winston.createLogger({
|
||||||
}),
|
}),
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
format: combine(
|
format: combine(
|
||||||
|
errors({ stack: true, trace: true }), // <-- use errors format
|
||||||
colorize({ all: true }),
|
colorize({ all: true }),
|
||||||
timestamp({
|
timestamp({
|
||||||
format: "YYYY-MM-DD HH:MM:ss",
|
format: "YYYY-MM-DD HH:MM:ss",
|
||||||
|
|
35
src/managers/command/index.ts
Normal file
35
src/managers/command/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { Collection, Client } from "discord.js";
|
||||||
|
import listDir from "../../helpers/listDir";
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
import { ICommand } from "../../interfaces/Command";
|
||||||
|
|
||||||
|
export const register = async (client: Client) => {
|
||||||
|
client.commands = new Collection();
|
||||||
|
|
||||||
|
const commandNames = await listDir("plugins/commands");
|
||||||
|
|
||||||
|
if (!commandNames) throw new Error("Could not list commands");
|
||||||
|
|
||||||
|
logger.info(`Loading ${commandNames.length} commands`);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
commandNames.map(async (commandName) => {
|
||||||
|
const command: ICommand = await import(
|
||||||
|
`../../plugins/commands/${commandName}`
|
||||||
|
).catch(async (e) => {
|
||||||
|
throw new Error(`Could not load command: ${commandName}`, e);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.commands.set(command.builder.name, command);
|
||||||
|
|
||||||
|
logger.verbose(`${command.builder.name} loaded`);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(async () => {
|
||||||
|
logger.info(`Finished loading commands.`);
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
throw new Error(`Could not load commands: ${err}`);
|
||||||
|
});
|
||||||
|
};
|
27
src/managers/database/index.ts
Normal file
27
src/managers/database/index.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// 3rd party dependencies
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
import { url } from "../../config/database";
|
||||||
|
|
||||||
|
export const start = async () => {
|
||||||
|
await mongoose
|
||||||
|
.connect(url)
|
||||||
|
.then(async (connection) => {
|
||||||
|
logger.info(`Connected to database: ${connection.connection.name}`);
|
||||||
|
})
|
||||||
|
.catch(async (e) => {
|
||||||
|
logger.error("Could not connect to database", e);
|
||||||
|
});
|
||||||
|
|
||||||
|
mongoose.connection.on("error", async (error) => {
|
||||||
|
logger.error(`${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
mongoose.connection.on("warn", async (warning) => {
|
||||||
|
logger.warn(warning);
|
||||||
|
});
|
||||||
|
};
|
29
src/managers/event/index.ts
Normal file
29
src/managers/event/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/* eslint-disable no-loops/no-loops */
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import listDir from "../../helpers/listDir";
|
||||||
|
import { IEvent } from "../../interfaces/Event";
|
||||||
|
import logger from "../../logger";
|
||||||
|
|
||||||
|
export const register = async (client: Client) => {
|
||||||
|
const eventNames = await listDir("plugins/events");
|
||||||
|
if (!eventNames) return;
|
||||||
|
|
||||||
|
for await (const eventName of eventNames) {
|
||||||
|
const event: IEvent = await import(`../../plugins/events/${eventName}`);
|
||||||
|
const eventExecutor = async (...args: Promise<void>[]) =>
|
||||||
|
event.execute(...args).catch(async (err) => {
|
||||||
|
logger.error(`${err}`);
|
||||||
|
});
|
||||||
|
if (!event.options?.type) return;
|
||||||
|
|
||||||
|
switch (event.options.type) {
|
||||||
|
case "once":
|
||||||
|
client.once(eventName, eventExecutor);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "on":
|
||||||
|
client.on(eventName, eventExecutor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
13
src/managers/index.ts
Normal file
13
src/managers/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
|
import * as database from "./database";
|
||||||
|
import * as schedule from "./schedule";
|
||||||
|
import * as event from "./event";
|
||||||
|
import * as command from "./command";
|
||||||
|
|
||||||
|
export const start = async (client: Client) => {
|
||||||
|
await database.start();
|
||||||
|
await schedule.start(client);
|
||||||
|
await command.register(client);
|
||||||
|
await event.register(client);
|
||||||
|
};
|
29
src/managers/schedule/index.ts
Normal file
29
src/managers/schedule/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import logger from "../../logger";
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
|
||||||
|
import { IJob } from "../../interfaces/Job";
|
||||||
|
|
||||||
|
import listDir from "../../helpers/listDir";
|
||||||
|
|
||||||
|
import schedule from "node-schedule";
|
||||||
|
|
||||||
|
export const start = async (client: Client) => {
|
||||||
|
logger.info("Starting schedule manager...");
|
||||||
|
|
||||||
|
const jobNames = await listDir("jobs");
|
||||||
|
if (!jobNames) return logger.info("No jobs found");
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
jobNames.map(async (jobName) => {
|
||||||
|
const job: IJob = await import(`../../jobs/${jobName}`);
|
||||||
|
|
||||||
|
schedule.scheduleJob(job.options.schedule, async () => {
|
||||||
|
logger.info(`Executed job ${jobName}!`);
|
||||||
|
await job.execute(client);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
).then(async () => {
|
||||||
|
const list = schedule.scheduledJobs;
|
||||||
|
logger.silly(list);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,10 +1,11 @@
|
||||||
import { Snowflake } from "discord.js";
|
import { Snowflake } from "discord.js";
|
||||||
import { model, Schema } from "mongoose";
|
import { model, Schema } from "mongoose";
|
||||||
|
import { IEncryptionData } from "../interfaces/EncryptionData";
|
||||||
|
|
||||||
export interface IApi {
|
export interface IApi {
|
||||||
guildId: Snowflake;
|
guildId: Snowflake;
|
||||||
url: string;
|
url: IEncryptionData;
|
||||||
token: { iv: string; content: string };
|
token: IEncryptionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiSchema = new Schema<IApi>(
|
const apiSchema = new Schema<IApi>(
|
||||||
|
@ -16,11 +17,18 @@ const apiSchema = new Schema<IApi>(
|
||||||
index: true,
|
index: true,
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
|
iv: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
unique: false,
|
unique: false,
|
||||||
index: true,
|
index: true,
|
||||||
default: "https://localhost/api/",
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
token: {
|
token: {
|
||||||
iv: {
|
iv: {
|
||||||
|
@ -28,14 +36,12 @@ const apiSchema = new Schema<IApi>(
|
||||||
required: true,
|
required: true,
|
||||||
unique: false,
|
unique: false,
|
||||||
index: true,
|
index: true,
|
||||||
default: "token",
|
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
unique: false,
|
unique: false,
|
||||||
index: true,
|
index: true,
|
||||||
default: "token",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
|
@ -4,7 +4,10 @@ import { Schema, model } from "mongoose";
|
||||||
export interface ITimeout {
|
export interface ITimeout {
|
||||||
userId: Snowflake;
|
userId: Snowflake;
|
||||||
guildId: Snowflake;
|
guildId: Snowflake;
|
||||||
|
cooldown: number;
|
||||||
timeoutId: string;
|
timeoutId: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeoutSchema = new Schema<ITimeout>(
|
const timeoutSchema = new Schema<ITimeout>(
|
||||||
|
@ -21,6 +24,12 @@ const timeoutSchema = new Schema<ITimeout>(
|
||||||
unique: false,
|
unique: false,
|
||||||
index: true,
|
index: true,
|
||||||
},
|
},
|
||||||
|
cooldown: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
timeoutId: { type: String },
|
timeoutId: { type: String },
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
8
src/plugins/buttons/primary/index.ts
Normal file
8
src/plugins/buttons/primary/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import logger from "../../../logger";
|
||||||
|
|
||||||
|
export const metadata = { guildOnly: false, ephemeral: false };
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
logger.debug("primary button clicked!");
|
||||||
|
};
|
19
src/plugins/commands/counters/index.ts
Normal file
19
src/plugins/commands/counters/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
import logger from "../../../logger";
|
||||||
|
|
||||||
|
import modules from "../../commands/counters/modules";
|
||||||
|
|
||||||
|
export const builder = new SlashCommandBuilder()
|
||||||
|
.setName("counters")
|
||||||
|
.setDescription("View guild counters")
|
||||||
|
|
||||||
|
.addSubcommand(modules.view.builder);
|
||||||
|
|
||||||
|
export const moduleData = modules;
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
if (interaction.options.getSubcommand() === "view") {
|
||||||
|
await modules.view.execute(interaction);
|
||||||
|
}
|
||||||
|
};
|
3
src/plugins/commands/counters/modules/index.ts
Normal file
3
src/plugins/commands/counters/modules/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import view from "./view";
|
||||||
|
|
||||||
|
export default { view };
|
|
@ -1,10 +1,10 @@
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
import { ChannelType } from "discord-api-types/v10";
|
import { ChannelType } from "discord-api-types/v10";
|
||||||
|
|
||||||
import counterSchema from "@schemas/counter";
|
import counterSchema from "../../../../../models/counter";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
metadata: { guildOnly: true, ephemeral: false },
|
metadata: { guildOnly: true, ephemeral: false },
|
||||||
|
@ -25,7 +25,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
execute: async (interaction: CommandInteraction) => {
|
execute: async (interaction: CommandInteraction) => {
|
||||||
if (interaction.guild == null) return;
|
|
||||||
const { errorColor, successColor, footerText, footerIcon } =
|
const { errorColor, successColor, footerText, footerIcon } =
|
||||||
await getEmbedConfig(interaction.guild);
|
await getEmbedConfig(interaction.guild);
|
||||||
const { options, guild } = interaction;
|
const { options, guild } = interaction;
|
37
src/plugins/commands/credits/index.ts
Normal file
37
src/plugins/commands/credits/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import logger from "../../../logger";
|
||||||
|
|
||||||
|
import modules from "./modules";
|
||||||
|
|
||||||
|
export const builder = new SlashCommandBuilder()
|
||||||
|
.setName("credits")
|
||||||
|
.setDescription("Manage your credits.")
|
||||||
|
|
||||||
|
.addSubcommand(modules.balance.builder)
|
||||||
|
.addSubcommand(modules.gift.builder)
|
||||||
|
.addSubcommand(modules.top.builder)
|
||||||
|
.addSubcommand(modules.work.builder);
|
||||||
|
|
||||||
|
export const moduleData = modules;
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
const { options } = interaction;
|
||||||
|
|
||||||
|
switch (options.getSubcommand()) {
|
||||||
|
case "balance":
|
||||||
|
await modules.balance.execute(interaction);
|
||||||
|
break;
|
||||||
|
case "gift":
|
||||||
|
await modules.gift.execute(interaction);
|
||||||
|
break;
|
||||||
|
case "top":
|
||||||
|
await modules.top.execute(interaction);
|
||||||
|
break;
|
||||||
|
case "work":
|
||||||
|
await modules.work.execute(interaction);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.silly(`Unknown subcommand ${options.getSubcommand()}`);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,10 +1,10 @@
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
import logger from "@logger";
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
import fetchUser from "@helpers/fetchUser";
|
import fetchUser from "../../../../../helpers/fetchUser";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
metadata: { guildOnly: true, ephemeral: true },
|
metadata: { guildOnly: true, ephemeral: true },
|
||||||
|
@ -19,7 +19,6 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
execute: async (interaction: CommandInteraction) => {
|
execute: async (interaction: CommandInteraction) => {
|
||||||
if (interaction.guild == null) return;
|
|
||||||
const { errorColor, successColor, footerText, footerIcon } =
|
const { errorColor, successColor, footerText, footerIcon } =
|
||||||
await getEmbedConfig(interaction.guild);
|
await getEmbedConfig(interaction.guild);
|
||||||
const { options, user, guild } = interaction;
|
const { options, user, guild } = interaction;
|
|
@ -2,16 +2,15 @@
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
|
|
||||||
// Configurations
|
// Configurations
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
import logger from "@logger";
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
// Helpers
|
import mongoose from "mongoose";
|
||||||
import saveUser from "@helpers/saveUser";
|
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
import fetchUser from "@helpers/fetchUser";
|
import fetchUser from "../../../../../helpers/fetchUser";
|
||||||
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
|
|
||||||
// Function
|
// Function
|
||||||
|
@ -39,7 +38,6 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
execute: async (interaction: CommandInteraction) => {
|
execute: async (interaction: CommandInteraction) => {
|
||||||
if (interaction.guild == null) return;
|
|
||||||
const { errorColor, successColor, footerText, footerIcon } =
|
const { errorColor, successColor, footerText, footerIcon } =
|
||||||
await getEmbedConfig(interaction.guild);
|
await getEmbedConfig(interaction.guild);
|
||||||
const { options, user, guild, client } = interaction;
|
const { options, user, guild, client } = interaction;
|
||||||
|
@ -184,41 +182,58 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const session = await mongoose.startSession();
|
||||||
|
|
||||||
|
session.startTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
// Withdraw amount from fromUserDB
|
// Withdraw amount from fromUserDB
|
||||||
fromUserDB.credits -= optionAmount;
|
fromUserDB.credits -= optionAmount;
|
||||||
|
|
||||||
// Deposit amount to toUserDB
|
// Deposit amount to toUserDB
|
||||||
toUserDB.credits += optionAmount;
|
toUserDB.credits += optionAmount;
|
||||||
|
|
||||||
// Save users
|
await fromUserDB.save();
|
||||||
await saveUser(fromUserDB, toUserDB).then(async () => {
|
|
||||||
// Get DM user object
|
|
||||||
const dmUser = client.users.cache.get(optionUser.id);
|
|
||||||
|
|
||||||
if (dmUser == null) return;
|
await toUserDB.save();
|
||||||
|
|
||||||
// Send DM to user
|
await session.commitTransaction();
|
||||||
await dmUser
|
} catch (error) {
|
||||||
.send({
|
await session.abortTransaction();
|
||||||
|
session.endSession();
|
||||||
|
logger.error(`${error}`);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
embed
|
embed
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`${
|
"An error occurred while trying to gift credits. Please try again."
|
||||||
user.tag
|
)
|
||||||
} has gifted you ${optionAmount} credits with reason: ${
|
.setColor(errorColor),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// ending the session
|
||||||
|
session.endSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DM user object
|
||||||
|
const dmUser = client.users.cache.get(optionUser.id);
|
||||||
|
|
||||||
|
if (!dmUser) throw new Error("User not found");
|
||||||
|
|
||||||
|
// Send DM to user
|
||||||
|
await dmUser.send({
|
||||||
|
embeds: [
|
||||||
|
embed
|
||||||
|
.setDescription(
|
||||||
|
`${user.tag} has gifted you ${optionAmount} credits with reason: ${
|
||||||
optionReason || "unspecified"
|
optionReason || "unspecified"
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
.setColor(successColor),
|
.setColor(successColor),
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
.catch(async (error) =>
|
|
||||||
logger.error(`[Gift] Error sending DM to user: ${error}`)
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.silly(
|
|
||||||
`[Gift] Successfully gifted ${optionAmount} credits to ${optionUser.tag}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return interaction.editReply({
|
return interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
|
@ -231,6 +246,5 @@ export default {
|
||||||
.setColor(successColor),
|
.setColor(successColor),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
6
src/plugins/commands/credits/modules/index.ts
Normal file
6
src/plugins/commands/credits/modules/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import balance from "./balance";
|
||||||
|
import gift from "./gift";
|
||||||
|
import top from "./top";
|
||||||
|
import work from "./work";
|
||||||
|
|
||||||
|
export default { balance, gift, top, work };
|
|
@ -1,10 +1,10 @@
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
import { CommandInteraction, MessageEmbed } from "discord.js";
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
import logger from "@logger";
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
import userSchema, { IUser } from "@schemas/user";
|
import userSchema, { IUser } from "../../../../../models/user";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
metadata: { guildOnly: true, ephemeral: false },
|
metadata: { guildOnly: true, ephemeral: false },
|
||||||
|
@ -13,7 +13,6 @@ export default {
|
||||||
return command.setName("top").setDescription(`View the top users`);
|
return command.setName("top").setDescription(`View the top users`);
|
||||||
},
|
},
|
||||||
execute: async (interaction: CommandInteraction) => {
|
execute: async (interaction: CommandInteraction) => {
|
||||||
if (interaction.guild == null) return;
|
|
||||||
const { errorColor, successColor, footerText, footerIcon } =
|
const { errorColor, successColor, footerText, footerIcon } =
|
||||||
await getEmbedConfig(interaction.guild);
|
await getEmbedConfig(interaction.guild);
|
||||||
const { guild } = interaction;
|
const { guild } = interaction;
|
||||||
|
@ -55,7 +54,7 @@ export default {
|
||||||
embeds: [
|
embeds: [
|
||||||
embed
|
embed
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Below are the top 10 users in this guild.
|
`Below are the top ten members in this guild.
|
||||||
|
|
||||||
${topTen.map(entry).join("\n")}
|
${topTen.map(entry).join("\n")}
|
||||||
`
|
`
|
|
@ -4,17 +4,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
import Chance from "chance";
|
import Chance from "chance";
|
||||||
|
|
||||||
// Configurations
|
// Configurations
|
||||||
import getEmbedConfig from "@helpers/getEmbedConfig";
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
import logger from "@logger";
|
import logger from "../../../../../logger";
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
import timeoutSchema from "@schemas/timeout";
|
import * as cooldown from "../../../../../helpers/cooldown";
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import fetchUser from "@helpers/fetchUser";
|
import fetchUser from "../../../../../helpers/fetchUser";
|
||||||
import fetchGuild from "@helpers/fetchGuild";
|
import fetchGuild from "../../../../../helpers/fetchGuild";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
metadata: { guildOnly: true, ephemeral: true },
|
metadata: { guildOnly: true, ephemeral: true },
|
||||||
|
@ -23,9 +23,9 @@ export default {
|
||||||
return command.setName("work").setDescription(`Work to earn credits`);
|
return command.setName("work").setDescription(`Work to earn credits`);
|
||||||
},
|
},
|
||||||
execute: async (interaction: CommandInteraction) => {
|
execute: async (interaction: CommandInteraction) => {
|
||||||
if (interaction.guild == null) return;
|
const { successColor, footerText, footerIcon } = await getEmbedConfig(
|
||||||
const { errorColor, successColor, footerText, footerIcon } =
|
interaction.guild
|
||||||
await getEmbedConfig(interaction.guild); // Destructure member
|
); // Destructure member
|
||||||
const { guild, user } = interaction;
|
const { guild, user } = interaction;
|
||||||
|
|
||||||
const embed = new MessageEmbed()
|
const embed = new MessageEmbed()
|
||||||
|
@ -39,33 +39,13 @@ export default {
|
||||||
// Chance module
|
// Chance module
|
||||||
const chance = new Chance();
|
const chance = new Chance();
|
||||||
|
|
||||||
// Check if user has a timeout
|
|
||||||
const isTimeout = await timeoutSchema?.findOne({
|
|
||||||
guildId: guild?.id,
|
|
||||||
userId: user?.id,
|
|
||||||
timeoutId: "2022-03-15-19-16",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (guild === null) {
|
if (guild === null) {
|
||||||
return logger?.silly(`Guild is null`);
|
return logger?.silly(`Guild is null`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const guildDB = await fetchGuild(guild);
|
const guildDB = await fetchGuild(guild);
|
||||||
|
|
||||||
// If user is not on timeout
|
await cooldown.command(interaction, guildDB?.credits?.workTimeout);
|
||||||
if (isTimeout) {
|
|
||||||
logger?.silly(`User ${user?.id} is on timeout`);
|
|
||||||
|
|
||||||
return interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
embed
|
|
||||||
.setDescription(
|
|
||||||
`You are on timeout, please wait ${guildDB?.credits.workTimeout} seconds.`
|
|
||||||
)
|
|
||||||
.setColor(errorColor),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const creditsEarned = chance.integer({
|
const creditsEarned = chance.integer({
|
||||||
min: 0,
|
min: 0,
|
||||||
|
@ -93,23 +73,5 @@ export default {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a timeout for the user
|
|
||||||
await timeoutSchema?.create({
|
|
||||||
guildId: guild?.id,
|
|
||||||
userId: user?.id,
|
|
||||||
timeoutId: "2022-03-15-19-16",
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
|
||||||
logger?.silly(`Removing timeout for user ${user?.id}`);
|
|
||||||
|
|
||||||
// When timeout is out, remove it from the database
|
|
||||||
await timeoutSchema?.deleteOne({
|
|
||||||
guildId: guild?.id,
|
|
||||||
userId: user?.id,
|
|
||||||
timeoutId: "2022-03-15-19-16",
|
|
||||||
});
|
|
||||||
}, guildDB?.credits?.workTimeout);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
22
src/plugins/commands/dns/index.ts
Normal file
22
src/plugins/commands/dns/index.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
import modules from "./modules";
|
||||||
|
export const moduleData = modules;
|
||||||
|
|
||||||
|
export const builder = new SlashCommandBuilder()
|
||||||
|
.setName("dns")
|
||||||
|
.setDescription("DNS commands.")
|
||||||
|
|
||||||
|
.addSubcommand(modules.lookup.builder);
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
switch (interaction.options.getSubcommand()) {
|
||||||
|
case "lookup":
|
||||||
|
return modules.lookup.execute(interaction);
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`Unknown subcommand: ${interaction.options.getSubcommand()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
3
src/plugins/commands/dns/modules/index.ts
Normal file
3
src/plugins/commands/dns/modules/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import lookup from "./lookup";
|
||||||
|
|
||||||
|
export default { lookup };
|
131
src/plugins/commands/dns/modules/lookup/index.ts
Normal file
131
src/plugins/commands/dns/modules/lookup/index.ts
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
|
|
||||||
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
metadata: { guildOnly: false, ephemeral: false },
|
||||||
|
|
||||||
|
builder: (command: SlashCommandSubcommandBuilder) => {
|
||||||
|
return command
|
||||||
|
.setName("lookup")
|
||||||
|
.setDescription(
|
||||||
|
"Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)"
|
||||||
|
)
|
||||||
|
.addStringOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("query")
|
||||||
|
.setDescription("The query you want to look up.")
|
||||||
|
.setRequired(true)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
execute: async (interaction: CommandInteraction) => {
|
||||||
|
const { errorColor, successColor, footerText, footerIcon } =
|
||||||
|
await getEmbedConfig(interaction.guild);
|
||||||
|
const embedTitle = "[:hammer:] Utility (Lookup)";
|
||||||
|
|
||||||
|
const { options } = interaction;
|
||||||
|
const query = options.getString("query");
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.get(`http://ip-api.com/json/${query}`)
|
||||||
|
.then(async (response) => {
|
||||||
|
if (response.data.status !== "success") {
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new MessageEmbed()
|
||||||
|
.setTitle(embedTitle)
|
||||||
|
.setFooter({
|
||||||
|
text: footerText,
|
||||||
|
iconURL: footerIcon,
|
||||||
|
})
|
||||||
|
.setTimestamp(new Date())
|
||||||
|
.setColor(errorColor)
|
||||||
|
.setFooter({ text: footerText, iconURL: footerIcon })
|
||||||
|
.setDescription(
|
||||||
|
`${response?.data?.message}: ${response?.data?.query}`
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new MessageEmbed()
|
||||||
|
.setTitle(embedTitle)
|
||||||
|
.setFooter({
|
||||||
|
text: footerText,
|
||||||
|
iconURL: footerIcon,
|
||||||
|
})
|
||||||
|
.setTimestamp(new Date())
|
||||||
|
.setColor(successColor)
|
||||||
|
.setFields([
|
||||||
|
{
|
||||||
|
name: ":classical_building: AS",
|
||||||
|
value: `${response.data.as || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":classical_building: ISP",
|
||||||
|
value: `${response.data.isp || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":classical_building: Organization",
|
||||||
|
value: `${response.data.org || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":compass: Latitude",
|
||||||
|
value: `${response.data.lat || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":compass: Longitude",
|
||||||
|
value: `${response.data.lon || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":clock4: Timezone",
|
||||||
|
value: `${response.data.timezone || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: Country",
|
||||||
|
value: `${response.data.country || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: Region",
|
||||||
|
value: `${response.data.regionName || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: City",
|
||||||
|
value: `${response.data.city || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: Country Code",
|
||||||
|
value: `${response.data.countryCode || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: Region Code",
|
||||||
|
value: `${response.data.region || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ":globe_with_meridians: ZIP",
|
||||||
|
value: `${response.data.zip || "Unknown"}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
23
src/plugins/commands/fun/index.ts
Normal file
23
src/plugins/commands/fun/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import logger from "../../../logger";
|
||||||
|
|
||||||
|
import modules from "../../commands/fun/modules";
|
||||||
|
|
||||||
|
export const builder = new SlashCommandBuilder()
|
||||||
|
.setName("fun")
|
||||||
|
.setDescription("Fun commands.")
|
||||||
|
|
||||||
|
.addSubcommand(modules.meme.builder);
|
||||||
|
|
||||||
|
export const moduleData = modules;
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
const { options } = interaction;
|
||||||
|
|
||||||
|
if (options.getSubcommand() === "meme") {
|
||||||
|
await modules.meme.execute(interaction);
|
||||||
|
} else {
|
||||||
|
logger.silly(`Unknown subcommand ${options.getSubcommand()}`);
|
||||||
|
}
|
||||||
|
};
|
5
src/plugins/commands/fun/modules/index.ts
Normal file
5
src/plugins/commands/fun/modules/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import meme from "./meme";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
meme,
|
||||||
|
};
|
59
src/plugins/commands/fun/modules/meme/index.ts
Normal file
59
src/plugins/commands/fun/modules/meme/index.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
|
||||||
|
|
||||||
|
import axios from "axios";
|
||||||
|
import { CommandInteraction, MessageEmbed } from "discord.js";
|
||||||
|
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
metadata: { guildOnly: false, ephemeral: false, cooldown: 15 },
|
||||||
|
|
||||||
|
builder: (command: SlashCommandSubcommandBuilder) => {
|
||||||
|
return command.setName("meme").setDescription("Get a meme from r/memes)");
|
||||||
|
},
|
||||||
|
|
||||||
|
execute: async (interaction: CommandInteraction) => {
|
||||||
|
const { guild } = interaction;
|
||||||
|
|
||||||
|
const embedConfig = await getEmbedConfig(guild);
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.get("https://www.reddit.com/r/memes/random/.json")
|
||||||
|
.then(async (res) => {
|
||||||
|
const response = res.data[0].data.children;
|
||||||
|
const content = response[0].data;
|
||||||
|
|
||||||
|
const embed = new MessageEmbed()
|
||||||
|
.setAuthor({
|
||||||
|
name: content.title,
|
||||||
|
iconURL:
|
||||||
|
"https://www.redditinc.com/assets/images/site/reddit-logo.png",
|
||||||
|
url: `https://reddit.com${content.permalink}`,
|
||||||
|
})
|
||||||
|
.setTitle("[:sweat_smile:] Meme")
|
||||||
|
.addFields([
|
||||||
|
{
|
||||||
|
name: "Author",
|
||||||
|
value: `[${content.author}](https://reddit.com/user/${content.author})`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Votes",
|
||||||
|
value: `${content.ups}/${content.downs}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.setTimestamp(new Date())
|
||||||
|
.setImage(content.url)
|
||||||
|
.setFooter({
|
||||||
|
text: embedConfig.footerText,
|
||||||
|
iconURL: embedConfig.footerIcon,
|
||||||
|
})
|
||||||
|
.setColor(embedConfig.successColor);
|
||||||
|
|
||||||
|
return interaction.editReply({ embeds: [embed] });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw new Error(error.message);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
29
src/plugins/commands/manage/index.ts
Normal file
29
src/plugins/commands/manage/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//Dependencies
|
||||||
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
import modules from "../../commands/manage/modules";
|
||||||
|
import logger from "../../../logger";
|
||||||
|
|
||||||
|
export const moduleData = modules;
|
||||||
|
|
||||||
|
// Function
|
||||||
|
export const builder = new SlashCommandBuilder()
|
||||||
|
.setName("manage")
|
||||||
|
.setDescription("Manage the bot.")
|
||||||
|
.addSubcommandGroup(modules.counters.builder)
|
||||||
|
.addSubcommandGroup(modules.credits.builder);
|
||||||
|
|
||||||
|
export const execute = async (interaction: CommandInteraction) => {
|
||||||
|
// Destructure
|
||||||
|
const { options } = interaction;
|
||||||
|
|
||||||
|
if (options?.getSubcommandGroup() === "credits") {
|
||||||
|
return modules.credits.execute(interaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.getSubcommandGroup() === "counters") {
|
||||||
|
return modules.counters.execute(interaction);
|
||||||
|
}
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue