Merge branch 'main' of https://git.radium.wtf/st/twitter
This commit is contained in:
commit
105dd2dc4d
16 files changed
+1393
-34498
No files matched your search
+8
-8
@@ -1,8 +1,8 @@
|
||||
# Discord Bot Token — get yours at https://discord.com/developers/applications
|
||||
BOT_TOKEN=your_bot_token_here
|
||||
|
||||
# Command prefix (default: !)
|
||||
PREFIX=!
|
||||
|
||||
# Guild ID for test server (optional — used for dev utilities)
|
||||
GUILD_ID=your_guild_id_here
|
||||
# Discord Bot Token — get yours at https://discord.com/developers/applications
|
||||
BOT_TOKEN=your_bot_token_here
|
||||
|
||||
# Command prefix (default: !)
|
||||
PREFIX=!
|
||||
|
||||
# Guild ID for test server (optional — used for dev utilities)
|
||||
GUILD_ID=your_guild_id_here
|
||||
+3
-1
@@ -141,4 +141,6 @@ dist
|
||||
|
||||
# Vite logs files
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
items.json
|
||||
db.json
|
||||
@@ -1,121 +1,121 @@
|
||||
# twitter (v3)
|
||||
|
||||
a discord.js v14 moderation (+ more) bot
|
||||
|
||||
---
|
||||
|
||||
## features
|
||||
|
||||
- prefix command system — commands triggered by a configurable prefix (default `!`)
|
||||
- auto-loading — commands and events are loaded automatically from their folders
|
||||
- aliases — each command can define one or more aliases
|
||||
- winston logging — coloured console output + rolling log files (`logs/combined.log`, `logs/error.log`)
|
||||
- global error handling — unhandled rejections and uncaught exceptions are caught and logged
|
||||
|
||||
---
|
||||
|
||||
## project structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── index.js # entry point — creates the client, loads handlers
|
||||
├── commands/
|
||||
│ ├── help.js # !help — lists commands or shows command detail
|
||||
│ └── ping.js # !ping — latency check
|
||||
├── events/
|
||||
│ ├── ready.js # fires once when the bot is online
|
||||
│ └── messageCreate.js # parses every message for prefix commands
|
||||
├── handlers/
|
||||
│ ├── commandHandler.js # reads src/commands/ and registers commands
|
||||
│ └── eventHandler.js # reads src/events/ and registers discord events
|
||||
└── utils/
|
||||
└── logger.js # winston logger instance
|
||||
logs/ # created automatically at runtime (git-ignored)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## setup
|
||||
|
||||
### 1. clone & install dependencies
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yhqe/twitter_v3.git
|
||||
cd twitter_v3
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. configure environment variables
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
open `.env` and fill in:
|
||||
|
||||
| variable | required | description |
|
||||
|-------------|----------|------------------------------------------------------|
|
||||
| `BOT_TOKEN` | yes | your bot token from the discord developer portal |
|
||||
| `PREFIX` | no | command prefix (default: `!`) |
|
||||
| `GUILD_ID` | no | test server id (handy for dev stuff) |
|
||||
|
||||
### 3. enable the message content intent
|
||||
|
||||
go to your application in the [discord developer portal](https://discord.com/developers/applications), open **bot → privileged gateway intents**, and enable **message content intent**. you need this for prefix commands.
|
||||
|
||||
### 4. start the bot
|
||||
|
||||
```bash
|
||||
# production
|
||||
npm start
|
||||
|
||||
# development (auto-restarts on file changes, node.js 18+)
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## adding commands
|
||||
|
||||
create a file in `src/commands/`. the handler will load it automatically on next start.
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
name: 'example', // primary command name (also used as the key)
|
||||
description: 'An example command.',
|
||||
aliases: ['ex'], // optional aliases
|
||||
usage: 'example <text>', // shown in !help example
|
||||
|
||||
async execute(client, message, args) {
|
||||
await message.reply(`You said: ${args.join(' ')}`);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## adding events
|
||||
|
||||
create a file in `src/events/`. set `once: true` if it should fire only once (e.g. `ready`).
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.GuildMemberAdd,
|
||||
|
||||
async execute(client, member) {
|
||||
console.log(`${member.user.tag} joined ${member.guild.name}`);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## logging
|
||||
|
||||
log files are written to the `logs/` directory (git-ignored). the log level defaults to `info` and can be overridden with the `LOG_LEVEL` environment variable (e.g. `LOG_LEVEL=debug npm start`).
|
||||
|
||||
|
||||
# twitter (v3)
|
||||
|
||||
a discord.js v14 moderation (+ more) bot
|
||||
|
||||
---
|
||||
|
||||
## features
|
||||
|
||||
- prefix command system — commands triggered by a configurable prefix (default `!`)
|
||||
- auto-loading — commands and events are loaded automatically from their folders
|
||||
- aliases — each command can define one or more aliases
|
||||
- winston logging — coloured console output + rolling log files (`logs/combined.log`, `logs/error.log`)
|
||||
- global error handling — unhandled rejections and uncaught exceptions are caught and logged
|
||||
|
||||
---
|
||||
|
||||
## project structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── index.js # entry point — creates the client, loads handlers
|
||||
├── commands/
|
||||
│ ├── help.js # !help — lists commands or shows command detail
|
||||
│ └── ping.js # !ping — latency check
|
||||
├── events/
|
||||
│ ├── ready.js # fires once when the bot is online
|
||||
│ └── messageCreate.js # parses every message for prefix commands
|
||||
├── handlers/
|
||||
│ ├── commandHandler.js # reads src/commands/ and registers commands
|
||||
│ └── eventHandler.js # reads src/events/ and registers discord events
|
||||
└── utils/
|
||||
└── logger.js # winston logger instance
|
||||
logs/ # created automatically at runtime (git-ignored)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## setup
|
||||
|
||||
### 1. clone & install dependencies
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yhqe/twitter_v3.git
|
||||
cd twitter_v3
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. configure environment variables
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
open `.env` and fill in:
|
||||
|
||||
| variable | required | description |
|
||||
|-------------|----------|------------------------------------------------------|
|
||||
| `BOT_TOKEN` | yes | your bot token from the discord developer portal |
|
||||
| `PREFIX` | no | command prefix (default: `!`) |
|
||||
| `GUILD_ID` | no | test server id (handy for dev stuff) |
|
||||
|
||||
### 3. enable the message content intent
|
||||
|
||||
go to your application in the [discord developer portal](https://discord.com/developers/applications), open **bot → privileged gateway intents**, and enable **message content intent**. you need this for prefix commands.
|
||||
|
||||
### 4. start the bot
|
||||
|
||||
```bash
|
||||
# production
|
||||
npm start
|
||||
|
||||
# development (auto-restarts on file changes, node.js 18+)
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## adding commands
|
||||
|
||||
create a file in `src/commands/`. the handler will load it automatically on next start.
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
name: 'example', // primary command name (also used as the key)
|
||||
description: 'An example command.',
|
||||
aliases: ['ex'], // optional aliases
|
||||
usage: 'example <text>', // shown in !help example
|
||||
|
||||
async execute(client, message, args) {
|
||||
await message.reply(`You said: ${args.join(' ')}`);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## adding events
|
||||
|
||||
create a file in `src/events/`. set `once: true` if it should fire only once (e.g. `ready`).
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.GuildMemberAdd,
|
||||
|
||||
async execute(client, member) {
|
||||
console.log(`${member.user.tag} joined ${member.guild.name}`);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## logging
|
||||
|
||||
log files are written to the `logs/` directory (git-ignored). the log level defaults to `info` and can be overridden with the `LOG_LEVEL` environment variable (e.g. `LOG_LEVEL=debug npm start`).
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"autoroles": {
|
||||
"1133441692196950101": "1192595134026616894"
|
||||
},
|
||||
"autobans": {
|
||||
"1133441692196950101": "1264634623950848158"
|
||||
},
|
||||
"prefixes": {
|
||||
"1362160460253761536": "*"
|
||||
}
|
||||
}
|
||||
-33156
File diff suppressed because it is too large.
Load diff
Generated
+609
-609
File diff suppressed because it is too large.
Load diff
+139
-139
@@ -1,139 +1,139 @@
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
async function getAvailableCapes(username) {
|
||||
const capes =[];
|
||||
|
||||
const mojangReq = await Promise.allSettled([
|
||||
fetch(`https://api.ashcon.app/mojang/v2/user/${username}`),
|
||||
]);
|
||||
|
||||
if (mojangReq.status === 'fulfilled' && mojangReq.value.ok) {
|
||||
const mojangData = await mojangReq.value.json();
|
||||
if (mojangData.textures && mojangData.textures.cape) {
|
||||
capes.push({
|
||||
source: 'Official (Equipped)',
|
||||
url: mojangData.textures.cape.url
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return capes;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'mc',
|
||||
description: `request different information about a minecraft user`,
|
||||
aliases: ['minecraft'],
|
||||
usage: 'mc <username>',
|
||||
|
||||
async execute(client, message, args) {
|
||||
var embed;
|
||||
|
||||
let sent = await message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<a:loading_icon:1366587619890302998> fetching...',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
|
||||
const username = args[0];
|
||||
if (!username) {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> please provide a valid minecraft username!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return await sent.edit({ content: null, embeds: [embed] });
|
||||
}
|
||||
|
||||
const uuid_res = await fetch(`https://api.mojang.com/users/profiles/minecraft/${username}`);
|
||||
|
||||
const uuid = await uuid_res.json().then(res => res.id).catch(err => {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> cannot fetch uuid!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return null;
|
||||
});
|
||||
|
||||
const res = await fetch(`https://sessionserver.mojang.com/session/minecraft/profile/${uuid}`);
|
||||
const data = await res.json().then(res => res).catch(err => {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> cannot fetch data!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return null;
|
||||
});
|
||||
|
||||
getAvailableCapes(`${data.name}`).then(capes => {
|
||||
logger.debug(`found ${capes.length} cape(s):`);
|
||||
logger.debug(capes);
|
||||
});
|
||||
|
||||
if (!data) return await sent.edit({ content: null, embeds: [embed] });
|
||||
|
||||
const skin = await fetch (`https://minotar.net/helm/${data.name}/420.png`).then(res => res.url).catch(err => null);
|
||||
|
||||
const capes = data.properties[0].value;
|
||||
const decoded_capes = Buffer.from(capes, 'base64').toString('utf-8');
|
||||
const cape_url = JSON.parse(decoded_capes).textures.CAPE ? JSON.parse(decoded_capes).textures.CAPE.url : null;
|
||||
|
||||
logger.debug(`[mc] fetched data for "${username}": ${JSON.stringify(data)}`);
|
||||
logger.debug(`[mc] fetched cape data for "${username}": ${JSON.stringify(cape_url)}`);
|
||||
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `**[${data.name}](https://namemc.com/profile/${data.name})**`,
|
||||
fields: [
|
||||
{ name: 'uuid', value: `${data.id}`, inline: true },
|
||||
],
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
thumbnail: {
|
||||
url: skin,
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
},
|
||||
};
|
||||
|
||||
await sent.edit({ content: null, embeds: [embed] });
|
||||
},
|
||||
};
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
async function getAvailableCapes(username) {
|
||||
const capes =[];
|
||||
|
||||
const mojangReq = await Promise.allSettled([
|
||||
fetch(`https://api.ashcon.app/mojang/v2/user/${username}`),
|
||||
]);
|
||||
|
||||
if (mojangReq.status === 'fulfilled' && mojangReq.value.ok) {
|
||||
const mojangData = await mojangReq.value.json();
|
||||
if (mojangData.textures && mojangData.textures.cape) {
|
||||
capes.push({
|
||||
source: 'Official (Equipped)',
|
||||
url: mojangData.textures.cape.url
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return capes;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'mc',
|
||||
description: `request different information about a minecraft user`,
|
||||
aliases: ['minecraft'],
|
||||
usage: 'mc <username>',
|
||||
|
||||
async execute(client, message, args) {
|
||||
var embed;
|
||||
|
||||
let sent = await message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<a:loading_icon:1366587619890302998> fetching...',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
|
||||
const username = args[0];
|
||||
if (!username) {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> please provide a valid minecraft username!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return await sent.edit({ content: null, embeds: [embed] });
|
||||
}
|
||||
|
||||
const uuid_res = await fetch(`https://api.mojang.com/users/profiles/minecraft/${username}`);
|
||||
|
||||
const uuid = await uuid_res.json().then(res => res.id).catch(err => {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> cannot fetch uuid!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return null;
|
||||
});
|
||||
|
||||
const res = await fetch(`https://sessionserver.mojang.com/session/minecraft/profile/${uuid}`);
|
||||
const data = await res.json().then(res => res).catch(err => {
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
description: '<:no:1481811955592466492> cannot fetch data!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
return null;
|
||||
});
|
||||
|
||||
getAvailableCapes(`${data.name}`).then(capes => {
|
||||
logger.debug(`found ${capes.length} cape(s):`);
|
||||
logger.debug(capes);
|
||||
});
|
||||
|
||||
if (!data) return await sent.edit({ content: null, embeds: [embed] });
|
||||
|
||||
const skin = await fetch (`https://minotar.net/helm/${data.name}/420.png`).then(res => res.url).catch(err => null);
|
||||
|
||||
const capes = data.properties[0].value;
|
||||
const decoded_capes = Buffer.from(capes, 'base64').toString('utf-8');
|
||||
const cape_url = JSON.parse(decoded_capes).textures.CAPE ? JSON.parse(decoded_capes).textures.CAPE.url : null;
|
||||
|
||||
logger.debug(`[mc] fetched data for "${username}": ${JSON.stringify(data)}`);
|
||||
logger.debug(`[mc] fetched cape data for "${username}": ${JSON.stringify(cape_url)}`);
|
||||
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
title: '',
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `**[${data.name}](https://namemc.com/profile/${data.name})**`,
|
||||
fields: [
|
||||
{ name: 'uuid', value: `${data.id}`, inline: true },
|
||||
],
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
thumbnail: {
|
||||
url: skin,
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
},
|
||||
};
|
||||
|
||||
await sent.edit({ content: null, embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -48,7 +48,7 @@ module.exports = {
|
||||
name: 'rbx',
|
||||
description: `various commands related to roblox`,
|
||||
aliases: ['roblox'],
|
||||
usage: `rbx user <username>\n${env.PREFIX}rbx item <item id>\n${env.PREFIX}rbx group <group id>\n${env.PREFIX}rbx game <game id>\n${env.PREFIX}rbx avatar <username>`,
|
||||
usage: 'rbx user <username>\n.rbx item <item id>\n.rbx group <group id>\n.rbx game <game id>',
|
||||
|
||||
async execute(client, message) {
|
||||
var embed = {};
|
||||
@@ -206,48 +206,6 @@ module.exports = {
|
||||
timestamp: new Date(),
|
||||
}
|
||||
}
|
||||
} else if (subcommand === 'avatar') {
|
||||
try {
|
||||
const user = data.data[0];
|
||||
const a_res = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar?userIds=${user.id}&size=720x720&format=Png&isCircular=false`, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
const a_data = await a_res.json();
|
||||
logger.debug(`[rbx] fetched avatar for "${argument}": ${JSON.stringify(a_data)}`);
|
||||
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `**${user.displayName}**'s avatar`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
image: {
|
||||
url: `${a_data.data[0].imageUrl}`
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error(`[rbx] failed to fetch avatar for user id "${argument}":`, err);
|
||||
embed = {
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:no:1481811955592466492> could not fetch avatar data for user "${argument}"!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
};
|
||||
}
|
||||
} else if (subcommand === 'item') {
|
||||
try {
|
||||
const res = await fetch(`https://economy.roproxy.com/v2/assets/${argument}/details`, {
|
||||
|
||||
@@ -4,6 +4,108 @@ const { time } = require("discord.js");
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
name: 'autoban',
|
||||
description: `automatically ban a user if they join the server`,
|
||||
aliases: ['ab', 'banish'],
|
||||
usage: 'ban <user> [reason] [duration]',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('BanMembers')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const user = message.mentions.users.first() || await client.users.fetch(args[0]).catch(() => null);
|
||||
if (!user) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> please specify a user to autoban!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const reason = args.slice(1).join(' ') || 'autobanned (twitter)';
|
||||
|
||||
const dbPath = path.join(__dirname, '..', '..', '..', 'db.json');
|
||||
|
||||
let db = {};
|
||||
if (fs.existsSync(dbPath)) {
|
||||
db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
||||
}
|
||||
|
||||
if (!db.autobans) {
|
||||
db.autobans = {};
|
||||
}
|
||||
|
||||
db.autobans[message.guild.id] = user.id;
|
||||
|
||||
try {
|
||||
await fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:check:1481811973447356496> ${user.username} has been added to the autoban list!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> an error occurred while trying to add the user to the autoban list!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
name: 'autoban',
|
||||
description: `automatically ban a user if they join the server`,
|
||||
|
||||
@@ -5,8 +5,8 @@ const { time } = require("discord.js");
|
||||
module.exports = {
|
||||
name: 'ban',
|
||||
description: `ban a user from the current server`,
|
||||
aliases: ['b'],
|
||||
usage: 'ban <user> [reason]',
|
||||
aliases: ['banish'],
|
||||
usage: 'ban <user> [reason] [duration]',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('BanMembers')) {
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'kick',
|
||||
description: `kick a user from the current server`,
|
||||
aliases: ['kick'],
|
||||
usage: 'kick <user> [reason]',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('KickMembers')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const user = message.mentions.users.first() || client.users.cache.get(args[0]);
|
||||
if (!user) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> please specify a user to ban!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const reason = args.slice(1).join(' ') || 'no reason provided (twitter)';
|
||||
const duration = args[2] ? parseInt(args[2]) : null;
|
||||
|
||||
try {
|
||||
await message.guild.members.kick(user, { reason });
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:check:1481811973447356496> ${user.username} has been kicked!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> an error occurred while trying to ban the user!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
},
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'kick',
|
||||
description: `kick a user from the current server`,
|
||||
aliases: ['kick'],
|
||||
usage: 'kick <user> [reason]',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('KickMembers')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const user = message.mentions.users.first() || client.users.cache.get(args[0]);
|
||||
if (!user) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> please specify a user to ban!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const reason = args.slice(1).join(' ') || 'no reason provided (twitter)';
|
||||
const duration = args[2] ? parseInt(args[2]) : null;
|
||||
|
||||
try {
|
||||
await message.guild.members.kick(user, { reason });
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:check:1481811973447356496> ${user.username} has been kicked!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> an error occurred while trying to ban the user!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,84 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'autorole',
|
||||
description: `assign a role to new members when they join the server`,
|
||||
aliases: ['ar'],
|
||||
usage: 'autorole <role>',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('Administrator')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const role = message.mentions.roles.first() || message.guild.roles.cache.get(args[0]);
|
||||
|
||||
if (!role) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> please provide a valid role!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
// add the role to a json file for the guild and save it to be used when new members join (if the role is already in the database, update it to the new role)
|
||||
const dbPath = path.join(__dirname, '..', '..', '..', 'db.json');
|
||||
|
||||
let db = {};
|
||||
if (fs.existsSync(dbPath)) {
|
||||
db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
||||
}
|
||||
|
||||
if (!db.autoroles) {
|
||||
db.autoroles = {};
|
||||
}
|
||||
|
||||
db.autoroles[message.guild.id] = role.id;
|
||||
|
||||
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
||||
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:check:1481811973447356496> "${role.name}" has been set as the autorole!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'autorole',
|
||||
description: `assign a role to new members when they join the server`,
|
||||
aliases: ['ar'],
|
||||
usage: 'autorole <role>',
|
||||
|
||||
async execute(client, message, args) {
|
||||
if (!message.member.permissions.has('Administrator')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
const role = message.mentions.roles.first() || message.guild.roles.cache.get(args[0]);
|
||||
|
||||
if (!role) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: '<:no:1481811955592466492> please provide a valid role!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
// add the role to a json file for the guild and save it to be used when new members join (if the role is already in the database, update it to the new role)
|
||||
const dbPath = path.join(__dirname, '..', '..', '..', 'db.json');
|
||||
|
||||
let db = {};
|
||||
if (fs.existsSync(dbPath)) {
|
||||
db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
||||
}
|
||||
|
||||
if (!db.autoroles) {
|
||||
db.autoroles = {};
|
||||
}
|
||||
|
||||
db.autoroles[message.guild.id] = role.id;
|
||||
|
||||
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
||||
|
||||
message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:check:1481811973447356496> "${role.name}" has been set as the autorole!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}]
|
||||
});
|
||||
}
|
||||
};
|
||||
+61
-61
@@ -1,61 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'purge',
|
||||
description: `delete a specified number of messages from the current channel`,
|
||||
aliases: ['clear', 'p'],
|
||||
usage: 'purge <number>',
|
||||
|
||||
async execute(client, message) {
|
||||
if (!message.member.permissions.has('ManageMessages')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
const args = message.content.split(' ').slice(1);
|
||||
const amount = parseInt(args[0]) + 1;
|
||||
|
||||
if (isNaN(amount) || amount < 1 || amount > 100) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
description: '<:no:1481811955592466492> please provide a number between 1 and 100!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
const deletedMessages = await message.channel.bulkDelete(amount, true);
|
||||
const reply = await message.channel.send({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:no:1481811955592466492> successfully deleted ${deletedMessages.size - 1} messages!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
setTimeout(() => reply.delete().catch(() => { }), 5000);
|
||||
},
|
||||
};
|
||||
'use strict';
|
||||
|
||||
const { time } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
name: 'purge',
|
||||
description: `delete a specified number of messages from the current channel`,
|
||||
aliases: ['clear', 'p'],
|
||||
usage: 'purge <number>',
|
||||
|
||||
async execute(client, message) {
|
||||
if (!message.member.permissions.has('ManageMessages')) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
description: '<:no:1481811955592466492> you do not have permission to use this command!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
const args = message.content.split(' ').slice(1);
|
||||
const amount = parseInt(args[0]) + 1;
|
||||
|
||||
if (isNaN(amount) || amount < 1 || amount > 100) {
|
||||
return message.reply({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
description: '<:no:1481811955592466492> please provide a number between 1 and 100!',
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
const deletedMessages = await message.channel.bulkDelete(amount, true);
|
||||
const reply = await message.channel.send({
|
||||
content: null, embeds: [{
|
||||
color: 0x8aa2b3,
|
||||
author: {
|
||||
name: message.author.username,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
description: `<:no:1481811955592466492> successfully deleted ${deletedMessages.size - 1} messages!`,
|
||||
footer: {
|
||||
text: `twitter @ ${message.guild.name}`,
|
||||
icon_url: client.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
}],
|
||||
});
|
||||
setTimeout(() => reply.delete().catch(() => { }), 5000);
|
||||
},
|
||||
};
|
||||
@@ -1,53 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Events } = require('discord.js');
|
||||
const guildMemberAddEvent = require('../events/guildMemberAdd');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Loads all event files from src/events and registers them on the client.
|
||||
* Each event file must export: { name, once?, execute }
|
||||
*
|
||||
* @param {import('discord.js').Client} client
|
||||
*/
|
||||
function loadEvents(client) {
|
||||
const eventsPath = path.join(__dirname, '..', 'events');
|
||||
const eventFiles = fs
|
||||
.readdirSync(eventsPath)
|
||||
.filter((file) => file.endsWith('.js'));
|
||||
|
||||
let loaded = 0;
|
||||
|
||||
for (const file of eventFiles) {
|
||||
try {
|
||||
const event = require(path.join(eventsPath, file));
|
||||
|
||||
if (!event.name || typeof event.execute !== 'function') {
|
||||
logger.warn(
|
||||
`[eventhandler] skipping ${file}: missing "name" or "execute"`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.once) {
|
||||
client.once(event.name, (...args) => event.execute(client, ...args));
|
||||
} else {
|
||||
client.on(event.name, (...args) => event.execute(client, ...args));
|
||||
}
|
||||
|
||||
logger.debug(`[eventhandler] registered event: ${event.name}`);
|
||||
loaded++;
|
||||
} catch (err) {
|
||||
logger.error(`[eventhandler] failed to load ${file}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
client.on(Events.GuildMemberAdd, guildMemberAddEvent.bind(null, client));
|
||||
loaded++;
|
||||
|
||||
logger.info(`[eventhandler] ${loaded} event(s) registered`);
|
||||
}
|
||||
|
||||
module.exports = { loadEvents };
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Events } = require('discord.js');
|
||||
const guildMemberAddEvent = require('../events/guildMemberAdd');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Loads all event files from src/events and registers them on the client.
|
||||
* Each event file must export: { name, once?, execute }
|
||||
*
|
||||
* @param {import('discord.js').Client} client
|
||||
*/
|
||||
function loadEvents(client) {
|
||||
const eventsPath = path.join(__dirname, '..', 'events');
|
||||
const eventFiles = fs
|
||||
.readdirSync(eventsPath)
|
||||
.filter((file) => file.endsWith('.js'));
|
||||
|
||||
let loaded = 0;
|
||||
|
||||
for (const file of eventFiles) {
|
||||
try {
|
||||
const event = require(path.join(eventsPath, file));
|
||||
|
||||
if (!event.name || typeof event.execute !== 'function') {
|
||||
logger.warn(
|
||||
`[eventhandler] skipping ${file}: missing "name" or "execute"`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.once) {
|
||||
client.once(event.name, (...args) => event.execute(client, ...args));
|
||||
} else {
|
||||
client.on(event.name, (...args) => event.execute(client, ...args));
|
||||
}
|
||||
|
||||
logger.debug(`[eventhandler] registered event: ${event.name}`);
|
||||
loaded++;
|
||||
} catch (err) {
|
||||
logger.error(`[eventhandler] failed to load ${file}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
client.on(Events.GuildMemberAdd, guildMemberAddEvent.bind(null, client));
|
||||
loaded++;
|
||||
|
||||
logger.info(`[eventhandler] ${loaded} event(s) registered`);
|
||||
}
|
||||
|
||||
module.exports = { loadEvents };
|
||||
+50
-50
@@ -1,50 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const { Client, Collection, GatewayIntentBits } = require('discord.js');
|
||||
const logger = require('./utils/logger');
|
||||
const { loadCommands } = require('./handlers/commandHandler');
|
||||
const { loadEvents } = require('./handlers/eventHandler');
|
||||
|
||||
// Validate required environment variables
|
||||
if (!process.env.BOT_TOKEN) {
|
||||
logger.error(
|
||||
'BOT_TOKEN is not set. Copy .env.example to .env and fill in your token.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Create the Discord client with the intents needed for prefix commands
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent, // Required to read message content for prefix commands
|
||||
GatewayIntentBits.DirectMessages,
|
||||
],
|
||||
});
|
||||
|
||||
// Attach a Collection to store commands
|
||||
client.commands = new Collection();
|
||||
|
||||
// Load commands and events
|
||||
loadCommands(client);
|
||||
loadEvents(client);
|
||||
|
||||
// Global error handling
|
||||
process.on('unhandledRejection', (err) => {
|
||||
logger.error('[process] unhandled rejection:', err);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
logger.error('[process] uncaught exception:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Connect to Discord
|
||||
client.login(process.env.BOT_TOKEN).catch((err) => {
|
||||
logger.error('[login] failed to log in:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
'use strict';
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const { Client, Collection, GatewayIntentBits } = require('discord.js');
|
||||
const logger = require('./utils/logger');
|
||||
const { loadCommands } = require('./handlers/commandHandler');
|
||||
const { loadEvents } = require('./handlers/eventHandler');
|
||||
|
||||
// Validate required environment variables
|
||||
if (!process.env.BOT_TOKEN) {
|
||||
logger.error(
|
||||
'BOT_TOKEN is not set. Copy .env.example to .env and fill in your token.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Create the Discord client with the intents needed for prefix commands
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent, // Required to read message content for prefix commands
|
||||
GatewayIntentBits.DirectMessages,
|
||||
],
|
||||
});
|
||||
|
||||
// Attach a Collection to store commands
|
||||
client.commands = new Collection();
|
||||
|
||||
// Load commands and events
|
||||
loadCommands(client);
|
||||
loadEvents(client);
|
||||
|
||||
// Global error handling
|
||||
process.on('unhandledRejection', (err) => {
|
||||
logger.error('[process] unhandled rejection:', err);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
logger.error('[process] uncaught exception:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Connect to Discord
|
||||
client.login(process.env.BOT_TOKEN).catch((err) => {
|
||||
logger.error('[login] failed to log in:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
+74
-74
@@ -1,74 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
const { createLogger, format, transports } = require('winston');
|
||||
const { consoleFormat } = require("winston-console-format");
|
||||
const { printf } = format;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const logs_dir = path.join(__dirname, '..', '..', 'logs');
|
||||
if (!fs.existsSync(logs_dir)) {
|
||||
fs.mkdirSync(logs_dir, { recursive: true });
|
||||
}
|
||||
|
||||
const logger = createLogger({
|
||||
level: "silly",
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.ms(),
|
||||
format.errors({ stack: true }),
|
||||
format.splat(),
|
||||
format.json({ space: 2 })
|
||||
),
|
||||
transports: [
|
||||
new transports.Console({
|
||||
format: format.combine(
|
||||
format.colorize({ all: true }),
|
||||
format.padLevels(),
|
||||
consoleFormat({
|
||||
showMeta: true,
|
||||
metaStrip: ["timestamp", "service"],
|
||||
inspectOptions: {
|
||||
depth: Infinity,
|
||||
colors: true,
|
||||
maxArrayLength: Infinity,
|
||||
breakLength: 120,
|
||||
compact: Infinity,
|
||||
},
|
||||
})
|
||||
),
|
||||
}),
|
||||
|
||||
new transports.File({
|
||||
filename: path.join(logs_dir, 'combined.log'),
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.ms(),
|
||||
format.errors({ stack: true }),
|
||||
format.splat(),
|
||||
format.json({ space: 2 })
|
||||
),
|
||||
}),
|
||||
|
||||
new transports.File({
|
||||
filename: path.join(logs_dir, 'error.log'),
|
||||
level: 'error',
|
||||
format: format.combine(
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
||||
format.errors({ stack: true }),
|
||||
printf(({ level, message, timestamp, stack, ...meta }) => {
|
||||
let log = `${timestamp} [${level.toUpperCase()}]: ${message}`;
|
||||
if (stack) {
|
||||
log += `\ntrace: ${stack}`;
|
||||
}
|
||||
if (Object.keys(meta).length > 0) {
|
||||
log += `\nmetadata: ${JSON.stringify(meta, null, 2)}`;
|
||||
}
|
||||
return log;
|
||||
})
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
'use strict';
|
||||
|
||||
const { createLogger, format, transports } = require('winston');
|
||||
const { consoleFormat } = require("winston-console-format");
|
||||
const { printf } = format;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const logs_dir = path.join(__dirname, '..', '..', 'logs');
|
||||
if (!fs.existsSync(logs_dir)) {
|
||||
fs.mkdirSync(logs_dir, { recursive: true });
|
||||
}
|
||||
|
||||
const logger = createLogger({
|
||||
level: "silly",
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.ms(),
|
||||
format.errors({ stack: true }),
|
||||
format.splat(),
|
||||
format.json({ space: 2 })
|
||||
),
|
||||
transports: [
|
||||
new transports.Console({
|
||||
format: format.combine(
|
||||
format.colorize({ all: true }),
|
||||
format.padLevels(),
|
||||
consoleFormat({
|
||||
showMeta: true,
|
||||
metaStrip: ["timestamp", "service"],
|
||||
inspectOptions: {
|
||||
depth: Infinity,
|
||||
colors: true,
|
||||
maxArrayLength: Infinity,
|
||||
breakLength: 120,
|
||||
compact: Infinity,
|
||||
},
|
||||
})
|
||||
),
|
||||
}),
|
||||
|
||||
new transports.File({
|
||||
filename: path.join(logs_dir, 'combined.log'),
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.ms(),
|
||||
format.errors({ stack: true }),
|
||||
format.splat(),
|
||||
format.json({ space: 2 })
|
||||
),
|
||||
}),
|
||||
|
||||
new transports.File({
|
||||
filename: path.join(logs_dir, 'error.log'),
|
||||
level: 'error',
|
||||
format: format.combine(
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
||||
format.errors({ stack: true }),
|
||||
printf(({ level, message, timestamp, stack, ...meta }) => {
|
||||
let log = `${timestamp} [${level.toUpperCase()}]: ${message}`;
|
||||
if (stack) {
|
||||
log += `\ntrace: ${stack}`;
|
||||
}
|
||||
if (Object.keys(meta).length > 0) {
|
||||
log += `\nmetadata: ${JSON.stringify(meta, null, 2)}`;
|
||||
}
|
||||
return log;
|
||||
})
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
Reference in new issue
Block a user