mirror of
https://github.com/veganhacktivists/arabot.git
synced 2025-05-18 13:44:13 +02:00
Compare commits
9 Commits
63c3b14b1c
...
32776a2311
Author | SHA1 | Date | |
---|---|---|---|
![]() |
32776a2311 | ||
![]() |
d72b66f988 | ||
![]() |
e03bd6e85e | ||
![]() |
a400cf9507 | ||
![]() |
2fbb6c9265 | ||
![]() |
fc8c12b346 | ||
![]() |
9ebf8a6938 | ||
![]() |
bc7f2ffcfd | ||
![]() |
86f391e131 |
@ -3,15 +3,18 @@ DISCORD_TOKEN= # Bot token from: https://discord.com/developers/
|
||||
|
||||
# Configuration
|
||||
DEFAULT_PREFIX= # Prefix used to run commands in Discord
|
||||
DEVELOPMENT= # (true/false) Enables developer mode
|
||||
DEVELOPMENT= # (true/false) Enables developer mode
|
||||
|
||||
# Docker
|
||||
POSTGRES_USER=USERNAME
|
||||
POSTGRES_PASSWORD=PASSWORD
|
||||
POSTGRES_DB=DB
|
||||
|
||||
# Redis
|
||||
REDIS_URL= # URL to redis database (if running everything within docker compose, use "redis")
|
||||
# Redis (if running everything within docker compose, use "redis" for the host and leave the rest empty)
|
||||
REDIS_HOST= # URL to redis database
|
||||
REDIS_USER= # redis database user
|
||||
REDIS_PASSWORD= # redis database password
|
||||
REDIS_PORT= # redis database port
|
||||
|
||||
# Database URL (designed for Postgres, but designed on Prisma)
|
||||
DATABASE_URL= # "postgresql://USERNAME:PASSWORD@postgres:5432/DB?schema=ara&sslmode=prefer"
|
||||
|
5
nixpacks.toml
Normal file
5
nixpacks.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[phases.build]
|
||||
cmds = ["pnpm prisma generate", "..."]
|
||||
|
||||
[start]
|
||||
cmd = 'pnpm run start:migrate'
|
28
package.json
28
package.json
@ -33,31 +33,31 @@
|
||||
"node": ">=20",
|
||||
"pnpm": ">=9"
|
||||
},
|
||||
"packageManager": "pnpm@9.6.0",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.18.0",
|
||||
"@sapphire/discord.js-utilities": "^7.3.0",
|
||||
"@sapphire/framework": "^5.2.1",
|
||||
"@prisma/client": "^5.22.0",
|
||||
"@sapphire/discord.js-utilities": "^7.3.2",
|
||||
"@sapphire/framework": "^5.3.2",
|
||||
"@sapphire/plugin-logger": "^4.0.2",
|
||||
"@sapphire/plugin-scheduled-tasks": "^10.0.1",
|
||||
"@sapphire/plugin-scheduled-tasks": "^10.0.2",
|
||||
"@sapphire/plugin-subcommands": "^6.0.3",
|
||||
"@sapphire/stopwatch": "^1.5.2",
|
||||
"@sapphire/time-utilities": "^1.7.12",
|
||||
"@sapphire/stopwatch": "^1.5.4",
|
||||
"@sapphire/time-utilities": "^1.7.14",
|
||||
"@sapphire/ts-config": "^5.0.1",
|
||||
"@sapphire/utilities": "^3.17.0",
|
||||
"bullmq": "^5.12.10",
|
||||
"discord.js": "^14.15.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"@sapphire/utilities": "^3.18.1",
|
||||
"bullmq": "^5.34.10",
|
||||
"discord.js": "^14.17.3",
|
||||
"ioredis": "^5.4.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "~5.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.16.1",
|
||||
"@types/node": "^20.17.13",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"prettier": "3.2.4",
|
||||
"prisma": "^5.18.0"
|
||||
}
|
||||
"prisma": "^5.22.0"
|
||||
},
|
||||
"packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
|
||||
}
|
||||
|
618
pnpm-lock.yaml
generated
618
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||
import type { Message } from 'discord.js';
|
||||
import { Message, MessageFlagsBitField } from 'discord.js';
|
||||
import { ChannelType, TextChannel } from 'discord.js';
|
||||
|
||||
export class AnonymousCommand extends Command {
|
||||
@ -67,8 +67,8 @@ export class AnonymousCommand extends Command {
|
||||
if (guild === null) {
|
||||
await interaction.reply({
|
||||
content: 'Error fetching guild!',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -77,8 +77,17 @@ export class AnonymousCommand extends Command {
|
||||
if (interaction.channel === null) {
|
||||
await interaction.reply({
|
||||
content: 'Error getting the channel!',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interaction.channel.isSendable()) {
|
||||
await interaction.reply({
|
||||
content: `I do not have sufficient permissions to send a message in ${interaction.channel}!`,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -86,8 +95,8 @@ export class AnonymousCommand extends Command {
|
||||
await interaction.channel.send(message);
|
||||
await interaction.reply({
|
||||
content: 'Sent the message',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -95,8 +104,8 @@ export class AnonymousCommand extends Command {
|
||||
if (channel.type !== ChannelType.GuildText) {
|
||||
await interaction.reply({
|
||||
content: 'Could not send, unsupported text channel!',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -105,8 +114,8 @@ export class AnonymousCommand extends Command {
|
||||
|
||||
await interaction.reply({
|
||||
content: 'Sent the message',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -121,7 +130,7 @@ export class AnonymousCommand extends Command {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel.isTextBased()) {
|
||||
if (channel.isSendable()) {
|
||||
await channel.send(text);
|
||||
} else {
|
||||
await message.react('❌');
|
||||
|
@ -30,9 +30,9 @@ import {
|
||||
TextChannel,
|
||||
GuildMember,
|
||||
Snowflake,
|
||||
MessageFlagsBitField,
|
||||
} from 'discord.js';
|
||||
import type { Message } from 'discord.js';
|
||||
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
||||
import {
|
||||
addSusNoteDB,
|
||||
findNotes,
|
||||
@ -407,16 +407,21 @@ export class SusCommand extends Subcommand {
|
||||
const message = await interaction.reply({
|
||||
embeds: [noteEmbed],
|
||||
components: [buttons],
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
|
||||
// Checks if the message is not an APIMessage
|
||||
if (!isMessageInstance(message)) {
|
||||
if (message.resource === null) {
|
||||
await interaction.editReply('Failed to retrieve the message :(');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isSendable()) {
|
||||
await interaction.editReply('Cannot send messages in this channel!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen for the button presses
|
||||
const collector = channel.createMessageComponentCollector({
|
||||
max: 1, // Maximum of 1 button press
|
||||
@ -519,8 +524,8 @@ export class SusCommand extends Subcommand {
|
||||
if (guild === null || channel === null) {
|
||||
await interaction.reply({
|
||||
content: 'Error fetching guild or channel!',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -531,8 +536,8 @@ export class SusCommand extends Subcommand {
|
||||
if (member === undefined) {
|
||||
await interaction.reply({
|
||||
content: 'Error fetching user!',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -545,8 +550,8 @@ export class SusCommand extends Subcommand {
|
||||
if (notes.length === 0) {
|
||||
await interaction.reply({
|
||||
content: `${user} had no notes!`,
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -596,16 +601,21 @@ export class SusCommand extends Subcommand {
|
||||
const message = await interaction.reply({
|
||||
embeds: [noteEmbed],
|
||||
components: [buttons],
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
|
||||
// Checks if the message is not an APIMessage
|
||||
if (!isMessageInstance(message)) {
|
||||
if (message.resource === null) {
|
||||
await interaction.editReply('Failed to retrieve the message :(');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isSendable()) {
|
||||
await interaction.editReply('Cannot send messages in this channel!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen for the button presses
|
||||
const collector = channel.createMessageComponentCollector({
|
||||
max: 1, // Maximum of 1 button press
|
||||
|
@ -17,9 +17,8 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
||||
import { Command } from '@sapphire/framework';
|
||||
import type { Message } from 'discord.js';
|
||||
import { Message, MessageFlagsBitField } from 'discord.js';
|
||||
|
||||
export class PingCommand extends Command {
|
||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||
@ -41,12 +40,13 @@ export class PingCommand extends Command {
|
||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||
const msg = await interaction.reply({
|
||||
content: 'Ping?',
|
||||
ephemeral: true,
|
||||
fetchReply: true,
|
||||
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||
withResponse: true,
|
||||
});
|
||||
|
||||
if (isMessageInstance(msg)) {
|
||||
const diff = msg.createdTimestamp - interaction.createdTimestamp;
|
||||
if (msg.resource !== null && msg.resource.message !== null) {
|
||||
const diff =
|
||||
msg.resource.message.createdTimestamp - interaction.createdTimestamp;
|
||||
const ping = Math.round(this.container.client.ws.ping);
|
||||
return interaction.editReply(
|
||||
`Pong 🏓! (Round trip took: ${diff}ms. Heartbeat: ${ping}ms.)`,
|
||||
@ -57,6 +57,11 @@ export class PingCommand extends Command {
|
||||
}
|
||||
|
||||
public async messageRun(message: Message) {
|
||||
if (!message.channel.isSendable()) {
|
||||
// TODO manage logging/errors properly
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = await message.channel.send('Ping?');
|
||||
|
||||
const diff = msg.createdTimestamp - message.createdTimestamp;
|
||||
|
16
src/index.ts
16
src/index.ts
@ -27,6 +27,10 @@ import '@sapphire/plugin-logger/register';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Redis } from 'ioredis';
|
||||
|
||||
const REDIS_PORT = process.env.REDIS_PORT
|
||||
? parseInt(process.env.REDIS_PORT)
|
||||
: undefined;
|
||||
|
||||
// Setting up the Sapphire client
|
||||
const client = new SapphireClient({
|
||||
defaultPrefix: process.env.DEFAULT_PREFIX,
|
||||
@ -49,7 +53,10 @@ const client = new SapphireClient({
|
||||
tasks: {
|
||||
bull: {
|
||||
connection: {
|
||||
host: process.env.REDIS_URL,
|
||||
host: process.env.REDIS_HOST,
|
||||
username: process.env.REDIS_USER,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
port: REDIS_PORT,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -62,9 +69,12 @@ const main = async () => {
|
||||
client.logger.info('Logging in');
|
||||
|
||||
// Create databases
|
||||
container.database = await new PrismaClient();
|
||||
container.database = new PrismaClient();
|
||||
container.redis = new Redis({
|
||||
host: process.env.REDIS_URL,
|
||||
host: process.env.REDIS_HOST,
|
||||
username: process.env.REDIS_USER,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
port: REDIS_PORT,
|
||||
db: 1,
|
||||
});
|
||||
|
||||
|
@ -49,6 +49,11 @@ export class XpListener extends Listener {
|
||||
// If no counts exist on the database, then create the first count from the bot
|
||||
if (lastCount === null) {
|
||||
if (this.container.client.id === null) {
|
||||
if (!message.channel.isSendable()) {
|
||||
// TODO manage logging/errors properly
|
||||
return;
|
||||
}
|
||||
|
||||
message.channel.send(
|
||||
'An unexpected error occurred trying to set up the counting channel, please contact a developer!',
|
||||
);
|
||||
@ -63,6 +68,11 @@ export class XpListener extends Listener {
|
||||
|
||||
lastCount = await getLastCount();
|
||||
if (lastCount === null) {
|
||||
if (!message.channel.isSendable()) {
|
||||
// TODO manage logging/errors properly
|
||||
return;
|
||||
}
|
||||
|
||||
message.channel.send(
|
||||
'An unexpected error occurred, please contact a developer!',
|
||||
);
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
import { Listener } from '@sapphire/framework';
|
||||
import type { Client } from 'discord.js';
|
||||
import IDs from '#utils/ids';
|
||||
|
||||
export class ReadyListener extends Listener {
|
||||
public constructor(
|
||||
@ -35,8 +36,24 @@ export class ReadyListener extends Listener {
|
||||
});
|
||||
}
|
||||
|
||||
public run(client: Client) {
|
||||
public async run(client: Client) {
|
||||
const { username, id } = client.user!;
|
||||
this.container.logger.info(`Successfully logged in as ${username} (${id})`);
|
||||
|
||||
const botLogChannel = await client.channels.fetch(IDs.channels.logs.bot);
|
||||
|
||||
if (botLogChannel === null) {
|
||||
this.container.logger.error(
|
||||
'ReadyListener: Could not find the channel for bot logs.',
|
||||
);
|
||||
return;
|
||||
} else if (!botLogChannel.isSendable()) {
|
||||
this.container.logger.info(
|
||||
'ReadyListener: No permission to send in bots logs channel.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
botLogChannel.send('The bot has started up!');
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,11 @@ export class Suggestions extends Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mailbox.isSendable()) {
|
||||
// TODO manage logging/errors properly
|
||||
return;
|
||||
}
|
||||
|
||||
const sent = await mailbox.send({
|
||||
embeds: [suggestion],
|
||||
content: message.author.toString(),
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
const devIDs = {
|
||||
guild: '999431674972618792',
|
||||
roles: {
|
||||
trusted: '999431675081666599',
|
||||
booster: '',
|
||||
@ -126,6 +127,7 @@ const devIDs = {
|
||||
},
|
||||
logs: {
|
||||
restricted: '999431681217937513',
|
||||
bot: '999431681217937516',
|
||||
economy: '999431681599623198',
|
||||
sus: '999431681599623199',
|
||||
},
|
||||
|
@ -20,17 +20,18 @@
|
||||
import devIDs from '#utils/devIDs';
|
||||
|
||||
let IDs = {
|
||||
guild: '730907954345279591',
|
||||
roles: {
|
||||
trusted: '731563158011117590',
|
||||
trusted: '1329089675977035879',
|
||||
booster: '731213264540795012',
|
||||
nonvegan: {
|
||||
nonvegan: '774763753308815400',
|
||||
vegCurious: '832656046572961803',
|
||||
nonvegan: '1329093962153332848',
|
||||
vegCurious: '1329107984227369020',
|
||||
convinced: '797132019166871612',
|
||||
},
|
||||
vegan: {
|
||||
vegan: '788114978020392982',
|
||||
activist: '730915638746546257',
|
||||
activist: '1329112833115295815',
|
||||
nvAccess: '1076857105648209971',
|
||||
plus: '798682625619132428',
|
||||
araVegan: '995394977658044506',
|
||||
@ -75,7 +76,7 @@ let IDs = {
|
||||
stageHost: '854893757593419786',
|
||||
patron: '765370219207852055',
|
||||
patreon: '993848684640997406',
|
||||
verifyBlock: '1032765019269640203',
|
||||
verifyBlock: '1329107805130461247',
|
||||
bookClub: '955516408249352212',
|
||||
debateHost: '935508325615931443',
|
||||
gameNightHost: '952779915701415966',
|
||||
@ -128,6 +129,7 @@ let IDs = {
|
||||
},
|
||||
logs: {
|
||||
restricted: '920993034462715925',
|
||||
bot: '872126272015314966',
|
||||
economy: '932050015034159174',
|
||||
sus: '872884989950324826',
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user