diff --git a/src/bot/commands/slashCommands/arrest.ts b/src/bot/commands/slashCommands/arrest.ts index eb1ae79..3cf4d0c 100644 --- a/src/bot/commands/slashCommands/arrest.ts +++ b/src/bot/commands/slashCommands/arrest.ts @@ -1,7 +1,7 @@ import { GuildMember, SlashCommandBuilder } from 'discord.js'; import { SlashCommand } from '@marquinhos/types'; -import ArrestedModel from '@schemas/arrested'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; export const arrest: SlashCommand = { command: new SlashCommandBuilder() @@ -15,31 +15,53 @@ export const arrest: SlashCommand = { ), execute: async (interaction) => { const arrested = interaction.options.get('preso')?.member as GuildMember; - if (arrested.user.id === process.env.BOT_ID) { - arrestMember(interaction.member as GuildMember); - interaction.reply({ content: 'Tu realmente tentou essa?' }); + if (arrested.user.id === interaction.client.user?.id) { + await arrestMember(interaction.member as GuildMember); + await interaction.reply({ content: 'Tu realmente tentou essa?' }); return; } if (arrested.user.bot) { - interaction.reply({ content: 'Não pode prender meus irmãos bots.' }); + await interaction.reply({ + content: 'Não pode prender meus irmãos bots.', + }); return; } - arrestMember(arrested); + const arrestResult = await arrestMember(arrested); + if (!arrestResult) { + await interaction.reply({ content: `${arrested} já está preso!` }); + return; + } interaction.reply({ content: `${arrested} você está PRESO!` }); }, cooldown: 10, }; -function arrestMember(member: GuildMember) { +async function arrestMember(member: GuildMember) { const memberChannelId = member.voice.channelId; - const newArrested = new ArrestedModel({ - id: member.id, - user: member.user.username, - }); - newArrested.save(); if (memberChannelId && memberChannelId != member.guild.afkChannelId) { member.voice.setChannel(member.guild.afkChannelId); } + + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, + }); + + if (!guildUser) { + const newGuildUser = new GuildUserModel({ + guildId: member.guild.id, + userId: member.id, + arrested: true, + }); + return await newGuildUser.save(); + } + + if (guildUser.arrested) { + return null; + } + + guildUser.arrested = true; + return await guildUser.save(); } diff --git a/src/bot/commands/slashCommands/arrested.ts b/src/bot/commands/slashCommands/arrested.ts index 51b921e..0385a8e 100644 --- a/src/bot/commands/slashCommands/arrested.ts +++ b/src/bot/commands/slashCommands/arrested.ts @@ -1,30 +1,37 @@ -import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; +import { EmbedBuilder, GuildMember, SlashCommandBuilder } from 'discord.js'; import { SlashCommand } from '@marquinhos/types'; -import ArrestedModel from '@schemas/arrested'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; export const arrested: SlashCommand = { command: new SlashCommandBuilder() .setName('encarcerados') .setDescription('Te dou uma lista de quem tá preso'), execute: async (interaction) => { - const arrested = await (await getArrested()).toArray(); - interaction.reply({ - embeds: [ - new EmbedBuilder() - .setDescription( - arrested - .map((a) => a.user) - .toString() - .replace(',', '\n') - ) - .setTitle('Segue lista dos criminosos:'), - ], + const arrested = await getArrested( + (interaction.member as GuildMember).guild.id + ); + const listOfArresteds: string = arrested + .map((prisioner) => `:locked: <@${prisioner.userId}>`) + .join('\n'); + + const arrestedsEmbed = interaction.client + .baseEmbed() + .setTitle(':rotating_light: Lista dos criminosos :rotating_light:') + .setTimestamp(); + + if (listOfArresteds) { + arrestedsEmbed.setDescription(`${listOfArresteds}`); + } else { + arrestedsEmbed.setDescription('**Ninguém está preso!**'); + } + return await interaction.reply({ + embeds: [arrestedsEmbed], }); }, cooldown: 10, }; -async function getArrested() { - return ArrestedModel.collection.find(); +async function getArrested(guildId: string) { + return await GuildUserModel.find({ guildId, arrested: true }); } diff --git a/src/bot/commands/slashCommands/release.ts b/src/bot/commands/slashCommands/release.ts index c9182ac..de5fbf7 100644 --- a/src/bot/commands/slashCommands/release.ts +++ b/src/bot/commands/slashCommands/release.ts @@ -1,7 +1,7 @@ import { GuildMember, SlashCommandBuilder } from 'discord.js'; import { SlashCommand } from '@marquinhos/types'; -import ArrestedModel from '@schemas/arrested'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; export const release: SlashCommand = { command: new SlashCommandBuilder() @@ -14,10 +14,8 @@ export const release: SlashCommand = { .setRequired(true) ), execute: async (interaction) => { - // Gets the member chosen to arrest const arrested = interaction.options.get('preso')?.member as GuildMember; - // User cannot releat themselves if (interaction.member === arrested) { interaction.reply({ content: `Cara, e desde quando os presos têm a chave da cela?`, @@ -25,25 +23,41 @@ export const release: SlashCommand = { return; } - // Search for the member in the BD - const result = await findAndReleaseMember( - arrested.id, - arrested.user.username - ); + const releaseResult = await findAndReleaseMember(arrested); - if (result?.value) { - interaction.reply({ content: `Abrindo a cela do ${arrested}.` }); - return; + if (releaseResult) { + return await interaction.reply({ + content: `Abrindo a cela do ${arrested}.`, + }); + } else if (releaseResult === null) { + return await interaction.reply({ + content: `O ${arrested} nunca foi preso.`, + }); } interaction.reply({ - content: `Não acho que o ${arrested.nickname} tava preso não.`, + content: `Não acho que o ${arrested} tava preso não.`, }); }, cooldown: 10, }; -function findAndReleaseMember(id: string, user: string) { - // Deletes user from DB. If not found, return object with value property null - return ArrestedModel.collection.findOneAndDelete({ id, user }); +async function findAndReleaseMember(member: GuildMember) { + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, + }); + + if (!guildUser) { + return null; + } + + if (!guildUser.arrested) { + return false; + } + + guildUser.arrested = false; + await guildUser.save(); + + return true; } diff --git a/src/bot/commands/slashCommands/silence.ts b/src/bot/commands/slashCommands/silence.ts index dd53b68..25c5969 100644 --- a/src/bot/commands/slashCommands/silence.ts +++ b/src/bot/commands/slashCommands/silence.ts @@ -1,7 +1,7 @@ -import { GuildMember, SlashCommandBuilder } from 'discord.js'; +import { Guild, GuildMember, SlashCommandBuilder } from 'discord.js'; import { SlashCommand } from '@marquinhos/types'; -import SilencedModel from '@schemas/silenced'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; export const silence: SlashCommand = { command: new SlashCommandBuilder() @@ -16,28 +16,52 @@ export const silence: SlashCommand = { execute: async (interaction) => { const silenced = interaction.options.get('silenciado') ?.member as GuildMember; - if (silenced.user.id === process.env.BOT_ID) { - silenceMember(interaction.member as GuildMember); - interaction.reply({ content: 'Po, vei, seja inteligente' }); + if (silenced.user.id === interaction.client.user?.id) { + await silenceMember(interaction.member as GuildMember); + await interaction.reply({ content: 'Po, vei, seja inteligente' }); return; } if (silenced.user.bot) { - interaction.reply({ content: 'Não pode fazer isso com meus os bots.' }); + await interaction.reply({ + content: 'Não pode fazer isso com meus amigos bots.', + }); return; } - silenceMember(silenced); - interaction.reply({ content: `${silenced} SILÊNCIO!` }); + const userSilenced = silenceMember(silenced); + + if (!userSilenced) { + await interaction.reply({ content: `${silenced} já está caladinho!` }); + return; + } + await interaction.reply({ content: `${silenced} SILÊNCIO!` }); }, cooldown: 10, }; -// TODO => Segregate model by guildId -function silenceMember(member: GuildMember) { - const newSilenced = new SilencedModel({ - id: member.id, - user: member.user.username, +async function silenceMember(member: GuildMember) { + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, }); - newSilenced.save(); + + if (!guildUser) { + const newGuildUser = new GuildUserModel({ + guildId: member.guild.id, + userId: member.id, + silenced: true, + }); + await newGuildUser.save(); + return true; + } + + if (guildUser.silenced) { + return null; + } + + guildUser.silenced = true; + await guildUser.save(); + + return true; } diff --git a/src/bot/commands/slashCommands/unsilence.ts b/src/bot/commands/slashCommands/unsilence.ts index 835cd57..ed95f3c 100644 --- a/src/bot/commands/slashCommands/unsilence.ts +++ b/src/bot/commands/slashCommands/unsilence.ts @@ -1,6 +1,6 @@ import { GuildMember, SlashCommandBuilder } from 'discord.js'; import { SlashCommand } from '@marquinhos/types'; -import SilencedModel from '@schemas/silenced'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; export const unsilence: SlashCommand = { command: new SlashCommandBuilder() @@ -24,12 +24,14 @@ export const unsilence: SlashCommand = { } // Search for the member in the BD - const result = await findAndUnsilenceMember( - unsilenced.id, - unsilenced.user.username - ); + const unsilencedGuildUser = await findAndUnsilenceMember(unsilenced); - if (result?.value) { + if (unsilencedGuildUser === null) { + interaction.reply({ content: `O ${unsilenced} nunca foi silenciado.` }); + return; + } + + if (unsilencedGuildUser) { interaction.reply({ content: `${unsilenced} pode falar novamente.` }); return; } @@ -41,7 +43,21 @@ export const unsilence: SlashCommand = { cooldown: 10, }; -function findAndUnsilenceMember(id: string, user: string) { - // Deletes user from DB. If not found, return object with value property null - return SilencedModel.collection.findOneAndDelete({ id, user }); +async function findAndUnsilenceMember(member: GuildMember) { + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, + }); + + if (!guildUser) { + return null; + } + + if (!guildUser.silenced) { + return false; + } + + guildUser.silenced = false; + await guildUser.save(); + return true; } diff --git a/src/bot/events/messageCreate.ts b/src/bot/events/messageCreate.ts index d6c3b4c..54ac942 100644 --- a/src/bot/events/messageCreate.ts +++ b/src/bot/events/messageCreate.ts @@ -13,8 +13,8 @@ import { BotEvent } from '@marquinhos/types'; import { logger } from '@utils/logger'; import { safeExecute } from '@utils/errorHandling'; import { musicBotMessageHandler } from '@utils/lastfm'; -import SilencedModel from '@schemas/silenced'; import BotError from '@utils/botError'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; dotenv.config(); @@ -148,8 +148,14 @@ async function silencedUserHandler(message: Message) { } async function isUserSilenced(member: GuildMember) { - return await SilencedModel.collection.findOne({ - id: member.id, - user: member.user.username, + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, }); + + if (!guildUser) { + return false; + } + + return guildUser.silenced; } diff --git a/src/bot/events/voiceStateUpdate.ts b/src/bot/events/voiceStateUpdate.ts index f45ba1e..33539fe 100644 --- a/src/bot/events/voiceStateUpdate.ts +++ b/src/bot/events/voiceStateUpdate.ts @@ -1,7 +1,7 @@ import { GuildMember, VoiceState } from 'discord.js'; import { BotEvent } from '@marquinhos/types'; -import ArrestedModel from '@schemas/arrested'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; // WIP export const voiceStateUpdate: BotEvent = { @@ -47,10 +47,16 @@ async function userChangedVoiceState(member: GuildMember) { } async function isUserArrested(member: GuildMember) { - return ArrestedModel.collection.findOne({ - id: member.id, - user: member.user.username, + const guildUser = await GuildUserModel.findOne({ + guildId: member.guild.id, + userId: member.id, }); + + if (!guildUser) { + return false; + } + + return guildUser.arrested; } async function arrestUser(member: GuildMember) { diff --git a/src/database/schemas/arrested.ts b/src/database/schemas/arrested.ts deleted file mode 100644 index 4a923a6..0000000 --- a/src/database/schemas/arrested.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Schema, model } from 'mongoose'; -import { IArrested } from '@marquinhos/types'; - -const ArrestedSchema = new Schema({ - id: { required: true, type: String }, - user: { required: true, type: String }, -}); - -const ArrestedModel = model('arrested', ArrestedSchema); - -export default ArrestedModel; diff --git a/src/database/schemas/balance.ts b/src/database/schemas/balance.ts index dcc98bb..a661375 100644 --- a/src/database/schemas/balance.ts +++ b/src/database/schemas/balance.ts @@ -1,12 +1,5 @@ import { Schema, model } from 'mongoose'; -import { IBalance, IBalanceStatement } from '@marquinhos/types'; - -const BalanceSchema = new Schema({ - userId: { required: true, type: String }, - guildId: { required: true, type: String }, - amount: { required: true, type: Number }, - lastUpdate: { required: true, type: Date }, -}); +import { IBalanceStatement } from '@marquinhos/types'; const BalanceStatementSchema = new Schema({ userId: { required: true, type: String }, @@ -21,8 +14,6 @@ const BalanceStatementSchema = new Schema({ date: { required: true, type: Date }, }); -const BalanceModel = model('balance', BalanceSchema); - const BalanceStatementModel = model('balanceStatement', BalanceStatementSchema); -export { BalanceModel, BalanceStatementModel }; +export { BalanceStatementModel }; diff --git a/src/database/schemas/guildUser.ts b/src/database/schemas/guildUser.ts index ec98f28..6a2b786 100644 --- a/src/database/schemas/guildUser.ts +++ b/src/database/schemas/guildUser.ts @@ -7,6 +7,7 @@ const GuildUserSchema = new Schema({ silenced: { type: Boolean, default: false }, arrested: { type: Boolean, default: false }, coins: { type: Number, default: 0 }, + lastBalanceUpdate: { type: Date, default: new Date(0) }, lastBonusRedeemed: { type: Date, default: new Date(0) }, lastBetOnCoinFlip: { type: Date, default: new Date(0) }, freeCoinFlipCount: { type: Number, default: 0 }, diff --git a/src/database/schemas/silenced.ts b/src/database/schemas/silenced.ts deleted file mode 100644 index ccac8bb..0000000 --- a/src/database/schemas/silenced.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Schema, model } from 'mongoose'; -import { ISilenced } from '@marquinhos/types'; - -const SilencedSchema = new Schema({ - id: { required: true, type: String }, - user: { required: true, type: String }, -}); - -const SilencedModel = model('silenced', SilencedSchema); - -export default SilencedModel; diff --git a/src/services/coinBalanceManager.ts b/src/services/coinBalanceManager.ts index 3c24486..ca77bac 100644 --- a/src/services/coinBalanceManager.ts +++ b/src/services/coinBalanceManager.ts @@ -1,11 +1,9 @@ -import { - BalanceModel, - BalanceStatementModel, -} from '@marquinhos/database/schemas/balance'; +import { BalanceStatementModel } from '@marquinhos/database/schemas/balance'; +import GuildUserModel from '@marquinhos/database/schemas/guildUser'; import { BalanceChangeStatus, - IBalance, IBalanceStatement, + IGuildUser, } from '@marquinhos/types'; import { coerceNumberProperty } from '@marquinhos/utils/coercion'; import { Document } from 'mongoose'; @@ -14,13 +12,13 @@ export async function getCoinBalance( userId: string, guildId: string ): Promise { - const userBalance = await BalanceModel.findOne({ userId, guildId }).exec(); + const userBalance = await GuildUserModel.findOne({ userId, guildId }).exec(); if (!userBalance) { return null; } - return coerceNumberProperty(userBalance.amount); + return coerceNumberProperty(userBalance.coins); } export async function getBalanceStatement( @@ -45,7 +43,10 @@ export async function updateCoinBalance( let balanceUpdated = false; if (validAmount) { - const userBalance = await BalanceModel.findOne({ userId, guildId }).exec(); + const userBalance = await GuildUserModel.findOne({ + userId, + guildId, + }).exec(); if (!userBalance) { balanceUpdated = await createBalance(userId, guildId, coercedAmount); @@ -80,10 +81,13 @@ export async function addCoins( let balanceUpdated = false; if (validAmount) { - const userBalance = await BalanceModel.findOne({ userId, guildId }).exec(); + const userBalance = await GuildUserModel.findOne({ + userId, + guildId, + }).exec(); if (userBalance) { - const oldAmount = coerceNumberProperty(userBalance.amount); + const oldAmount = coerceNumberProperty(userBalance.coins); balanceUpdated = await updateBalance( userBalance, oldAmount + coercedAmount @@ -120,10 +124,13 @@ export async function subtractCoins( let statementCreated = false; if (validAmount) { - const userBalance = await BalanceModel.findOne({ userId, guildId }).exec(); + const userBalance = await GuildUserModel.findOne({ + userId, + guildId, + }).exec(); if (userBalance) { - const oldAmount = coerceNumberProperty(userBalance.amount); + const oldAmount = coerceNumberProperty(userBalance.coins); const newAmount = oldAmount - coercedAmount; if (newAmount >= 0) { balanceUpdated = await updateBalance(userBalance, newAmount); @@ -155,7 +162,7 @@ export async function resetCoins( guildId: string, executorId: string ): Promise { - const operationSuccess = !!(await BalanceModel.deleteOne({ + const operationSuccess = !!(await GuildUserModel.deleteOne({ userId, guildId, }).exec()); @@ -174,11 +181,11 @@ export async function resetCoins( } async function updateBalance( - userBalance: Document & IBalance, + userBalance: Document & IGuildUser, amount: number ): Promise { - userBalance.amount = amount; - userBalance.lastUpdate = new Date(); + userBalance.coins = amount; + userBalance.lastBalanceUpdate = new Date(); return !!(await userBalance.save()); } @@ -187,7 +194,7 @@ async function createBalance( guildId: string, amount: number ): Promise { - return !!(await BalanceModel.create({ + return !!(await GuildUserModel.create({ userId, guildId, amount, diff --git a/src/types.d.ts b/src/types.d.ts index cedd136..db57953 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -132,19 +132,13 @@ export type FlipCoinResult = { elapsedTime: number; }; -export interface IBalance extends mongoose.Document { - userId: string; - guildId: string; - amount: number; - lastUpdate: Date; -} - export interface IGuildUser extends mongoose.Document { guildId: string; userId: string; silenced: boolean; arrested: boolean; coins: number; + lastBalanceUpdate: Date; lastBonusRedeemed: Date; lastBetOnCoinFlip: Date; freeCoinFlipCount: number;