Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,339 changes: 4,339 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "servant",
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"description": "",
"description": "Bot de moderação simples no Discord",
"main": "index.js",
"scripts": {
"lint": "eslint \"src/**/*.ts\"",
Expand All @@ -18,8 +18,8 @@
"watch:compile": "tsc --watch",
"watch:start": "node --watch dist/"
},
"keywords": [],
"author": "",
"keywords": ["bot", "Moderation", "Discord"],
"author": "Alfheim Bot",
"license": "Apache-2.0",
"packageManager": "pnpm@10.14.0",
"devDependencies": {
Expand Down
20 changes: 20 additions & 0 deletions src/commands/Administrator/shutdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Comando temporário de finalização do cliente
*/
import { Discord, Slash } from "discordx";
import { Category } from "@discordx/utilities";
import { CommandInteraction } from "discord.js";

@Discord()
@Category("Administrator")
export default class ShutdownCommand {
@Slash({
name: "shutdown",
description: "Desligar bot pelo cliente {owner}",
})
async shutdown(interaction: CommandInteraction): Promise<void> {
if (interaction.member?.user.id != process.env.OWNER_ID) return;
await interaction.reply({ content: `Encerrando o bot.` });
await interaction.client.destroy().catch((err) => console.log(err));
}
}
17 changes: 17 additions & 0 deletions src/commands/Experimental/ping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Discord, Slash } from "discordx";
import { Category } from "@discordx/utilities";
import { CommandInteraction } from "discord.js";

@Discord()
@Category("Experimental")
export default class PingCommand {
@Slash({
name: "ping",
description: "Apenas um teste rápido da interação",
})
async ping(interaction: CommandInteraction): Promise<void> {
await interaction.reply({
content: `Pong!`,
});
}
}
2 changes: 1 addition & 1 deletion src/commands/Experimental/test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Discord, Guild, Slash } from "discordx";
import { Discord, Slash } from "discordx";
import { Category } from "@discordx/utilities";
import { CommandInteraction } from "discord.js";

Expand Down
33 changes: 33 additions & 0 deletions src/commands/Information/avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Discord, Slash, SlashOption } from "discordx";
import { Category } from "@discordx/utilities";
import {
ApplicationCommandOptionType,
CommandInteraction,
GuildMember,
} from "discord.js";

@Discord()
@Category("Information")
export default class AvatarCommand {
@Slash({
name: "avatar",
description: "Exibir o avatar de alguém",
})
async avatar(
@SlashOption({
name: "member",
description: "alvo para ver o avatar",
required: false,
type: ApplicationCommandOptionType.Mentionable,
})
member: GuildMember | null,
interaction: CommandInteraction,
): Promise<void> {
if (!member)
interaction.reply(`Seu avatar\n${interaction.user.avatarURL()}`);
else
interaction.reply(
`Avatar de ${member}\n${member.user.avatarURL()}`,
);
}
}
80 changes: 80 additions & 0 deletions src/commands/Moderation/banir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Discord, Slash, SlashOption } from "discordx";
import { Category } from "@discordx/utilities";
import {
CommandInteraction,
PermissionFlagsBits,
GuildMember,
ApplicationCommandOptionType,
} from "discord.js";

@Discord()
@Category("Moderation")
export default class BanCommand {
@Slash({
name: "banir",
description: "banir um membro do servidor.",
})
async ban(
@SlashOption({
name: "member",
description: "membro para banir",
required: true,
type: ApplicationCommandOptionType.Mentionable,
})
member: GuildMember | null,
@SlashOption({
name: "reason",
description: "motivo para o banimento",
required: false,
type: ApplicationCommandOptionType.String,
})
reason: string,
interaction: CommandInteraction,
) {
if (!interaction.guild) return;
const author = await interaction.guild?.members.fetch(
interaction.user.id,
);

if (!member || member.id == interaction.user.id)
return interaction.reply(
`O membro mencionado não pode ser banido.`,
);

const memberRole = member?.roles.highest.position;
if (!memberRole)
return interaction.reply(
`Não foi possível capturar a posição do cargo.`,
);
if (!author?.permissions.has(PermissionFlagsBits.BanMembers))
return interaction.reply(
`Você não tem permissão para banir um membro.`,
);
else if (
!interaction.guild?.members.me?.permissions.has(
PermissionFlagsBits.BanMembers,
)
)
return interaction.reply(
`Eu não tenho permissão de banir um membro.`,
);
else if (interaction.guild.ownerId == member.id)
return interaction.reply(`Você não pode banir o dono do servidor.`);
else if (
interaction.guild?.members.me?.roles.highest.position <= memberRole
)
return interaction.reply(`Não posso banir alvos acima de mim.`);
else if (
author?.roles.highest.position <= member.roles.highest.position
)
return interaction.reply(`Não posso banir seu superior.`);

member
.ban({ reason: `${author.user.username} | ${reason}` })
.catch((err) => {
console.log(err);
return interaction.reply(`Não foi possível banir ${member}`);
});
await interaction.reply(`Banido ${member} por *${reason}*.`);
}
}
80 changes: 80 additions & 0 deletions src/commands/Moderation/expulsar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Discord, Slash, SlashOption } from "discordx";
import { Category } from "@discordx/utilities";
import {
CommandInteraction,
PermissionFlagsBits,
GuildMember,
ApplicationCommandOptionType,
} from "discord.js";

@Discord()
@Category("Moderation")
export default class KickCommand {
@Slash({
name: "expulsar",
description: "expulsar um membro do servidor.",
})
async kick(
@SlashOption({
name: "member",
description: "membro para expulsar",
required: true,
type: ApplicationCommandOptionType.Mentionable,
})
member: GuildMember | null,
@SlashOption({
name: "reason",
description: "motivo para a expulsão",
required: false,
type: ApplicationCommandOptionType.String,
})
reason: string,
interaction: CommandInteraction,
) {
if (!interaction.guild) return;
const author = await interaction.guild?.members.fetch(
interaction.user.id,
);

if (!member || member.id == interaction.user.id)
return interaction.reply(
`O membro mencionado não pode ser expulso.`,
);

const memberRole = member?.roles.highest.position;
if (!memberRole)
return interaction.reply(
`Não foi possível capturar a posição do cargo.`,
);
if (!author?.permissions.has(PermissionFlagsBits.KickMembers))
return interaction.reply(
`Você não tem permissão para expulsar um membro.`,
);
else if (
!interaction.guild?.members.me?.permissions.has(
PermissionFlagsBits.KickMembers,
)
)
return interaction.reply(
`Eu não tenho permissão de expulsar um membro.`,
);
else if (interaction.guild.ownerId == member.id)
return interaction.reply(
`Você não pode expulsar o dono do servidor.`,
);
else if (
interaction.guild?.members.me?.roles.highest.position <= memberRole
)
return interaction.reply(`Não posso expulsar alvos acima de mim.`);
else if (
author?.roles.highest.position <= member.roles.highest.position
)
return interaction.reply(`Não posso expulsar seu superior.`);

member.kick(`${author.user.username} | ${reason}`).catch((err) => {
console.log(err);
return interaction.reply(`Não foi possível expulsar ${member}`);
});
await interaction.reply(`Expulso ${member} por *${reason}*.`);
}
}
5 changes: 3 additions & 2 deletions src/events/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Client, Discord, Once, type ArgsOf } from "discordx";

@Discord()
export default class ReadyEvent {
@Once({ event: "ready" })
@Once({ event: "clientReady" })
async onReady(_: ArgsOf<"ready">, client: Client) {
await client.clearApplicationCommands();
await client.initApplicationCommands();
console.log(`${client.user?.tag} is ready!`);
console.log(`${client.user?.tag} online!`);
}
}
15 changes: 11 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Servant, um bom simples de moderação
* Nosso foco é entregar recursos e comandos personalizados, e isso reflete ao nosso código.
*
* Nosso servidor: /qTgfcN6ct7
* Por: Alfheim Team
*/
import "reflect-metadata";
import "dotenv/config";
import { container } from "tsyringe";
Expand All @@ -8,8 +15,8 @@ import { drizzle } from "drizzle-orm/node-postgres";

DIService.engine = tsyringeDependencyRegistryEngine.setInjector(container);

const DISCORD_GUILD_ID = process.env.DISCORD_GUILD_ID;
const DISCORD_TOKEN = process.env.DISCORD_TOKEN || "";
const GUILD_ID = process.env.GUILD_ID;
const BOT_TOKEN = process.env.BOT_TOKEN || "";
const DATABASE_URL = process.env.DATABASE_URL || "";

const db = drizzle(DATABASE_URL);
Expand All @@ -22,12 +29,12 @@ const client = new Client({
GatewayIntentBits.MessageContent,
],
silent: false,
botGuilds: DISCORD_GUILD_ID ? [DISCORD_GUILD_ID] : [],
botGuilds: GUILD_ID ? [GUILD_ID] : [],
});

async function main() {
await importx(`${dirname(import.meta.url)}/{events,commands}/**/*.{ts,js}`);
await client.login(DISCORD_TOKEN);
await client.login(BOT_TOKEN);
}

main();