Compare commits

..

No commits in common. "main" and "v3.1.0" have entirely different histories.
main ... v3.1.0

68 changed files with 3329 additions and 9446 deletions

View file

@ -7,16 +7,15 @@
# any secret values. # any secret values.
BOT_TOKEN= BOT_TOKEN=
BOT_VER=3.2.1 BOT_VER=3.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_DATE=08 May 2023
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=682942374040961060 BOT_CLIENTID=682942374040961060
ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
CACHE_INTERVAL=1800000 # 30 minutes
DB_HOST=127.0.0.1 DB_HOST=127.0.0.1
DB_PORT=3101 DB_PORT=3101
DB_NAME=vylbot DB_NAME=vylbot

View file

@ -7,7 +7,7 @@ steps:
- name: deploy - name: deploy
image: appleboy/drone-ssh image: appleboy/drone-ssh
settings: settings:
host: 192.168.1.115 host: 192.168.68.121
username: vylpes username: vylpes
password: password:
from_secret: ssh_password from_secret: ssh_password
@ -28,7 +28,7 @@ steps:
- name: stage - name: stage
image: appleboy/drone-ssh image: appleboy/drone-ssh
settings: settings:
host: 192.168.1.115 host: 192.168.68.121
username: vylpes username: vylpes
password: password:
from_secret: ssh_password from_secret: ssh_password
@ -54,11 +54,11 @@ steps:
- yarn install --frozen-lockfile - yarn install --frozen-lockfile
- yarn build - yarn build
# - name: test - name: test
# image: node image: node
# commands: commands:
# - yarn install --frozen-lockfile - yarn install --frozen-lockfile
# - yarn test - yarn test
trigger: trigger:
branch: branch:

View file

@ -1,67 +0,0 @@
name: Deploy To Production
on:
push:
branches:
- main
jobs:
build:
environment: prod
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test
- name: "Copy files over to location"
run: cp -r . ${{ secrets.PROD_REPO_PATH }}
deploy:
environment: prod
needs: build
runs-on: node
steps:
- uses: https://github.com/appleboy/ssh-action@v1.0.0
env:
DB_NAME: ${{ secrets.PROD_DB_NAME }}
DB_AUTH_USER: ${{ secrets.PROD_DB_AUTH_USER }}
DB_AUTH_PASS: ${{ secrets.PROD_DB_AUTH_PASS }}
DB_HOST: ${{ secrets.PROD_DB_HOST }}
DB_PORT: ${{ secrets.PROD_DB_PORT }}
DB_ROOT_HOST: ${{ secrets.PROD_DB_ROOT_HOST }}
DB_SYNC: ${{ secrets.PROD_DB_SYNC }}
DB_LOGGING: ${{ secrets.PROD_DB_LOGGING }}
DB_DATA_LOCATION: ${{ secrets.PROD_DB_DATA_LOCATION }}
SERVER_PATH: ${{ secrets.PROD_SSH_SERVER_PATH }}
BOT_TOKEN: ${{ secrets.PROD_BOT_TOKEN }}
BOT_VER: ${{ vars.PROD_BOT_VER }}
BOT_AUTHOR: ${{ vars.PROD_BOT_AUTHOR }}
BOT_OWNERID: ${{ vars.PROD_BOT_OWNERID }}
BOT_CLIENTID: ${{ vars.PROD_BOT_CLIENTID }}
ABOUT_FUNDING: ${{ vars.PROD_ABOUT_FUNDING }}
ABOUT_REPO: ${{ vars.PROD_ABOUT_REPO }}
CACHE_INTERVAL: ${{ vars.PROD_CACHE_INTERVAL }}
with:
host: ${{ secrets.PROD_SSH_HOST }}
username: ${{ secrets.PROD_SSH_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
port: ${{ secrets.PROD_SSH_PORT }}
envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL
script: |
source .sshrc \
&& cd /home/vylpes/apps/vylbot/vylbot_prod \
&& docker compose down \
&& (pm2 stop vylbot_prod || true) \
&& (pm2 delete vylbot_prod || true) \
&& docker compose up -d \
&& sleep 10 \
&& npm run db:up \
&& pm2 start --name vylbot_prod dist/vylbot.js

View file

@ -1,67 +0,0 @@
name: Deploy To Stage
on:
push:
branches:
- develop
jobs:
build:
environment: prod
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test
- name: "Copy files over to location"
run: cp -r . ${{ secrets.STAGE_REPO_PATH }}
deploy:
environment: prod
needs: build
runs-on: node
steps:
- uses: https://github.com/appleboy/ssh-action@v1.0.0
env:
DB_NAME: ${{ secrets.STAGE_DB_NAME }}
DB_AUTH_USER: ${{ secrets.STAGE_DB_AUTH_USER }}
DB_AUTH_PASS: ${{ secrets.STAGE_DB_AUTH_PASS }}
DB_HOST: ${{ secrets.STAGE_DB_HOST }}
DB_PORT: ${{ secrets.STAGE_DB_PORT }}
DB_ROOT_HOST: ${{ secrets.STAGE_DB_ROOT_HOST }}
DB_SYNC: ${{ secrets.STAGE_DB_SYNC }}
DB_LOGGING: ${{ secrets.STAGE_DB_LOGGING }}
DB_DATA_LOCATION: ${{ secrets.STAGE_DB_DATA_LOCATION }}
SERVER_PATH: ${{ secrets.STAGE_SSH_SERVER_PATH }}
BOT_TOKEN: ${{ secrets.STAGE_BOT_TOKEN }}
BOT_VER: ${{ vars.STAGE_BOT_VER }}
BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }}
BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }}
BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }}
ABOUT_FUNDING: ${{ vars.STAGE_ABOUT_FUNDING }}
ABOUT_REPO: ${{ vars.STAGE_ABOUT_REPO }}
CACHE_INTERVAL: ${{ vars.STAGE_CACHE_INTERVAL }}
with:
host: ${{ secrets.STAGE_SSH_HOST }}
username: ${{ secrets.STAGE_SSH_USER }}
key: ${{ secrets.STAGE_SSH_KEY }}
port: ${{ secrets.STAGE_SSH_PORT }}
envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL
script: |
source .sshrc \
&& cd /home/vylpes/apps/vylbot/vylbot_stage \
&& docker compose down \
&& (pm2 stop vylbot_stage || true) \
&& (pm2 delete vylbot_stage || true) \
&& docker compose up -d \
&& sleep 10 \
&& npm run db:up \
&& pm2 start --name vylbot_stage dist/vylbot.js

View file

@ -1,24 +0,0 @@
name: Test
on:
push:
branches:
- feature/*
- hotfix/*
- renovate/*
jobs:
build:
environment: stage
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test

View file

@ -24,6 +24,6 @@ Please describe the tests that you ran to verify the changes. Provide instructio
- [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation - [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings - [ ] My changes generate no new warnings
- [ ] I have added tests that provide my fix is effective or that my feature works - [ ] I have added tests that provde my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes - [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules - [ ] Any dependent changes have been merged and published in downstream modules

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**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.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

16
.github/ISSUE_TEMPLATE/epic.md vendored Normal file
View file

@ -0,0 +1,16 @@
---
name: Epic
about: Agile Epic
title: ''
labels: epic
assignees: ''
---
*Description here*
## Stories
*Stories linked to this epic*
## Bugs
*Bugs linked to this epic*

View file

@ -0,0 +1,20 @@
---
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.

24
.github/ISSUE_TEMPLATE/user-story.md vendored Normal file
View file

@ -0,0 +1,24 @@
---
name: User Story
about: Agile User Story
title: ''
labels: needs criteria, story
assignees: ''
---
Epic Link: N/A
Story Points: N/A
---
*Description here*
## Acceptance Criteria
*Add your ACs here*
## Notes
*Any extra information*
## Subtasks
*Add technical subtasks here*

30
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,30 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
# Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules

0
.gitlab/.gitkeep Normal file
View file

View file

View file

@ -0,0 +1,29 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
- This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
# Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependant changes have been merged and published in downstream modules

24
.prod.env Normal file
View file

@ -0,0 +1,24 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=3.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=680083120896081954
ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
DB_HOST=127.0.0.1
DB_PORT=3121
DB_NAME=vylbot
DB_AUTH_USER=prod
DB_AUTH_PASS=prod
DB_SYNC=false
DB_LOGGING=false

24
.stage.env Normal file
View file

@ -0,0 +1,24 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=3.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=1016767908740857949
ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
DB_HOST=127.0.0.1
DB_PORT=3111
DB_NAME=vylbot
DB_AUTH_USER=stage
DB_AUTH_PASS=stage
DB_SYNC=false
DB_LOGGING=false

View file

@ -26,11 +26,11 @@ event.message.delete.channel: Sets the channel the bot will log message delete e
event.message.update.enabled: Enables/Disables the message delete log event (Default: "false") event.message.update.enabled: Enables/Disables the message delete log event (Default: "false")
event.message.update.channel: Sets the channel the bot will log message delete events to (Default: "message-logs") event.message.update.channel: Sets the channel the bot will log message delete events to (Default: "message-logs")
event.member.add.enabled: Enables/Disables the member join log event (Default: "false") event.member.add.enabled: Enables/Disables the message delete log event (Default: "false")
event.member.add.channel: Sets the channel the bot will log member join events to (Default: "member-logs") event.member.add.channel: Sets the channel the bot will log message delete events to (Default: "member-logs")
event.member.remove.enabled: Enables/Disables the member leave log event (Default: "false") event.member.remove.enabled: Enables/Disables the message delete log event (Default: "false")
event.member.remove.channel: Sets the channel the bot will log member leave events to (Default: "member-logs") event.member.remove.channel: Sets the channel the bot will log message delete events to (Default: "member-logs")
event.member.update.enabled: Enables/Disables the member update log event (Default: "false") event.member.update.enabled: Enables/Disables the message delete log event (Default: "false")
event.member.update.channel: Sets the channel the bot will log member update events to (Default: "member-logs") event.member.update.channel: Sets the channel the bot will log message delete events to (Default: "member-logs")

View file

@ -1,2 +0,0 @@
ALTER TABLE server
ADD LastCached datetime NOT NULL DEFAULT '2024-03-01 18:10:04';

31
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
prod_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=vylbot
- MYSQL_USER=prod
- MYSQL_PASSWORD=prod
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3121:3306"
volumes:
- prod_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3122:80"
environment:
- PMA_ARBITRARY=1

31
docker-compose.stage.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
stage_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=vylbot
- MYSQL_USER=stage
- MYSQL_PASSWORD=stage
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3111:3306"
volumes:
- stage_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3112:80"
environment:
- PMA_ARBITRARY=1

View file

@ -1,17 +1,31 @@
version: "3.9" version: "3.9"
volumes:
dev_database_data:
services: services:
# discord:
# build: .
database: database:
image: mysql/mysql-server image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password command: --default-authentication-plugin=mysql_native_password
restart: always restart: always
environment: environment:
- MYSQL_DATABASE=$DB_NAME - MYSQL_DATABASE=vylbot
- MYSQL_USER=$DB_AUTH_USER - MYSQL_USER=dev
- MYSQL_PASSWORD=$DB_AUTH_PASS - MYSQL_PASSWORD=dev
- MYSQL_ROOT_PASSWORD=$DB_AUTH_PASS - MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=$DB_ROOT_HOST - MYSQL_ROOT_HOST=0.0.0.0
ports: ports:
- "$DB_PORT:3306" - "3101:3306"
volumes: volumes:
- $DB_DATA_LOCATION:/var/lib/mysql - dev_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3102:80"
environment:
- PMA_ARBITRARY=1

8815
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "vylbot-app", "name": "vylbot-app",
"version": "3.2.2", "version": "3.1.0",
"description": "A discord bot made for Vylpes' Den", "description": "A discord bot made for Vylpes' Den",
"main": "./dist/vylbot", "main": "./dist/vylbot",
"typings": "./dist", "typings": "./dist",
@ -8,11 +8,9 @@
"clean": "rm -rf node_modules/ dist/", "clean": "rm -rf node_modules/ dist/",
"build": "tsc", "build": "tsc",
"start": "node ./dist/vylbot", "start": "node ./dist/vylbot",
"test": "echo true", "test": "jest",
"db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js", "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js",
"db:down": "typeorm migration:revert -d dist/database/dataSources/appDataSource.js", "db:down": "typeorm migration:revert -d dist/database/dataSources/appDataSource.js"
"db:create": "typeorm migration:create ./src/database/migrations",
"release": "np --no-publish"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,7 +25,7 @@
"homepage": "https://github.com/Vylpes/vylbot-app", "homepage": "https://github.com/Vylpes/vylbot-app",
"funding": "https://ko-fi.com/vylpes", "funding": "https://ko-fi.com/vylpes",
"dependencies": { "dependencies": {
"@discordjs/rest": "^2.0.0", "@discordjs/rest": "^1.1.0",
"@types/jest": "^29.0.0", "@types/jest": "^29.0.0",
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.0",
"discord.js": "^14.3.0", "discord.js": "^14.3.0",
@ -35,19 +33,14 @@
"emoji-regex": "^10.0.0", "emoji-regex": "^10.0.0",
"jest": "^29.0.0", "jest": "^29.0.0",
"jest-mock-extended": "^3.0.0", "jest-mock-extended": "^3.0.0",
"minimatch": "9.0.3", "minimatch": "9.0.2",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"random-bunny": "^2.1.6", "random-bunny": "^2.0.5",
"ts-jest": "^29.0.0", "ts-jest": "^29.0.0",
"typeorm": "0.3.20" "typeorm": "0.3.17"
},
"resolutions": {
"**/semver": "^7.5.2",
"**/undici": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"np": "^10.0.0",
"typescript": "^5.0.0" "typescript": "^5.0.0"
} }
} }

View file

@ -1,42 +0,0 @@
import { ButtonInteraction, CacheType } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent";
import SettingsHelper from "../helpers/SettingsHelper";
export default class Verify extends ButtonEvent {
public override async execute(interaction: ButtonInteraction<CacheType>) {
if (!interaction.guildId || !interaction.guild) return;
const roleName = await SettingsHelper.GetSetting("verification.role", interaction.guildId);
if (!roleName) return;
const role = interaction.guild.roles.cache.find(x => x.name == roleName);
if (!role) {
await interaction.reply({
content: `Unable to find the role, ${roleName}`,
ephemeral: true,
});
return;
}
const member = interaction.guild.members.cache.find(x => x.id == interaction.user.id);
if (!member || !member.manageable) {
await interaction.reply({
content: "Unable to give role to user",
ephemeral: true,
});
return;
}
await member.roles.add(role);
await interaction.reply({
content: "Given role",
ephemeral: true,
});
}
}

View file

@ -1,4 +1,4 @@
import { Client, Partials } from "discord.js"; import { Client } from "discord.js";
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import { createConnection } from "typeorm"; import { createConnection } from "typeorm";
import { EventType } from "../constants/EventType"; import { EventType } from "../constants/EventType";
@ -9,14 +9,10 @@ import { Command } from "../type/command";
import { Events } from "./events"; import { Events } from "./events";
import { Util } from "./util"; import { Util } from "./util";
import AppDataSource from "../database/dataSources/appDataSource"; import AppDataSource from "../database/dataSources/appDataSource";
import ButtonEventItem from "../contracts/ButtonEventItem";
import { ButtonEvent } from "../type/buttonEvent";
import CacheHelper from "../helpers/CacheHelper";
export class CoreClient extends Client { export class CoreClient extends Client {
private static _commandItems: ICommandItem[]; private static _commandItems: ICommandItem[];
private static _eventItems: IEventItem[]; private static _eventItems: IEventItem[];
private static _buttonEvents: ButtonEventItem[];
private _events: Events; private _events: Events;
private _util: Util; private _util: Util;
@ -29,17 +25,12 @@ export class CoreClient extends Client {
return this._eventItems; return this._eventItems;
} }
public static get buttonEvents(): ButtonEventItem[] { constructor(intents: number[]) {
return this._buttonEvents; super({ intents: intents });
}
constructor(intents: number[], partials: Partials[]) {
super({ intents: intents, partials: partials });
dotenv.config(); dotenv.config();
CoreClient._commandItems = []; CoreClient._commandItems = [];
CoreClient._eventItems = []; CoreClient._eventItems = [];
CoreClient._buttonEvents = [];
this._events = new Events(); this._events = new Events();
this._util = new Util(); this._util = new Util();
@ -60,10 +51,6 @@ export class CoreClient extends Client {
await super.login(process.env.BOT_TOKEN); await super.login(process.env.BOT_TOKEN);
this.guilds.cache.forEach(async (guild) => {
await CacheHelper.UpdateServerCache(guild);
});
this._util.loadEvents(this, CoreClient._eventItems); this._util.loadEvents(this, CoreClient._eventItems);
this._util.loadSlashCommands(this); this._util.loadSlashCommands(this);
} }
@ -86,13 +73,4 @@ export class CoreClient extends Client {
CoreClient._eventItems.push(item); CoreClient._eventItems.push(item);
} }
public static RegisterButtonEvent(buttonId: string, event: ButtonEvent) {
const item: ButtonEventItem = {
ButtonId: buttonId,
Event: event,
};
CoreClient._buttonEvents.push(item);
}
} }

View file

@ -1,18 +1,40 @@
import { Interaction } from "discord.js"; import { Interaction } from "discord.js";
import ChatInputCommand from "./interactionCreate/chatInputCommand"; import ICommandItem from "../contracts/ICommandItem";
import Button from "./interactionCreate/button"; import SettingsHelper from "../helpers/SettingsHelper";
import { CoreClient } from "./client";
export class Events { export class Events {
public async onInteractionCreate(interaction: Interaction) { public async onInteractionCreate(interaction: Interaction) {
if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return; if (!interaction.guildId) return;
if (interaction.isChatInputCommand()) { const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
ChatInputCommand.onChatInput(interaction); const disabledCommands = disabledCommandsString?.split(",");
const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId);
if (disabledCommands?.find(x => x == interaction.commandName)) {
await interaction.reply(disabledCommandsMessage || "This command is disabled.");
return;
} }
if (interaction.isButton()) { const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
Button.onButtonClicked(interaction); const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
let itemToUse: ICommandItem;
if (!itemForServer) {
if (!item) {
await interaction.reply('Command not found');
return;
}
itemToUse = item;
} else {
itemToUse = itemForServer;
} }
itemToUse.Command.execute(interaction);
} }
// Emit when bot is logged in and ready to use // Emit when bot is logged in and ready to use

View file

@ -1,17 +0,0 @@
import { ButtonInteraction } from "discord.js";
import { CoreClient } from "../client";
export default class Button {
public static async onButtonClicked(interaction: ButtonInteraction) {
if (!interaction.isButton) return;
const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]);
if (!item) {
await interaction.reply("Event not found.");
return;
}
item.Event.execute(interaction);
}
}

View file

@ -1,27 +0,0 @@
import { Interaction } from "discord.js";
import { CoreClient } from "../client";
import ICommandItem from "../../contracts/ICommandItem";
export default class ChatInputCommand {
public static async onChatInput(interaction: Interaction) {
if (!interaction.isChatInputCommand()) return;
const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
let itemToUse: ICommandItem;
if (!itemForServer) {
if (!item) {
await interaction.reply("Command not found.");
return;
}
itemToUse = item;
} else {
itemToUse = itemForServer;
}
itemToUse.Command.execute(interaction);
}
}

View file

@ -37,7 +37,7 @@ export class Util {
.flatMap(x => x.Command.CommandBuilder); .flatMap(x => x.Command.CommandBuilder);
if (!client.guilds.cache.has(guild)) continue; if (!client.guilds.cache.has(guild)) continue;
rest.put( rest.put(
Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild),
{ {

View file

@ -6,7 +6,7 @@ export default class AddRole extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('addlobby') .setName('addlobby')
.setDescription('Add lobby channel') .setDescription('Add lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)

View file

@ -7,7 +7,7 @@ export default class ListLobby extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('listlobby') .setName('listlobby')
.setDescription('Lists all channels set up as lobbies') .setDescription('Lists all channels set up as lobbies')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);

View file

@ -6,7 +6,7 @@ export default class Lobby extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('lobby') .setName('lobby')
.setDescription('Attempt to organise a lobby'); .setDescription('Attempt to organise a lobby');
} }

View file

@ -7,7 +7,7 @@ export default class RemoveLobby extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('removelobby') .setName('removelobby')
.setDescription('Remove a lobby channel') .setDescription('Remove a lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)

View file

@ -7,7 +7,7 @@ export default class Entry extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('entry') .setName('entry')
.setDescription('Sends the entry embed') .setDescription('Sends the entry embed')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);

View file

@ -7,7 +7,7 @@ export default class ConfigRole extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('configrole') .setName('configrole')
.setDescription('Toggle your roles') .setDescription('Toggle your roles')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles) .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles)

View file

@ -7,7 +7,7 @@ export default class Role extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('role') .setName('role')
.setDescription('Toggle your roles') .setDescription('Toggle your roles')
.addSubcommand(subcommand => .addSubcommand(subcommand =>

View file

@ -6,7 +6,7 @@ export default class About extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('about') .setName('about')
.setDescription('About VylBot'); .setDescription('About VylBot');
} }

View file

@ -9,7 +9,7 @@ export default class Audits extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("audits") .setName("audits")
.setDescription("View audits of a particular user in the server") .setDescription("View audits of a particular user in the server")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)

View file

@ -9,7 +9,7 @@ export default class Ban extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("ban") .setName("ban")
.setDescription("Ban a member from the server with an optional reason") .setDescription("Ban a member from the server with an optional reason")
.setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers)
@ -44,7 +44,6 @@ export default class Ban extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Banned") .setTitle("Member Banned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",

View file

@ -7,7 +7,7 @@ export default class Bunny extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("bunny") .setName("bunny")
.setDescription("Get a random picture of a rabbit."); .setDescription("Get a random picture of a rabbit.");
} }
@ -15,8 +15,6 @@ export default class Bunny extends Command {
public override async execute(interaction: CommandInteraction) { public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return; if (!interaction.isChatInputCommand()) return;
await interaction.deferReply();
const subreddits = [ const subreddits = [
'rabbits', 'rabbits',
'bunnieswithhats', 'bunnieswithhats',
@ -39,9 +37,9 @@ export default class Bunny extends Command {
.setURL(`https://reddit.com${result.Result!.Permalink}`) .setURL(`https://reddit.com${result.Result!.Permalink}`)
.setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`}); .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`});
await interaction.editReply({ embeds: [ embed ]}); await interaction.reply({ embeds: [ embed ]});
} else { } else {
await interaction.editReply("There was an error running this command."); await interaction.reply("There was an error running this command.");
} }
} }
} }

View file

@ -5,7 +5,7 @@ export default class Clear extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("clear") .setName("clear")
.setDescription("Clears the channel of messages") .setDescription("Clears the channel of messages")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages) .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages)

View file

@ -7,7 +7,7 @@ export default class Code extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('code') .setName('code')
.setDescription('Manage the verification code of the server') .setDescription('Manage the verification code of the server')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)

View file

@ -10,7 +10,7 @@ export default class Config extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('config') .setName('config')
.setDescription('Configure the current server') .setDescription('Configure the current server')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
@ -116,15 +116,9 @@ export default class Config extends Command {
const setting = server.Settings.filter(x => x.Key == key.value)[0]; const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (setting) { if (setting) {
await interaction.reply(`\`${key.value}\`: \`${setting.Value}\``); await interaction.reply(`\`${key}\`: \`${setting.Value}\``);
} else { } else {
var defaultValue = DefaultValues.GetValue(key.value.toString()); await interaction.reply(`\`${key}\`: \`${DefaultValues.GetValue(key.value.toString())}\` <DEFAULT>`);
if (defaultValue) {
await interaction.reply(`\`${key.value}\`: \`${defaultValue}\` <DEFAULT>`);
} else {
await interaction.reply(`\`${key.value}\`: <NONE>`);
}
} }
} }
@ -197,4 +191,4 @@ export default class Config extends Command {
await interaction.reply('Setting has been set.'); await interaction.reply('Setting has been set.');
} }
} }

View file

@ -6,7 +6,7 @@ export default class Disable extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('disable') .setName('disable')
.setDescription('Disables a command') .setDescription('Disables a command')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)

View file

@ -6,7 +6,7 @@ export default class Ignore extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('ignore') .setName('ignore')
.setDescription('Ignore events in this channel') .setDescription('Ignore events in this channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);

View file

@ -9,7 +9,7 @@ export default class Kick extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("kick") .setName("kick")
.setDescription("Kick a member from the server with an optional reason") .setDescription("Kick a member from the server with an optional reason")
.setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers)
@ -44,7 +44,6 @@ export default class Kick extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Kicked") .setTitle("Member Kicked")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",

View file

@ -9,7 +9,7 @@ export default class Mute extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("mute") .setName("mute")
.setDescription("(DEPRECATED) Mute a member in the server with an optional reason") .setDescription("(DEPRECATED) Mute a member in the server with an optional reason")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
@ -42,7 +42,6 @@ export default class Mute extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Muted") .setTitle("Member Muted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",
@ -54,14 +53,7 @@ export default class Mute extends Command {
}, },
]); ]);
const mutedRoleName = await SettingsHelper.GetSetting('role.muted', interaction.guildId); const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
if (!mutedRoleName) {
await interaction.reply('Unable to find configuration. Please contact the bot author.');
return;
}
const mutedRole = interaction.guild.roles.cache.find(role => role.name == mutedRoleName);
if (!mutedRole) { if (!mutedRole) {
await interaction.reply('Muted role not found.'); await interaction.reply('Muted role not found.');

View file

@ -1,91 +0,0 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../type/command";
import { EmbedBuilder } from "@discordjs/builders";
import EmbedColours from "../constants/EmbedColours";
export default class Poll extends Command {
constructor() {
super();
this.CommandBuilder = new SlashCommandBuilder()
.setName('poll')
.setDescription('Run a poll, automatically adding reaction emojis as options')
.addStringOption(option =>
option
.setName('title')
.setDescription('Title of the poll')
.setRequired(true))
.addStringOption(option =>
option
.setName('option1')
.setDescription('Option 1')
.setRequired(true))
.addStringOption(option =>
option
.setName('option2')
.setDescription('Option 2')
.setRequired(true))
.addStringOption(option =>
option
.setName('option3')
.setDescription('Option 3'))
.addStringOption(option =>
option
.setName('option4')
.setDescription('Option 4'))
.addStringOption(option =>
option
.setName('option5')
.setDescription('Option 5'));
}
public override async execute(interaction: CommandInteraction) {
const title = interaction.options.get('title');
const option1 = interaction.options.get('option1');
const option2 = interaction.options.get('option2');
const option3 = interaction.options.get('option3');
const option4 = interaction.options.get('option4');
const option5 = interaction.options.get('option5');
if (!title || !option1 || !option2) return;
const description = [
option1.value as string,
option2.value as string,
option3?.value as string,
option4?.value as string,
option5?.value as string
]
.filter(x => x != null);
const arrayOfNumbers = [
':one:',
':two:',
':three:',
':four:',
':five:',
];
const reactionEmojis = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣"];
description.forEach((value, index) => {
description[index] = `${reactionEmojis[index]} ${description[index]}`;
});
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle(title.value as string)
.setDescription(description.join('\n'))
.setFooter({
text: `Poll by ${interaction.user.username}`,
iconURL: interaction.user.avatarURL()!,
});
const message = await interaction.reply({ embeds: [ embed ]});
description.forEach(async (value, index) => {
await (await message.fetch()).react(reactionEmojis[index]);
});
}
}

View file

@ -1,8 +1,7 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { existsSync, readFileSync } from "fs"; import { existsSync, readFileSync } from "fs";
import EmbedColours from "../constants/EmbedColours"; import EmbedColours from "../constants/EmbedColours";
import { Command } from "../type/command"; import { Command } from "../type/command";
import SettingsHelper from "../helpers/SettingsHelper";
interface IRules { interface IRules {
title?: string; title?: string;
@ -15,36 +14,13 @@ export default class Rules extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('rules') .setName("rules")
.setDescription("Rules-related commands") .setDescription("Send the rules embeds for this server")
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
.addSubcommand(x =>
x
.setName('embeds')
.setDescription('Send the rules embeds for this server'))
.addSubcommand(x =>
x
.setName('access')
.setDescription('Send the server verification embed button'));
} }
public override async execute(interaction: CommandInteraction) { public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
switch (interaction.options.getSubcommand()) {
case "embeds":
await this.SendEmbeds(interaction);
break;
case "access":
await this.SendAccessButton(interaction);
break;
default:
await interaction.reply("Subcommand doesn't exist.");
}
}
private async SendEmbeds(interaction: CommandInteraction) {
if (!interaction.guildId) return; if (!interaction.guildId) return;
if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) { if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) {
@ -95,27 +71,4 @@ export default class Rules extends Command {
await interaction.reply({ embeds: [ successEmbed ], ephemeral: true }); await interaction.reply({ embeds: [ successEmbed ], ephemeral: true });
} }
private async SendAccessButton(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const buttonLabel = await SettingsHelper.GetSetting("rules.access.label", interaction.guildId);
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId('verify')
.setStyle(ButtonStyle.Primary)
.setLabel(buttonLabel || "Verify")
]);
await interaction.channel?.send({
components: [ row ]
});
await interaction.reply({
content: "Success",
ephemeral: true,
});
}
} }

View file

@ -6,7 +6,7 @@ export default class Setup extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName('setup') .setName('setup')
.setDescription('Makes the server ready to be configured') .setDescription('Makes the server ready to be configured')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);

View file

@ -10,7 +10,7 @@ export default class Timeout extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("timeout") .setName("timeout")
.setDescription("Timeouts a user out, sending them a DM with the reason if possible") .setDescription("Timeouts a user out, sending them a DM with the reason if possible")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
@ -64,7 +64,6 @@ export default class Timeout extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Timed Out") .setTitle("Member Timed Out")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",

View file

@ -7,7 +7,7 @@ export default class Unmute extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("unmute") .setName("unmute")
.setDescription("(DEPRECATED) Unmute a member in the server with an optional reason") .setDescription("(DEPRECATED) Unmute a member in the server with an optional reason")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
@ -40,7 +40,6 @@ export default class Unmute extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Unmuted") .setTitle("Member Unmuted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",
@ -52,14 +51,7 @@ export default class Unmute extends Command {
}, },
]); ]);
const mutedRoleName = await SettingsHelper.GetSetting('role.muted', interaction.guildId); const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
if (!mutedRoleName) {
await interaction.reply('Unable to find configuration. Please contact the bot author.');
return;
}
const mutedRole = interaction.guild.roles.cache.find(role => role.name == mutedRoleName);
if (!mutedRole) { if (!mutedRole) {
await interaction.reply('Muted role not found.'); await interaction.reply('Muted role not found.');

View file

@ -9,7 +9,7 @@ export default class Warn extends Command {
constructor() { constructor() {
super(); super();
this.CommandBuilder = new SlashCommandBuilder() super.CommandBuilder = new SlashCommandBuilder()
.setName("warn") .setName("warn")
.setDescription("Warns a member in the server with an optional reason") .setDescription("Warns a member in the server with an optional reason")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
@ -41,7 +41,6 @@ export default class Warn extends Command {
.setColor(EmbedColours.Ok) .setColor(EmbedColours.Ok)
.setTitle("Member Warned") .setTitle("Member Warned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.setThumbnail(targetUser.user.avatarURL())
.addFields([ .addFields([
{ {
name: "Moderator", name: "Moderator",

View file

@ -6,7 +6,7 @@ export default class DefaultValues {
this.SetValues(); this.SetValues();
const res = this.values.find(x => x.Key == key); const res = this.values.find(x => x.Key == key);
if (!res) { if (!res) {
return undefined; return undefined;
} }
@ -31,7 +31,6 @@ export default class DefaultValues {
// Rules (Command) // Rules (Command)
this.values.push({ Key: "rules.file", Value: "data/rules/rules" }); this.values.push({ Key: "rules.file", Value: "data/rules/rules" });
this.values.push({ Key: "rules.access.label", Value: "Verify" });
// Channels // Channels
this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); this.values.push({ Key: "channels.logs.message", Value: "message-logs" });
@ -47,18 +46,18 @@ export default class DefaultValues {
// Event // Event
this.values.push({ Key: "event.message.delete.enabled", Value: "false" }); this.values.push({ Key: "event.message.delete.enabled", Value: "false" });
this.values.push({ Key: "event.message.delete.channel", Value: "message-logs" }); this.values.push({ Key: "event.message.delete.channel", Value: "message-logs" });
this.values.push({ Key: "event.message.update.enabled", Value: "false" }); this.values.push({ Key: "event.message.update.enabled", Value: "false" });
this.values.push({ Key: "event.message.update.channel", Value: "message-logs" }); this.values.push({ Key: "event.message.update.channel", Value: "message-logs" });
this.values.push({ Key: "event.member.add.enabled", Value: "false" }); this.values.push({ Key: "event.member.add.enabled", Value: "false" });
this.values.push({ Key: "event.member.add.channel", Value: "member-logs" }); this.values.push({ Key: "event.member.add.channel", Value: "member-logs" });
this.values.push({ Key: "event.member.remove.enabled", Value: "false" }); this.values.push({ Key: "event.member.remove.enabled", Value: "false" });
this.values.push({ Key: "event.member.remove.channel", Value: "member-logs" }); this.values.push({ Key: "event.member.remove.channel", Value: "member-logs" });
this.values.push({ Key: "event.member.update.enabled", Value: "false" }); this.values.push({ Key: "event.member.update.enabled", Value: "false" });
this.values.push({ Key: "event.member.update.channel", Value: "member-logs" }); this.values.push({ Key: "event.member.remove.channel", Value: "member-logs" });
} }
} }

View file

@ -1,8 +0,0 @@
import { ButtonEvent } from "../type/buttonEvent";
interface ButtonEventItem {
ButtonId: string,
Event: ButtonEvent,
}
export default ButtonEventItem;

View file

@ -1,4 +1,4 @@
import { Column, Entity, OneToMany } from "typeorm"; import { Entity, OneToMany } from "typeorm";
import BaseEntity from "../../contracts/BaseEntity"; import BaseEntity from "../../contracts/BaseEntity";
import Role from "./Role"; import Role from "./Role";
import Setting from "./Setting"; import Setting from "./Setting";
@ -9,22 +9,14 @@ export default class Server extends BaseEntity {
super(); super();
this.Id = serverId; this.Id = serverId;
this.LastCached = new Date();
} }
@Column({ default: "2024-03-01 18:10:04" })
LastCached: Date;
@OneToMany(() => Setting, x => x.Server) @OneToMany(() => Setting, x => x.Server)
Settings: Setting[]; Settings: Setting[];
@OneToMany(() => Role, x => x.Server) @OneToMany(() => Role, x => x.Server)
Roles: Role[]; Roles: Role[];
public UpdateLastCached(lastCached: Date) {
this.LastCached = lastCached;
}
public AddSettingToServer(setting: Setting) { public AddSettingToServer(setting: Setting) {
this.Settings.push(setting); this.Settings.push(setting);
} }

View file

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm"
import MigrationHelper from "../../../helpers/MigrationHelper"
export class AddServerCacheDate1709316734401 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
MigrationHelper.Up('1709316734401-AddServerCacheDate', '3.2.1', [
"01-Server",
], queryRunner);
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}

View file

@ -1,13 +1,10 @@
import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours"; import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import CacheHelper from "../../helpers/CacheHelper";
export default async function GuildMemberAdd(member: GuildMember) { export default async function GuildMemberAdd(member: GuildMember) {
if (!member.guild) return; if (!member.guild) return;
await CacheHelper.UpdateServerCache(member.guild);
const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return; if (!enabled || enabled.toLowerCase() != "true") return;

View file

@ -1,13 +1,10 @@
import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours"; import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import CacheHelper from "../../helpers/CacheHelper";
export default async function GuildMemberRemove(member: GuildMember) { export default async function GuildMemberRemove(member: GuildMember) {
if (!member.guild) return; if (!member.guild) return;
await CacheHelper.UpdateServerCache(member.guild);
const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return; if (!enabled || enabled.toLowerCase() != "true") return;

View file

@ -1,13 +1,8 @@
import { GuildMember } from "discord.js"; import { GuildMember } from "discord.js";
import NicknameChanged from "./GuildMemberUpdate/NicknameChanged"; import NicknameChanged from "./GuildMemberUpdate/NicknameChanged";
import CacheHelper from "../../helpers/CacheHelper";
export default async function GuildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { export default async function GuildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) {
const updatedFromCache = await CacheHelper.UpdateServerCache(newMember.guild); if (oldMember.nickname != newMember.nickname) { // Nickname change
if (updatedFromCache) return;
if (oldMember.nickname !== newMember.nickname) { // Nickname change
await NicknameChanged(oldMember, newMember); await NicknameChanged(oldMember, newMember);
} }
} }

View file

@ -1,14 +1,11 @@
import { Message } from "discord.js"; import { Message } from "discord.js";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import VerificationCheck from "./MessageCreate/VerificationCheck"; import VerificationCheck from "./MessageCreate/VerificationCheck";
import CacheHelper from "../../helpers/CacheHelper";
export default async function MessageCreate(message: Message) { export default async function MessageCreate(message: Message) {
if (!message.guild) return; if (!message.guild) return;
if (message.author.bot) return; if (message.author.bot) return;
await CacheHelper.UpdateServerCache(message.guild);
const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id);
if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") {

View file

@ -2,14 +2,11 @@ import { EmbedBuilder, Message, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours"; import EmbedColours from "../../constants/EmbedColours";
import IgnoredChannel from "../../database/entities/IgnoredChannel"; import IgnoredChannel from "../../database/entities/IgnoredChannel";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import CacheHelper from "../../helpers/CacheHelper";
export default async function MessageDelete(message: Message) { export default async function MessageDelete(message: Message) {
if (!message.guild) return; if (!message.guild) return;
if (message.author.bot) return; if (message.author.bot) return;
await CacheHelper.UpdateServerCache(message.guild);
const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id); const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return; if (!enabled || enabled.toLowerCase() != "true") return;
@ -36,8 +33,8 @@ export default async function MessageDelete(message: Message) {
if (message.attachments.size > 0) { if (message.attachments.size > 0) {
embed.addFields([ embed.addFields([
{ {
name: `Attachments (${message.attachments.size})`, name: "Attachments",
value: `\`\`\`${message.attachments.map(x => x.name).join("\n")}\`\`\`` value: `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``
} }
]); ]);
} }

View file

@ -2,14 +2,10 @@ import { EmbedBuilder, Message, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours"; import EmbedColours from "../../constants/EmbedColours";
import IgnoredChannel from "../../database/entities/IgnoredChannel"; import IgnoredChannel from "../../database/entities/IgnoredChannel";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import CacheHelper from "../../helpers/CacheHelper";
export default async function MessageUpdate(oldMessage: Message, newMessage: Message) { export default async function MessageUpdate(oldMessage: Message, newMessage: Message) {
if (!newMessage.guild) return; if (!newMessage.guild) return;
if (newMessage.author.bot) return; if (newMessage.author.bot) return;
await CacheHelper.UpdateServerCache(newMessage.guild);
if (oldMessage.content == newMessage.content) return; if (oldMessage.content == newMessage.content) return;
const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id); const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id);

View file

@ -1,33 +0,0 @@
import { Guild } from "discord.js";
import Server from "../database/entities/Server";
export default class CacheHelper {
public static async UpdateServerCache(guild: Guild): Promise<boolean> {
const cacheInterval = process.env.CACHE_INTERVAL;
if (!cacheInterval) return false;
let server = await Server.FetchOneById(Server, guild.id);
if (!server) {
server = new Server(guild.id);
await server.Save(Server, server);
await CacheHelper.UpdateCache(guild);
return true;
} else if (server.LastCached.getTime() + Number(cacheInterval) > Date.now()) {
await CacheHelper.UpdateCache(guild);
return true;
}
return false;
}
private static async UpdateCache(guild: Guild) {
console.log(`Updating cache for ${guild.name} (${guild.id})`);
await guild.members.fetch();
}
}

View file

@ -13,7 +13,6 @@ import Disable from "./commands/disable";
import Ignore from "./commands/ignore"; import Ignore from "./commands/ignore";
import Kick from "./commands/kick"; import Kick from "./commands/kick";
import Mute from "./commands/mute"; import Mute from "./commands/mute";
import Poll from "./commands/poll";
import Role from "./commands/Role/role"; import Role from "./commands/Role/role";
import ConfigRole from "./commands/Role/config"; import ConfigRole from "./commands/Role/config";
import Rules from "./commands/rules"; import Rules from "./commands/rules";
@ -37,9 +36,6 @@ import MessageDelete from "./events/MessageEvents/MessageDelete";
import MessageUpdate from "./events/MessageEvents/MessageUpdate"; import MessageUpdate from "./events/MessageEvents/MessageUpdate";
import MessageCreate from "./events/MessageEvents/MessageCreate"; import MessageCreate from "./events/MessageEvents/MessageCreate";
// Button Event Imports
import Verify from "./buttonEvents/verify";
export default class Registry { export default class Registry {
public static RegisterCommands() { public static RegisterCommands() {
CoreClient.RegisterCommand("about", new About()); CoreClient.RegisterCommand("about", new About());
@ -53,7 +49,6 @@ export default class Registry {
CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("ignore", new Ignore());
CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("kick", new Kick());
CoreClient.RegisterCommand("mute", new Mute()); CoreClient.RegisterCommand("mute", new Mute());
CoreClient.RegisterCommand("poll", new Poll());
CoreClient.RegisterCommand("rules", new Rules()); CoreClient.RegisterCommand("rules", new Rules());
CoreClient.RegisterCommand("setup", new Setup()); CoreClient.RegisterCommand("setup", new Setup());
CoreClient.RegisterCommand("timeout", new Timeout()); CoreClient.RegisterCommand("timeout", new Timeout());
@ -87,8 +82,4 @@ export default class Registry {
CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate); CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate);
CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate); CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate);
} }
public static RegisterButtonEvents() {
CoreClient.RegisterButtonEvent("verify", new Verify());
}
} }

View file

@ -1,5 +0,0 @@
import { ButtonInteraction } from "discord.js";
export abstract class ButtonEvent {
abstract execute(interaction: ButtonInteraction): Promise<void>;
}

View file

@ -1,7 +1,7 @@
import { CoreClient } from "./client/client"; import { CoreClient } from "./client/client";
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import registry from "./registry"; import registry from "./registry";
import { IntentsBitField, Partials } from "discord.js"; import { IntentsBitField } from "discord.js";
dotenv.config(); dotenv.config();
@ -30,13 +30,9 @@ const client = new CoreClient([
IntentsBitField.Flags.GuildMessages, IntentsBitField.Flags.GuildMessages,
IntentsBitField.Flags.GuildMembers, IntentsBitField.Flags.GuildMembers,
IntentsBitField.Flags.MessageContent, IntentsBitField.Flags.MessageContent,
], [
Partials.GuildMember,
Partials.User,
]); ]);
registry.RegisterCommands(); registry.RegisterCommands();
registry.RegisterEvents(); registry.RegisterEvents();
registry.RegisterButtonEvents();
client.start(); client.start();

2941
yarn.lock Normal file

File diff suppressed because it is too large Load diff