From 0403955449c7be7131d787a10a13e0b3504525cd Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Wed, 25 Jan 2023 19:30:50 -0300 Subject: [PATCH 01/16] feat: add quiz event --- src/commands/event_coding.ts | 213 ++++++++++++++++++ src/commands/index.ts | 2 + src/defines/commands.json | 4 + src/defines/ids.json | 4 + src/defines/ids_development.json | 6 +- .../localisation/commands/event_coding.json | 17 ++ src/defines/localisation/defaults/reply.json | 4 +- src/defines/values.json | 1 + src/defines/values_development.json | 1 + src/events/cron/event_coding.ts | 13 ++ src/events/cron/index.ts | 2 + src/events/discord/channel.ts | 16 +- src/utils.ts | 21 +- 13 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 src/commands/event_coding.ts create mode 100644 src/defines/localisation/commands/event_coding.json create mode 100644 src/events/cron/event_coding.ts diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts new file mode 100644 index 0000000..9132d44 --- /dev/null +++ b/src/commands/event_coding.ts @@ -0,0 +1,213 @@ +import { CommandInteraction, DMChannel, GuildMember, SlashCommandBuilder } from 'discord.js' +import { Command } from '@/types' +import { EVENT_CODING } from '@/defines/ids.json' +import INTRODUCTION from '-/commands/introduction.json' +import CODING from '-/commands/event_coding.json' +import { START_CODE_CHALLENGE } from '@/defines/commands.json' +import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' +import { getChannel, isValidId, reply, sendInDM } from '@/utils' +import { embedTemplate } from '@/utils' +import axios from 'axios' +import https from 'https' + +const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { + try { + const result = await dm.awaitMessages({ + filter: (m) => m.author.id === interaction.user.id, + time: TIMEOUT_ANSWER, + max: 1, + }) + + return result.first()!.content + } catch (e) { + return TIMEOUT_COMMAND_STRING + } +} + +interface Reward { + he4rtCoin: number + he4rtXp: number + earned: boolean + badge: string + participantReward: boolean + id: number +} + +interface Event { + dateStart: Date + description: string + fkReward: number + isActive: boolean + id: number +} + +interface ApiResult { + type: string + message: string + dataResult: T +} + +const nextMultipleRoleSelection = async ( + roles: any[], + text: string, + dm: DMChannel, + member: GuildMember, + interaction: CommandInteraction +) => { + await dm.send(text) + await dm.send( + roles.reduce((acc, val, index) => (acc += `**${index + 1}**` + ` - ${val.emoji} ${val.name}` + '\n'), '\n') + ) + const myEmbed = embedTemplate({ + title: 'Evento de código', + description: CODING.CONTINUE, + }) + + await dm.send({ embeds: [myEmbed] }) + + const value = Number(await nextTextMessage(dm, interaction)) + + if (isValidId(value, roles)) { + member.roles.add(roles[value - 1].id) + + await nextMultipleRoleSelection(roles, text, dm, member, interaction) + + return + } + + if (value === 0) return + + await dm.send(INTRODUCTION.INVALID_NUMBER) + await nextMultipleRoleSelection(roles, text, dm, member, interaction) +} + +interface Quiz { + tip: string + question: string + answer: string + hasNextQuestion: boolean + id: number + fkEvent: number + title: string +} + +const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction): Promise => { + const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) + + const { data } = await instace.get>( + 'http://localhost:5028/api/Quiz/get-all-by-event-id?eventId=1&api-version=1' + ) + + const embedQuestions = data.dataResult.map((question) => { + return { + title: question.title, + hint: question.tip, + description: question.question, + answer: question.answer, + nextQuestion: question.hasNextQuestion, + } + }) + + for (const embedQuestion of embedQuestions) { + await dm.send({ embeds: [embedQuestion] }) + + async function retry() { + const userAnswer = await nextTextMessage(dm, interaction) + if (userAnswer === '/dica') { + dm.send(`**${embedQuestion.hint}**`) + await retry() + } else if (userAnswer !== embedQuestion.answer) { + dm.send('resposta errada') + await retry() + } else if (!embedQuestion.nextQuestion) { + dm.send('parabainsss 🎉🎉🎉🎉🎉🎉') + } else { + dm.send('próxima pergunta') + } + } + + await retry() + } +} + +const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): Promise => { + const myEmbed = embedTemplate({ + title: 'Evento de código', + description: CODING.CONTINUE, + }) + await sendInDM(dm, interaction, '', myEmbed) + + if (!sendInDM) return false + + await reply(interaction).successInAccessDM() + + return true +} + +const getReward = async () => { + const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) + const { data } = await instace.get>('https://localhost:7168/api/Reward/get-reward?api-version=1') + return data.dataResult +} + +export const useQuizEvent = (): Command => { + const data = new SlashCommandBuilder() + .setName(START_CODE_CHALLENGE.TITLE) + .setDescription(START_CODE_CHALLENGE.DESCRIPTION) + .setDMPermission(true) + + return [ + data, + async (interaction, client) => { + const author = interaction.user + const member = interaction.member as GuildMember + const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) + const { data } = await instace.get>( + 'https://localhost:7168/api/EventUser/check-user?userId=228956489690251264&api-version=1' + ) + if (data.type.toUpperCase() === 'WARNING') { + await reply(interaction).errorParticipantFail() + return + } + + const evt = await instace.get>('https://localhost:7168/api/Event/active-event?api-version=1') + + if (evt.data.type.toUpperCase() === 'NONE') { + await reply(interaction).errorEventNotFound() + return + } + client.users + .createDM(author) + .then(async (dm) => { + const valid = await validateAccess(dm, interaction) + + if (!valid) return + + await nextStringsData(dm, interaction) + + const channel = getChannel({ id: EVENT_CODING.id, client }) + await instace.post('https://localhost:7168/api/EventUser?api-version=1', { + fkEvent: 1, + fkUser: interaction.user.id, + }) + + const reward = await getReward() + + await member.roles.add(reward.badge) + + let winnerMessage = CODING.REWARD_ANNOUNCE + winnerMessage = winnerMessage.replace('{user}', `<@${interaction.user.id}>`) + winnerMessage = winnerMessage.replace('{coins}', `${reward.he4rtCoin}`) + winnerMessage = winnerMessage.replace('{exp}', `${reward.he4rtXp} XP`) + + reward.earned = true + if (!reward.participantReward) await instace.put('https://localhost:7168/api/Reward/?api-version=1', reward) + await channel?.send({ + content: `👋 ${winnerMessage}`, + }) + }) + .catch(() => {}) + .finally(async () => {}) + }, + ] +} diff --git a/src/commands/index.ts b/src/commands/index.ts index 74c9326..529c127 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -5,6 +5,7 @@ import { useBan } from './ban' import { useColor } from './color' import { useDaily } from './daily' import { useIntroduction } from './introduction' +import { useQuizEvent } from './event_coding' import { useProfileGet } from './profile/profile_get' import { useRanking } from './ranking' import { useUnban } from './unban' @@ -46,6 +47,7 @@ const registerHooks = (client: He4rtClient, commands: Command[]) => { export const registerCommands = async ({ client, rest }: Context) => { registerHooks(client, [ useIntroduction(), + useQuizEvent(), useAnnounce(), useColor(), useBan(), diff --git a/src/defines/commands.json b/src/defines/commands.json index a4910aa..3c33175 100644 --- a/src/defines/commands.json +++ b/src/defines/commands.json @@ -3,6 +3,10 @@ "TITLE": "apresentar", "DESCRIPTION": "Se apresente para os membros de nossa comunidade e ganhe cargos!" }, + "START_CODE_CHALLENGE": { + "TITLE": "ajudar-bot", + "DESCRIPTION": "Iniciar sequência de desafio de perguntas." + }, "ANNOUNCE": { "TITLE": "anunciar", "DESCRIPTION": "ADM: Anuncie no discord automaticamente com o uso do everyone." diff --git a/src/defines/ids.json b/src/defines/ids.json index 3ab7b87..1b910bb 100644 --- a/src/defines/ids.json +++ b/src/defines/ids.json @@ -75,6 +75,10 @@ "id": "1051348602259251250", "title": "🏆|voluntários" }, + "EVENT_CODING": { + "id": "1050944705610911774", + "name": "evento-he4rt" + }, "VALID_PRESENTATION_DEV_ROLES": [ { "id": "550411669084569602", "name": "GameDev", "emoji": "🎮" }, { "id": "540993488410378281", "name": "JavaScript", "emoji": "📜" }, diff --git a/src/defines/ids_development.json b/src/defines/ids_development.json index 1db8793..36268ce 100644 --- a/src/defines/ids_development.json +++ b/src/defines/ids_development.json @@ -40,7 +40,7 @@ "title": "😴 Ausente" }, "CHAT_CHANNEL": { - "id": "1042817315965763597", + "id": "1053459533667778630", "title": "💬︱bate-papo" }, "COMMANDS_CHANNEL": { @@ -75,6 +75,10 @@ "id": "1042817315965763600", "title": "🏆|voluntários" }, + "EVENT_CODING": { + "id": "1050944705610911774", + "name": "evento-he4rt" + }, "VALID_PRESENTATION_DEV_ROLES": [ { "id": "1042817315227582488", "name": "GameDev", "emoji": "🎮" }, { "id": "1042817315227582487", "name": "JavaScript", "emoji": "📜" }, diff --git a/src/defines/localisation/commands/event_coding.json b/src/defines/localisation/commands/event_coding.json new file mode 100644 index 0000000..a8601e3 --- /dev/null +++ b/src/defines/localisation/commands/event_coding.json @@ -0,0 +1,17 @@ +{ + "INTRO": "Nosso bot resolveu aprender sobre desenvolvimento e está dando seus primeiros passos com algoritimos, você está afim de ajudar nosso bot a resolver algumas questões? Se sim digite `/ajudar-bot` para iniciar esses desafios, toda ajuda é bem vinda e como recompensa você ganhará he4rt coins por participar e os três primeiros iram ganhar he4rt coins + experiência.", + "CONTINUE": "Muito obrgiado topar em ajudar!! No total temos 3 desafios para resolvermos, você poderá pedir dicas digitando `/hint` caso esteja com dificuldades e aqui vai o primeiro desafio...", + "QUESTION_1": { + "text": "Qual o resultado da seguinte equação? \n (/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) (* 3 (- 6 2) /(- 2 7)))", + "answer": "-0.24666666666666667" + }, + "QUESTION_2": { + "text": "Qual o resultado da segunda equação? \n (/ (+ 4 5 6 (/ 4 5) (- 2 3)) (* 3 (- 6 2) (- 2 7)))", + "answer": "-37/150" + }, + "QUESTION_3": { + "text": "Resolva a seguinte equação de bhaskara dado o seguinte input: 0.0 20.0 5.0", + "answer": "não computa" + }, + "REWARD_ANNOUNCE": "Parábens!!{user} Você conseguiu ajudar em todos os desafios e como recompensa você ganhou **{coins}** HCOINS e **{exp}** de experiência`!!!" +} diff --git a/src/defines/localisation/defaults/reply.json b/src/defines/localisation/defaults/reply.json index b16d7f2..3f518de 100644 --- a/src/defines/localisation/defaults/reply.json +++ b/src/defines/localisation/defaults/reply.json @@ -11,5 +11,7 @@ "ERROR_CHANNEL_PERMISSION": "Só é permitido usar este comando no canal ", "ERROR_CANNOT_BE_BANNED": "O usuário em questão não pode ser banido ou desbanido!", "ERROR_PAGINATION": "Este número de página não existe.", - "ERROR_PRESENTING": "**Você já está se apresentando! Consulte sua DM.**" + "ERROR_PRESENTING": "**Você já está se apresentando! Consulte sua DM.**", + "ERROR_PARTICIPANT_EVENT": "**Você já completou este evento.**", + "ERROR_EVENT_NO_FOUND": "**Não há evento ativo neste momento.**" } diff --git a/src/defines/values.json b/src/defines/values.json index 215e92b..700023c 100644 --- a/src/defines/values.json +++ b/src/defines/values.json @@ -2,6 +2,7 @@ "CLIENT_NAME": "He4rt Developers", "CLIENT_TIMEZONE": "America/Sao_Paulo", "TIMEOUT_COMMAND": 300000, + "TIMEOUT_ANSWER": 900000, "TIMEOUT_COMMAND_STRING": "__INVALID__RESPONSE__", "DEFINE_STRING_REPLACED": "__REPLACED__", "HE4RT_ICON_1_URL": "https://i.imgur.com/iWhfWMa.png", diff --git a/src/defines/values_development.json b/src/defines/values_development.json index 215e92b..700023c 100644 --- a/src/defines/values_development.json +++ b/src/defines/values_development.json @@ -2,6 +2,7 @@ "CLIENT_NAME": "He4rt Developers", "CLIENT_TIMEZONE": "America/Sao_Paulo", "TIMEOUT_COMMAND": 300000, + "TIMEOUT_ANSWER": 900000, "TIMEOUT_COMMAND_STRING": "__INVALID__RESPONSE__", "DEFINE_STRING_REPLACED": "__REPLACED__", "HE4RT_ICON_1_URL": "https://i.imgur.com/iWhfWMa.png", diff --git a/src/events/cron/event_coding.ts b/src/events/cron/event_coding.ts new file mode 100644 index 0000000..4851d0a --- /dev/null +++ b/src/events/cron/event_coding.ts @@ -0,0 +1,13 @@ +import { ApoiaseGET, He4rtClient, UserGET } from '@/types' +import { getGuild, getTargetMember, isApoiaseMember, js } from '@/utils' +import { APOIASE_CUSTOM_COLOR_MINIMAL_VALUE } from '@/defines/values.json' +import { EVENT_CODING } from '@/defines/ids.json' +import { CronJob } from 'cron' + +export const verifyEventCode = async (client: He4rtClient) => { + const guild = getGuild(client) + + await new CronJob('00 01 * * * *', async () => { + console.log('trigger') + }).start() +} diff --git a/src/events/cron/index.ts b/src/events/cron/index.ts index 2e6e010..4398376 100644 --- a/src/events/cron/index.ts +++ b/src/events/cron/index.ts @@ -1,6 +1,8 @@ import { He4rtClient } from '@/types' import { verifyApoiaseMembers } from './apoiase' +import { verifyEventCode } from './event_coding' export const cronEvents = async (client: He4rtClient) => { await verifyApoiaseMembers(client) + // await verifyEventCode(client) } diff --git a/src/events/discord/channel.ts b/src/events/discord/channel.ts index aea8405..a4c3ca8 100644 --- a/src/events/discord/channel.ts +++ b/src/events/discord/channel.ts @@ -1,4 +1,4 @@ -import { Message } from 'discord.js' +import { Message, TextBasedChannel } from 'discord.js' import { SUGGESTION_CHANNEL, CHAT_CHANNEL, @@ -8,8 +8,10 @@ import { ADVERTS_CHANNEL, PRESENTATIONS_CHANNEL, HE4RT_EMOJI_ID, + EVENT_CODING, } from '@/defines/ids.json' -import { isAdministrator, isImageHTTPUrl, isValidProxyContent } from '@/utils' +import CODING_EVENT from '@/defines/localisation/commands/event_coding.json' +import { embedTemplate, isAdministrator, isImageHTTPUrl, isValidProxyContent } from '@/utils' export const suppressEmbedMessagesInBusyChannels = async (message: Message) => { const validChannels = [CHAT_CHANNEL, MEETING_CHANNEL, MEETING_DELAS_CHANNEL] @@ -33,9 +35,19 @@ export const sendGoodMessagesInBusyChannels = (message: Message) => { if (validChannels.some((v) => v.id === message.channel.id)) { const content = message.content.toLowerCase().trim() + const eventCodingChannel = message.channel.client.channels.cache.find((channel) => channel.id === EVENT_CODING.id) + const myEmbed = embedTemplate({ + title: 'Evento de código', + description: CODING_EVENT.INTRO, + color: '#581c87', + }) + if (content.length > 50 && message.channel.id === CHAT_CHANNEL.id) return if (content.startsWith('bom dia')) { + ;(eventCodingChannel).send({ + embeds: [myEmbed], + }) message.reply({ content: `dia!` }).catch(() => {}) } if (content.startsWith('boa tarde')) { diff --git a/src/utils.ts b/src/utils.ts index b3ea67e..8738c3c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -44,6 +44,8 @@ import { ERROR_CANNOT_BE_BANNED, ERROR_PAGINATION, ERROR_PRESENTING, + ERROR_PARTICIPANT_EVENT, + ERROR_EVENT_NO_FOUND, } from '-/defaults/reply.json' import { NOT_FOUND, LANGUAGE_NONE } from '-/defaults/display.json' import { TIMEOUT_COMMAND_STRING, DEFINE_STRING_REPLACED } from '@/defines/values.json' @@ -202,8 +204,13 @@ export const replaceDefineString = (str: string, target: string) => { return str.replaceAll(DEFINE_STRING_REPLACED, target) } -export const sendInDM = async (dm: DMChannel, interaction: CommandInteraction | ButtonInteraction, str: string) => { - await dm.send(str).catch(async () => { +export const sendInDM = async ( + dm: DMChannel, + interaction: CommandInteraction | ButtonInteraction, + str: string, + embed?: EmbedBuilder +) => { + await dm.send({ content: str, embeds: [embed] }).catch(async () => { await reply(interaction).errorInAccessDM() return false @@ -271,6 +278,14 @@ export const reply = (interaction: CommandInteraction | ButtonInteraction) => { await interaction.reply({ content: ERROR_PRESENTING, ephemeral: true }) } + const errorParticipantFail = async () => { + await interaction.reply({ content: ERROR_PARTICIPANT_EVENT, ephemeral: true }) + } + + const errorEventNotFound = async () => { + await interaction.reply({ content: ERROR_EVENT_NO_FOUND, ephemeral: true }) + } + return { success, successInAccessDM, @@ -285,6 +300,8 @@ export const reply = (interaction: CommandInteraction | ButtonInteraction) => { errorSpecificChannel, errorPaginationFail, errorPresentingFail, + errorParticipantFail, + errorEventNotFound, } } From 718c741f3e8e58f8522209a0afa86ecac42a9c5e Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sat, 3 Jun 2023 14:21:45 -0300 Subject: [PATCH 02/16] fix: remove local api calls --- src/commands/event_coding.ts | 182 +++++++++++++------------------- src/events/cron/event_coding.ts | 2 +- src/events/cron/index.ts | 2 +- src/http/firebase.ts | 12 ++- src/types.ts | 27 +++++ 5 files changed, 113 insertions(+), 112 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index 9132d44..b25f774 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -1,15 +1,12 @@ import { CommandInteraction, DMChannel, GuildMember, SlashCommandBuilder } from 'discord.js' import { Command } from '@/types' import { EVENT_CODING } from '@/defines/ids.json' -import INTRODUCTION from '-/commands/introduction.json' import CODING from '-/commands/event_coding.json' import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' -import axios from 'axios' -import https from 'https' - +import { getReward } from '@/http/firebase' const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { try { const result = await dm.awaitMessages({ @@ -24,29 +21,6 @@ const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): } } -interface Reward { - he4rtCoin: number - he4rtXp: number - earned: boolean - badge: string - participantReward: boolean - id: number -} - -interface Event { - dateStart: Date - description: string - fkReward: number - isActive: boolean - id: number -} - -interface ApiResult { - type: string - message: string - dataResult: T -} - const nextMultipleRoleSelection = async ( roles: any[], text: string, @@ -77,57 +51,49 @@ const nextMultipleRoleSelection = async ( if (value === 0) return - await dm.send(INTRODUCTION.INVALID_NUMBER) + await dm.send('Voce erroou brother') await nextMultipleRoleSelection(roles, text, dm, member, interaction) } -interface Quiz { - tip: string - question: string - answer: string - hasNextQuestion: boolean - id: number - fkEvent: number - title: string -} -const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction): Promise => { - const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) - const { data } = await instace.get>( - 'http://localhost:5028/api/Quiz/get-all-by-event-id?eventId=1&api-version=1' - ) - - const embedQuestions = data.dataResult.map((question) => { - return { - title: question.title, - hint: question.tip, - description: question.question, - answer: question.answer, - nextQuestion: question.hasNextQuestion, - } - }) - - for (const embedQuestion of embedQuestions) { - await dm.send({ embeds: [embedQuestion] }) - - async function retry() { - const userAnswer = await nextTextMessage(dm, interaction) - if (userAnswer === '/dica') { - dm.send(`**${embedQuestion.hint}**`) - await retry() - } else if (userAnswer !== embedQuestion.answer) { - dm.send('resposta errada') - await retry() - } else if (!embedQuestion.nextQuestion) { - dm.send('parabainsss 🎉🎉🎉🎉🎉🎉') - } else { - dm.send('próxima pergunta') - } - } - - await retry() - } +const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction): Promise => { + // const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) + + // const { data } = await instace.get>( + // 'http://localhost:5028/api/Quiz/get-all-by-event-id?eventId=1&api-version=1' + // ) + + // const embedQuestions = data.dataResult.map((question) => { + // return { + // title: question.title, + // hint: question.tip, + // description: question.question, + // answer: question.answer, + // nextQuestion: question.hasNextQuestion, + // } + // }) + + // for (const embedQuestion of embedQuestions) { + // await dm.send({ embeds: [embedQuestion] }) + + // async function retry() { + // const userAnswer = await nextTextMessage(dm, interaction) + // if (userAnswer === '/dica') { + // dm.send(`**${embedQuestion.hint}**`) + // await retry() + // } else if (userAnswer !== embedQuestion.answer) { + // dm.send('resposta errada') + // await retry() + // } else if (!embedQuestion.nextQuestion) { + // dm.send('parabainsss 🎉🎉🎉🎉🎉🎉') + // } else { + // dm.send('próxima pergunta') + // } + // } + + // await retry() + // } } const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): Promise => { @@ -144,11 +110,6 @@ const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): P return true } -const getReward = async () => { - const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) - const { data } = await instace.get>('https://localhost:7168/api/Reward/get-reward?api-version=1') - return data.dataResult -} export const useQuizEvent = (): Command => { const data = new SlashCommandBuilder() @@ -161,21 +122,26 @@ export const useQuizEvent = (): Command => { async (interaction, client) => { const author = interaction.user const member = interaction.member as GuildMember - const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) - const { data } = await instace.get>( - 'https://localhost:7168/api/EventUser/check-user?userId=228956489690251264&api-version=1' - ) - if (data.type.toUpperCase() === 'WARNING') { - await reply(interaction).errorParticipantFail() - return - } - - const evt = await instace.get>('https://localhost:7168/api/Event/active-event?api-version=1') - - if (evt.data.type.toUpperCase() === 'NONE') { - await reply(interaction).errorEventNotFound() - return - } + + const result = getReward(client) + + console.log(result) + + // const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) + // const { data } = await instace.get>( + // 'https://localhost:7168/api/EventUser/check-user?userId=228956489690251264&api-version=1' + // ) + // if (data.type.toUpperCase() === 'WARNING') { + // await reply(interaction).errorParticipantFail() + // return + // } + + // const evt = await instace.get>('https://localhost:7168/api/Event/active-event?api-version=1') + + // if (evt.data.type.toUpperCase() === 'NONE') { + // await reply(interaction).errorEventNotFound() + // return + // } client.users .createDM(author) .then(async (dm) => { @@ -186,25 +152,25 @@ export const useQuizEvent = (): Command => { await nextStringsData(dm, interaction) const channel = getChannel({ id: EVENT_CODING.id, client }) - await instace.post('https://localhost:7168/api/EventUser?api-version=1', { - fkEvent: 1, - fkUser: interaction.user.id, - }) + // await instace.post('https://localhost:7168/api/EventUser?api-version=1', { + // fkEvent: 1, + // fkUser: interaction.user.id, + // }) - const reward = await getReward() + // const reward = await getReward() - await member.roles.add(reward.badge) + // await member.roles.add(reward.badge) - let winnerMessage = CODING.REWARD_ANNOUNCE - winnerMessage = winnerMessage.replace('{user}', `<@${interaction.user.id}>`) - winnerMessage = winnerMessage.replace('{coins}', `${reward.he4rtCoin}`) - winnerMessage = winnerMessage.replace('{exp}', `${reward.he4rtXp} XP`) + // let winnerMessage = CODING.REWARD_ANNOUNCE + // winnerMessage = winnerMessage.replace('{user}', `<@${interaction.user.id}>`) + // winnerMessage = winnerMessage.replace('{coins}', `${reward.he4rtCoin}`) + // winnerMessage = winnerMessage.replace('{exp}', `${reward.he4rtXp} XP`) - reward.earned = true - if (!reward.participantReward) await instace.put('https://localhost:7168/api/Reward/?api-version=1', reward) - await channel?.send({ - content: `👋 ${winnerMessage}`, - }) + // reward.earned = true + // if (!reward.participantReward) await instace.put('https://localhost:7168/api/Reward/?api-version=1', reward) + // await channel?.send({ + // content: `👋 ${winnerMessage}`, + // }) }) .catch(() => {}) .finally(async () => {}) diff --git a/src/events/cron/event_coding.ts b/src/events/cron/event_coding.ts index 4851d0a..e8eb3a6 100644 --- a/src/events/cron/event_coding.ts +++ b/src/events/cron/event_coding.ts @@ -7,7 +7,7 @@ import { CronJob } from 'cron' export const verifyEventCode = async (client: He4rtClient) => { const guild = getGuild(client) - await new CronJob('00 01 * * * *', async () => { + await new CronJob('0 */1 * * *', async () => { console.log('trigger') }).start() } diff --git a/src/events/cron/index.ts b/src/events/cron/index.ts index 4398376..af21e9c 100644 --- a/src/events/cron/index.ts +++ b/src/events/cron/index.ts @@ -4,5 +4,5 @@ import { verifyEventCode } from './event_coding' export const cronEvents = async (client: He4rtClient) => { await verifyApoiaseMembers(client) - // await verifyEventCode(client) + await verifyEventCode(client) } diff --git a/src/http/firebase.ts b/src/http/firebase.ts index ab4f6d9..baad9e5 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -1,4 +1,4 @@ -import { FirestoreMedal, FirestoreMedalUser, FirestoreUser, He4rtClient } from '@/types' +import { FirestoreMedal, FirestoreMedalUser, FirestoreUser, FirestoreReward, FirestoreEvent, FirestoreQuiz, He4rtClient } from '@/types' import { defu } from 'defu' export const upsertUser = async (client: He4rtClient, fields: Partial) => { @@ -18,7 +18,7 @@ export const deleteUser = async (client: He4rtClient, fields: Pick) => { const collection = client.firestore.collection('users') - + return collection.doc(fields.id).create({ id: fields.id }) } @@ -135,3 +135,11 @@ export const addUserInMedal = async ( return userCollection.doc(fields.id).set({ id: fields.id, expires_at: fields.expires_at }) } + +export const getReward = async (client: He4rtClient) => { + const rewardsCollection = client.firestore.collection('rewards') + + const reward = await rewardsCollection.doc('4VA73aMLRsmyqQiMs3PM').get() + console.log(reward.data() as FirestoreReward) + return reward.data() as FirestoreReward +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index b33f0a2..deffa99 100644 --- a/src/types.ts +++ b/src/types.ts @@ -224,3 +224,30 @@ export interface FirestoreMedalUser { id: string expires_at: string } + +export interface FirestoreReward { + he4rt_xp: number + earned: boolean + badge: string + participant_reward: boolean + id: string + fk_event: string +} + +export interface FirestoreEvent { + date_start: Date + date_end: Date + description: string + is_active: boolean + id: string +} + +export interface FirestoreQuiz { + tip: string + question: string + answer: string + has_next_question: boolean + id: string + fk_event: number + title: string +} \ No newline at end of file From f3c415aa71faf41f9ac91e3dfafe4cdc78dd2f8e Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sat, 3 Jun 2023 16:25:47 -0300 Subject: [PATCH 03/16] fix: claim reward logic --- src/commands/event_coding.ts | 6 ++---- src/http/firebase.ts | 38 ++++++++++++++++++++++++++++++++---- src/types.ts | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index b25f774..69988bf 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -6,7 +6,7 @@ import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' -import { getReward } from '@/http/firebase' +import { claimEventReward } from '@/http/firebase' const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { try { const result = await dm.awaitMessages({ @@ -123,10 +123,8 @@ export const useQuizEvent = (): Command => { const author = interaction.user const member = interaction.member as GuildMember - const result = getReward(client) - + const result = await claimEventReward(client, 'ognAHjTLGPO1XEqkBMk0', member.id) console.log(result) - // const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) // const { data } = await instace.get>( // 'https://localhost:7168/api/EventUser/check-user?userId=228956489690251264&api-version=1' diff --git a/src/http/firebase.ts b/src/http/firebase.ts index baad9e5..97ccd99 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -136,10 +136,40 @@ export const addUserInMedal = async ( return userCollection.doc(fields.id).set({ id: fields.id, expires_at: fields.expires_at }) } -export const getReward = async (client: He4rtClient) => { - const rewardsCollection = client.firestore.collection('rewards') +const addUserEvent = async (client: He4rtClient, {user, eventId}): Promise => { + const collection = client.firestore.collection('users_event') + + await collection.add({ id: user, event: eventId}) +} + +const updateEventReward = async (client: He4rtClient, reward: FirebaseFirestore.QueryDocumentSnapshot): Promise => { + const collection = client.firestore.collection('rewards') + const entity = defu({earned: true}, reward.data() as FirestoreReward) - const reward = await rewardsCollection.doc('4VA73aMLRsmyqQiMs3PM').get() - console.log(reward.data() as FirestoreReward) + collection.doc(reward.id).set(entity) +} + +const getReward = async (client: He4rtClient, eventId: string, place?: string) => { + const rewardsCollection = client.firestore.collection('rewards') + + const query = place === 'participant' + ? await rewardsCollection.where('fk_event', '==', eventId).where('earned', '==', false).limit(1).get() + : await rewardsCollection.where('fk_event', '==', eventId).where('place', '==', 'participant').limit(1).get() + + + const reward = query.docs[0] + return reward +} + +export const claimEventReward = async (client: He4rtClient, eventId: string, memberId: string) => { + const result = await getReward(client, eventId) + + const avaliableReward = result.data() as FirestoreReward + + const reward = avaliableReward.place === 'participant' + ? await getReward(client, eventId, 'participant') : result + + await addUserEvent(client, { user: memberId, eventId }) + await updateEventReward(client, reward) return reward.data() as FirestoreReward } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index deffa99..66b5940 100644 --- a/src/types.ts +++ b/src/types.ts @@ -229,7 +229,7 @@ export interface FirestoreReward { he4rt_xp: number earned: boolean badge: string - participant_reward: boolean + place: string id: string fk_event: string } From c0d58c144c8e076cb253a51a03a76e6c3f5f05bf Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sat, 3 Jun 2023 19:12:57 -0300 Subject: [PATCH 04/16] fix: function to valid if user has already participated --- src/commands/event_coding.ts | 27 ++++++++++++--------------- src/http/firebase.ts | 10 ++++++++++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index 69988bf..bf3800c 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -150,25 +150,22 @@ export const useQuizEvent = (): Command => { await nextStringsData(dm, interaction) const channel = getChannel({ id: EVENT_CODING.id, client }) - // await instace.post('https://localhost:7168/api/EventUser?api-version=1', { - // fkEvent: 1, - // fkUser: interaction.user.id, - // }) - // const reward = await getReward() + const claimedReward = await claimEventReward(client, 'sadkaskdaskdmklam', interaction.user.id) - // await member.roles.add(reward.badge) + await member.roles.add(claimedReward.badge) - // let winnerMessage = CODING.REWARD_ANNOUNCE - // winnerMessage = winnerMessage.replace('{user}', `<@${interaction.user.id}>`) - // winnerMessage = winnerMessage.replace('{coins}', `${reward.he4rtCoin}`) - // winnerMessage = winnerMessage.replace('{exp}', `${reward.he4rtXp} XP`) + let winnerMessage = CODING.REWARD_ANNOUNCE + winnerMessage = winnerMessage + .replace('{user}', `<@${interaction.user.id}>`) + .replace('{exp}', `${claimedReward.he4rt_xp} XP`) - // reward.earned = true - // if (!reward.participantReward) await instace.put('https://localhost:7168/api/Reward/?api-version=1', reward) - // await channel?.send({ - // content: `👋 ${winnerMessage}`, - // }) + if (claimedReward.place !== 'participant') { + await channel?.send({ + content: `👋 ${winnerMessage}`, + }) + } + }) .catch(() => {}) .finally(async () => {}) diff --git a/src/http/firebase.ts b/src/http/firebase.ts index 97ccd99..f22b4bc 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -172,4 +172,14 @@ export const claimEventReward = async (client: He4rtClient, eventId: string, mem await addUserEvent(client, { user: memberId, eventId }) await updateEventReward(client, reward) return reward.data() as FirestoreReward +} + +export const checkUserEventEntry = async (client: He4rtClient, {userId, eventId}): Promise => { + const eventUserCollection = client.firestore.collection('users_event') + + return await !!eventUserCollection + .where('event', '==', eventId) + .where('id', '==', userId) + .limit(1) + .get() } \ No newline at end of file From 0e15cdf387b6198a6018a7c5bf47381eabd2aaac Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sat, 3 Jun 2023 21:26:49 -0300 Subject: [PATCH 05/16] fix: start of quiz challenge and check for eligible users --- src/commands/event_coding.ts | 32 ++++++++++++++------------------ src/http/firebase.ts | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index bf3800c..a425af1 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -6,7 +6,7 @@ import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' -import { claimEventReward } from '@/http/firebase' +import { checkUserEventEntry, claimEventReward, getActiveEvent } from '@/http/firebase' const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { try { const result = await dm.awaitMessages({ @@ -123,23 +123,19 @@ export const useQuizEvent = (): Command => { const author = interaction.user const member = interaction.member as GuildMember - const result = await claimEventReward(client, 'ognAHjTLGPO1XEqkBMk0', member.id) - console.log(result) - // const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) - // const { data } = await instace.get>( - // 'https://localhost:7168/api/EventUser/check-user?userId=228956489690251264&api-version=1' - // ) - // if (data.type.toUpperCase() === 'WARNING') { - // await reply(interaction).errorParticipantFail() - // return - // } - - // const evt = await instace.get>('https://localhost:7168/api/Event/active-event?api-version=1') - - // if (evt.data.type.toUpperCase() === 'NONE') { - // await reply(interaction).errorEventNotFound() - // return - // } + const activeEvent = await getActiveEvent(client) + if (!activeEvent) { + await reply(interaction).errorEventNotFound() + return + } + + const isUserEligible = await checkUserEventEntry(client, { eventId: 'ognAHjTLGPO1XEqkBMk0', userId: interaction.user.id }) + + if (!isUserEligible) { + await reply(interaction).errorParticipantFail() + return + } + client.users .createDM(author) .then(async (dm) => { diff --git a/src/http/firebase.ts b/src/http/firebase.ts index f22b4bc..bc99590 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -177,9 +177,24 @@ export const claimEventReward = async (client: He4rtClient, eventId: string, mem export const checkUserEventEntry = async (client: He4rtClient, {userId, eventId}): Promise => { const eventUserCollection = client.firestore.collection('users_event') - return await !!eventUserCollection + const query = await eventUserCollection .where('event', '==', eventId) .where('id', '==', userId) .limit(1) .get() + + return !!query.empty +} + +export const getActiveEvent = async (client: He4rtClient) => { + const eventCollection = client.firestore.collection('events') + + const query = await eventCollection + .where('is_active', '==', true) + .limit(1) + .get() + + if(!query.empty) return query.docs[0].id + + return ''; } \ No newline at end of file From 0d51ec956a59919bcd5d5ee1d45aa69e3b60f52b Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sat, 3 Jun 2023 22:15:23 -0300 Subject: [PATCH 06/16] fix: quiz question logic --- src/commands/event_coding.ts | 78 +++++++++++++++--------------------- src/http/firebase.ts | 11 +++++ 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index a425af1..6ef087d 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -1,12 +1,12 @@ import { CommandInteraction, DMChannel, GuildMember, SlashCommandBuilder } from 'discord.js' -import { Command } from '@/types' +import { Command, He4rtClient } from '@/types' import { EVENT_CODING } from '@/defines/ids.json' import CODING from '-/commands/event_coding.json' import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' -import { checkUserEventEntry, claimEventReward, getActiveEvent } from '@/http/firebase' +import { checkUserEventEntry, claimEventReward, getActiveEvent, getEventQuizzesById } from '@/http/firebase' const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { try { const result = await dm.awaitMessages({ @@ -51,49 +51,35 @@ const nextMultipleRoleSelection = async ( if (value === 0) return - await dm.send('Voce erroou brother') await nextMultipleRoleSelection(roles, text, dm, member, interaction) } -const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction): Promise => { - // const instace = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }) - - // const { data } = await instace.get>( - // 'http://localhost:5028/api/Quiz/get-all-by-event-id?eventId=1&api-version=1' - // ) - - // const embedQuestions = data.dataResult.map((question) => { - // return { - // title: question.title, - // hint: question.tip, - // description: question.question, - // answer: question.answer, - // nextQuestion: question.hasNextQuestion, - // } - // }) - - // for (const embedQuestion of embedQuestions) { - // await dm.send({ embeds: [embedQuestion] }) - - // async function retry() { - // const userAnswer = await nextTextMessage(dm, interaction) - // if (userAnswer === '/dica') { - // dm.send(`**${embedQuestion.hint}**`) - // await retry() - // } else if (userAnswer !== embedQuestion.answer) { - // dm.send('resposta errada') - // await retry() - // } else if (!embedQuestion.nextQuestion) { - // dm.send('parabainsss 🎉🎉🎉🎉🎉🎉') - // } else { - // dm.send('próxima pergunta') - // } - // } - - // await retry() - // } +const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, client: He4rtClient, eventId: string): Promise => { + + const questions = await getEventQuizzesById(client, eventId) + + for (const question of questions) { + await dm.send({ embeds: [question] }) + + async function retry() { + const userInput = await nextTextMessage(dm, interaction) + if (userInput === '/hint') { + dm.send(`**${question.tip}**`) + await retry() + } else if (userInput !== question.answer) { + dm.send('Resposta errada') + await retry() + } else if (!question.has_next_question) { + dm.send('Parabéns!! você conseguiu concluir o evento🎉🎉🎉') + } else { + dm.send('**Próxima pergunta**') + } + } + + await retry() + } } const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): Promise => { @@ -123,13 +109,13 @@ export const useQuizEvent = (): Command => { const author = interaction.user const member = interaction.member as GuildMember - const activeEvent = await getActiveEvent(client) - if (!activeEvent) { + const activeEventId = await getActiveEvent(client) + if (!activeEventId) { await reply(interaction).errorEventNotFound() return } - const isUserEligible = await checkUserEventEntry(client, { eventId: 'ognAHjTLGPO1XEqkBMk0', userId: interaction.user.id }) + const isUserEligible = await checkUserEventEntry(client, { eventId: activeEventId, userId: interaction.user.id }) if (!isUserEligible) { await reply(interaction).errorParticipantFail() @@ -143,12 +129,12 @@ export const useQuizEvent = (): Command => { if (!valid) return - await nextStringsData(dm, interaction) + await nextStringsData(dm, interaction, client, activeEventId) const channel = getChannel({ id: EVENT_CODING.id, client }) - const claimedReward = await claimEventReward(client, 'sadkaskdaskdmklam', interaction.user.id) - + const claimedReward = await claimEventReward(client, activeEventId, interaction.user.id) + await member.roles.add(claimedReward.badge) let winnerMessage = CODING.REWARD_ANNOUNCE diff --git a/src/http/firebase.ts b/src/http/firebase.ts index bc99590..c0a40a5 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -197,4 +197,15 @@ export const getActiveEvent = async (client: He4rtClient) => { if(!query.empty) return query.docs[0].id return ''; +} + +export const getEventQuizzesById = async (client: He4rtClient, eventId) => { + const quizzesCollection = client.firestore.collection('quizzes') + + const query = await quizzesCollection + .where('fk_event', '==', eventId) + .get() + + const questions = query.docs.map((doc) => doc.data()); + return questions as FirestoreQuiz[] } \ No newline at end of file From 15ba9add1cba51adf8829eeaacafbb62e26e44e2 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sun, 4 Jun 2023 11:18:33 -0300 Subject: [PATCH 07/16] fix: embed text messages --- src/commands/event_coding.ts | 69 +++++++++++++++---- .../localisation/commands/event_coding.json | 17 +---- src/types.ts | 5 +- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index 6ef087d..919fb5e 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -33,7 +33,7 @@ const nextMultipleRoleSelection = async ( roles.reduce((acc, val, index) => (acc += `**${index + 1}**` + ` - ${val.emoji} ${val.name}` + '\n'), '\n') ) const myEmbed = embedTemplate({ - title: 'Evento de código', + title: '', description: CODING.CONTINUE, }) @@ -58,23 +58,46 @@ const nextMultipleRoleSelection = async ( const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, client: He4rtClient, eventId: string): Promise => { - const questions = await getEventQuizzesById(client, eventId) + const quizzes = await getEventQuizzesById(client, eventId) - for (const question of questions) { - await dm.send({ embeds: [question] }) + for (const quiz of quizzes) { + const embed = embedTemplate({ title: quiz.title, description: quiz.question }) + await dm.send({ embeds: [embed] }) + + const embedWrongAnswer = embedTemplate({ + title: 'Excelente tentativa, mas a resposta não está correta.', + description: 'Essa foi a resposta errada.', + color: '#FF0000' + }) + + const embedRightAnswer = embedTemplate({ + title: 'Ótima resposta, aqui vai a próxima!', + description: `Respostas: **${quiz.answer}**`, + color: '#00FF00' + }) + + const embedTipQuestion = embedTemplate({ + title: quiz.tip, + color: '#00FFFF' + }) + + const embedFinishedEvent = embedTemplate({ + title: 'Parabéns!! você conseguiu concluir o evento🎉🎉🎉', + }) async function retry() { const userInput = await nextTextMessage(dm, interaction) - if (userInput === '/hint') { - dm.send(`**${question.tip}**`) + if (userInput === '!dica') { + dm.send({embeds: [embedTipQuestion]}) await retry() - } else if (userInput !== question.answer) { - dm.send('Resposta errada') + } else if (userInput !== quiz.answer) { + dm.send('💥') + dm.send({embeds: [embedWrongAnswer]}) await retry() - } else if (!question.has_next_question) { - dm.send('Parabéns!! você conseguiu concluir o evento🎉🎉🎉') + } else if (!quiz.has_next_question) { + dm.send({ embeds: [embedFinishedEvent]}) } else { - dm.send('**Próxima pergunta**') + dm.send({ embeds: [embedRightAnswer]}) } } @@ -84,7 +107,7 @@ const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, c const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): Promise => { const myEmbed = embedTemplate({ - title: 'Evento de código', + title: 'Evento de programação', description: CODING.CONTINUE, }) await sendInDM(dm, interaction, '', myEmbed) @@ -122,6 +145,12 @@ export const useQuizEvent = (): Command => { return } + const places = { + first: 'Primeiro', + second: 'Segundo', + third: 'Terceiro', + } + client.users .createDM(author) .then(async (dm) => { @@ -142,9 +171,23 @@ export const useQuizEvent = (): Command => { .replace('{user}', `<@${interaction.user.id}>`) .replace('{exp}', `${claimedReward.he4rt_xp} XP`) + let participantMessage = CODING.REWARD_PARTICIPANT + participantMessage = participantMessage + .replace('{user}', `<@${interaction.user.id}>`) + .replace('{exp}', `${claimedReward.he4rt_xp} XP`) + + const participantEmbed = embedTemplate({title: participantMessage}) + + const winnerEmbed = embedTemplate({ + title: `${interaction.user.tag} conseguiu concluir todas as respostas com sucesso!`, + description: `Como recompensa do ${places[claimedReward.place]} lugar ganhou **${claimedReward.he4rt_xp}** de experiência!` + }) + if(claimedReward.place === 'participant') + await dm.send({ embeds: [(participantEmbed)]}) if (claimedReward.place !== 'participant') { await channel?.send({ - content: `👋 ${winnerMessage}`, + content: `👋`, + embeds: [winnerEmbed] }) } diff --git a/src/defines/localisation/commands/event_coding.json b/src/defines/localisation/commands/event_coding.json index a8601e3..a41852f 100644 --- a/src/defines/localisation/commands/event_coding.json +++ b/src/defines/localisation/commands/event_coding.json @@ -1,17 +1,6 @@ { "INTRO": "Nosso bot resolveu aprender sobre desenvolvimento e está dando seus primeiros passos com algoritimos, você está afim de ajudar nosso bot a resolver algumas questões? Se sim digite `/ajudar-bot` para iniciar esses desafios, toda ajuda é bem vinda e como recompensa você ganhará he4rt coins por participar e os três primeiros iram ganhar he4rt coins + experiência.", - "CONTINUE": "Muito obrgiado topar em ajudar!! No total temos 3 desafios para resolvermos, você poderá pedir dicas digitando `/hint` caso esteja com dificuldades e aqui vai o primeiro desafio...", - "QUESTION_1": { - "text": "Qual o resultado da seguinte equação? \n (/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) (* 3 (- 6 2) /(- 2 7)))", - "answer": "-0.24666666666666667" - }, - "QUESTION_2": { - "text": "Qual o resultado da segunda equação? \n (/ (+ 4 5 6 (/ 4 5) (- 2 3)) (* 3 (- 6 2) (- 2 7)))", - "answer": "-37/150" - }, - "QUESTION_3": { - "text": "Resolva a seguinte equação de bhaskara dado o seguinte input: 0.0 20.0 5.0", - "answer": "não computa" - }, - "REWARD_ANNOUNCE": "Parábens!!{user} Você conseguiu ajudar em todos os desafios e como recompensa você ganhou **{coins}** HCOINS e **{exp}** de experiência`!!!" + "CONTINUE": "Você topou o desafio!! No total temos 3 perguntas para você responder, você poderá pedir dicas digitando `!dica` caso esteja com dificuldades e aqui vai a primeira pergunta...", + "REWARD_ANNOUNCE": "Parábens!!{user} Você conseguiu responder todas as perguntas e como recompensa você ganhou **{exp}** de experiência!", + "REWARD_PARTICIPANT": "Parábens!! {user} Você conseguiu responder todas as perguntas e como recompensa você ganhou **{exp}** de experiência!" } diff --git a/src/types.ts b/src/types.ts index 66b5940..307b399 100644 --- a/src/types.ts +++ b/src/types.ts @@ -244,10 +244,9 @@ export interface FirestoreEvent { export interface FirestoreQuiz { tip: string - question: string answer: string has_next_question: boolean - id: string fk_event: number - title: string + title: string, + question: string } \ No newline at end of file From 223e14d35f6f1a9208b2d6e5bf4d29c49e7f7a34 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sun, 4 Jun 2023 15:27:14 -0300 Subject: [PATCH 08/16] fix: claimed reward for participant --- src/commands/event_coding.ts | 28 +++++++++------------------- src/http/firebase.ts | 18 +++++++++--------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/commands/event_coding.ts b/src/commands/event_coding.ts index 919fb5e..270a8a8 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/event_coding.ts @@ -146,9 +146,9 @@ export const useQuizEvent = (): Command => { } const places = { - first: 'Primeiro', - second: 'Segundo', - third: 'Terceiro', + first: 'primeiro', + second: 'segundo', + third: 'terceiro', } client.users @@ -166,31 +166,21 @@ export const useQuizEvent = (): Command => { await member.roles.add(claimedReward.badge) - let winnerMessage = CODING.REWARD_ANNOUNCE - winnerMessage = winnerMessage - .replace('{user}', `<@${interaction.user.id}>`) - .replace('{exp}', `${claimedReward.he4rt_xp} XP`) - let participantMessage = CODING.REWARD_PARTICIPANT participantMessage = participantMessage - .replace('{user}', `<@${interaction.user.id}>`) + .replace('{user}', `${author.username}`) .replace('{exp}', `${claimedReward.he4rt_xp} XP`) const participantEmbed = embedTemplate({title: participantMessage}) const winnerEmbed = embedTemplate({ - title: `${interaction.user.tag} conseguiu concluir todas as respostas com sucesso!`, + title: `${author.username} conseguiu responder todas as perguntas com sucesso!`, description: `Como recompensa do ${places[claimedReward.place]} lugar ganhou **${claimedReward.he4rt_xp}** de experiência!` }) - if(claimedReward.place === 'participant') - await dm.send({ embeds: [(participantEmbed)]}) - if (claimedReward.place !== 'participant') { - await channel?.send({ - content: `👋`, - embeds: [winnerEmbed] - }) - } - + + claimedReward.place === 'participant' + ? await dm.send({ embeds: [(participantEmbed)]}) + : await channel?.send({ embeds: [winnerEmbed] }) }) .catch(() => {}) .finally(async () => {}) diff --git a/src/http/firebase.ts b/src/http/firebase.ts index c0a40a5..ff0c507 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -151,11 +151,14 @@ const updateEventReward = async (client: He4rtClient, reward: FirebaseFirestore. const getReward = async (client: He4rtClient, eventId: string, place?: string) => { const rewardsCollection = client.firestore.collection('rewards') - - const query = place === 'participant' + const query = place !== 'participant' ? await rewardsCollection.where('fk_event', '==', eventId).where('earned', '==', false).limit(1).get() - : await rewardsCollection.where('fk_event', '==', eventId).where('place', '==', 'participant').limit(1).get() - + : await rewardsCollection + .where('fk_event', '==', eventId) + .where('place', '==', 'participant') + .where('earned', '==', true) + .limit(1) + .get() const reward = query.docs[0] return reward @@ -164,11 +167,8 @@ const getReward = async (client: He4rtClient, eventId: string, place?: string) = export const claimEventReward = async (client: He4rtClient, eventId: string, memberId: string) => { const result = await getReward(client, eventId) - const avaliableReward = result.data() as FirestoreReward - - const reward = avaliableReward.place === 'participant' - ? await getReward(client, eventId, 'participant') : result - + const reward = result === undefined ? await getReward(client, eventId, 'participant') : result + await addUserEvent(client, { user: memberId, eventId }) await updateEventReward(client, reward) return reward.data() as FirestoreReward From 995b935273949cfe2c4d81f3cf5d27229b063b68 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sun, 4 Jun 2023 15:39:04 -0300 Subject: [PATCH 09/16] fix: file names --- src/commands/index.ts | 2 +- src/commands/{event_coding.ts => quiz_event.ts} | 2 -- src/events/cron/index.ts | 2 +- src/events/cron/{event_coding.ts => quiz.ts} | 7 +++---- 4 files changed, 5 insertions(+), 8 deletions(-) rename src/commands/{event_coding.ts => quiz_event.ts} (99%) rename src/events/cron/{event_coding.ts => quiz.ts} (56%) diff --git a/src/commands/index.ts b/src/commands/index.ts index 153d842..3119c1c 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -41,7 +41,7 @@ import { useMedalAdd } from './medal/medal_add' import { useWatch } from './watch/watch_set' import { useWatchList } from './watch/watch_get' import { useWatchRemove } from './watch/watch_remove' -import { useQuizEvent } from './event_coding' +import { useQuizEvent } from './quiz_event' const registerHooks = (client: He4rtClient, commands: Command[]) => { commands.forEach(([data, cb]) => { diff --git a/src/commands/event_coding.ts b/src/commands/quiz_event.ts similarity index 99% rename from src/commands/event_coding.ts rename to src/commands/quiz_event.ts index 270a8a8..0878436 100644 --- a/src/commands/event_coding.ts +++ b/src/commands/quiz_event.ts @@ -54,8 +54,6 @@ const nextMultipleRoleSelection = async ( await nextMultipleRoleSelection(roles, text, dm, member, interaction) } - - const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, client: He4rtClient, eventId: string): Promise => { const quizzes = await getEventQuizzesById(client, eventId) diff --git a/src/events/cron/index.ts b/src/events/cron/index.ts index af21e9c..d978214 100644 --- a/src/events/cron/index.ts +++ b/src/events/cron/index.ts @@ -1,6 +1,6 @@ import { He4rtClient } from '@/types' import { verifyApoiaseMembers } from './apoiase' -import { verifyEventCode } from './event_coding' +import { verifyEventCode } from './quiz' export const cronEvents = async (client: He4rtClient) => { await verifyApoiaseMembers(client) diff --git a/src/events/cron/event_coding.ts b/src/events/cron/quiz.ts similarity index 56% rename from src/events/cron/event_coding.ts rename to src/events/cron/quiz.ts index e8eb3a6..62da811 100644 --- a/src/events/cron/event_coding.ts +++ b/src/events/cron/quiz.ts @@ -1,13 +1,12 @@ -import { ApoiaseGET, He4rtClient, UserGET } from '@/types' +import { He4rtClient } from '@/types' import { getGuild, getTargetMember, isApoiaseMember, js } from '@/utils' -import { APOIASE_CUSTOM_COLOR_MINIMAL_VALUE } from '@/defines/values.json' import { EVENT_CODING } from '@/defines/ids.json' import { CronJob } from 'cron' export const verifyEventCode = async (client: He4rtClient) => { const guild = getGuild(client) - await new CronJob('0 */1 * * *', async () => { - console.log('trigger') + await new CronJob('*/10 * * * * *', async () => { + }).start() } From b2ca8f3c00bca411a0d7c4c4b35380eda731eee7 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sun, 4 Jun 2023 22:22:29 -0300 Subject: [PATCH 10/16] fix: validation of the userInput --- src/commands/quiz_event.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index 0878436..510b299 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -85,10 +85,12 @@ const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, c async function retry() { const userInput = await nextTextMessage(dm, interaction) + const regex = new RegExp('\\b' + userInput.toLowerCase() + '\\b'); + if (userInput === '!dica') { dm.send({embeds: [embedTipQuestion]}) await retry() - } else if (userInput !== quiz.answer) { + } else if (regex.test(quiz.answer.toLowerCase())) { dm.send('💥') dm.send({embeds: [embedWrongAnswer]}) await retry() From 07da7971828151997b128d68fcd0268fdc3541eb Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Mon, 5 Jun 2023 06:47:11 -0300 Subject: [PATCH 11/16] fix: name files --- src/commands/quiz_event.ts | 12 +++---- src/defines/ids.json | 4 +-- .../{event_coding.json => quiz_event.json} | 0 src/events/cron/quiz.ts | 11 ++++-- src/events/discord/channel.ts | 34 ++----------------- 5 files changed, 19 insertions(+), 42 deletions(-) rename src/defines/localisation/commands/{event_coding.json => quiz_event.json} (100%) diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index 510b299..781cfeb 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -1,7 +1,7 @@ import { CommandInteraction, DMChannel, GuildMember, SlashCommandBuilder } from 'discord.js' import { Command, He4rtClient } from '@/types' -import { EVENT_CODING } from '@/defines/ids.json' -import CODING from '-/commands/event_coding.json' +import { QUIZ_EVENT } from '@/defines/ids.json' +import QUIZ from '-/commands/quiz_event.json' import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' @@ -34,7 +34,7 @@ const nextMultipleRoleSelection = async ( ) const myEmbed = embedTemplate({ title: '', - description: CODING.CONTINUE, + description: QUIZ.CONTINUE, }) await dm.send({ embeds: [myEmbed] }) @@ -108,7 +108,7 @@ const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, c const validateAccess = async (dm: DMChannel, interaction: CommandInteraction): Promise => { const myEmbed = embedTemplate({ title: 'Evento de programação', - description: CODING.CONTINUE, + description: QUIZ.CONTINUE, }) await sendInDM(dm, interaction, '', myEmbed) @@ -160,13 +160,13 @@ export const useQuizEvent = (): Command => { await nextStringsData(dm, interaction, client, activeEventId) - const channel = getChannel({ id: EVENT_CODING.id, client }) + const channel = getChannel({ id: QUIZ_EVENT.id, client }) const claimedReward = await claimEventReward(client, activeEventId, interaction.user.id) await member.roles.add(claimedReward.badge) - let participantMessage = CODING.REWARD_PARTICIPANT + let participantMessage = QUIZ.REWARD_PARTICIPANT participantMessage = participantMessage .replace('{user}', `${author.username}`) .replace('{exp}', `${claimedReward.he4rt_xp} XP`) diff --git a/src/defines/ids.json b/src/defines/ids.json index f89fda9..af08d14 100644 --- a/src/defines/ids.json +++ b/src/defines/ids.json @@ -80,9 +80,9 @@ "id": "1051348602259251250", "title": "🏆|voluntários" }, - "EVENT_CODING": { + "QUIZ_EVENT": { "id": "1050944705610911774", - "name": "evento-he4rt" + "name": "evento_q&a" }, "VALID_PRESENTATION_DEV_ROLES": [ { diff --git a/src/defines/localisation/commands/event_coding.json b/src/defines/localisation/commands/quiz_event.json similarity index 100% rename from src/defines/localisation/commands/event_coding.json rename to src/defines/localisation/commands/quiz_event.json diff --git a/src/events/cron/quiz.ts b/src/events/cron/quiz.ts index 62da811..296de2b 100644 --- a/src/events/cron/quiz.ts +++ b/src/events/cron/quiz.ts @@ -1,12 +1,17 @@ import { He4rtClient } from '@/types' -import { getGuild, getTargetMember, isApoiaseMember, js } from '@/utils' -import { EVENT_CODING } from '@/defines/ids.json' +import { getGuild } from '@/utils' +import { QUIZ_EVENT } from '@/defines/ids.json' import { CronJob } from 'cron' export const verifyEventCode = async (client: He4rtClient) => { const guild = getGuild(client) await new CronJob('*/10 * * * * *', async () => { - + // TODO + // BUSCAR TODOS OS EVENTOS QUE A DATA FINAL SEJA SUPERIOR AO DIA DE HOJE + // AO TRAZER OS EVENTOS VERIFICAR SE A DATA INICIAL DO EVENTO É SUPERIOR AO DIA DE HOJE + // SE FOR SUPERIOR ALTERAR O STATUS ACTIVE PARA VERDADEIRO + // AO ATIVAR O EVENT ENVIAR MENSAGEM PARA O CANAL DE QUIZ INFORMANDO QUE O EVENTO ESTÁ DISPONÍVEL PARA PARTICIPAR + // QUANDO A DATA DE HOJE FOR SUPERIOR A DATA FINAL ALTERAR O STATUS DO EVENTO PARA INATIVO }).start() } diff --git a/src/events/discord/channel.ts b/src/events/discord/channel.ts index f76181c..018b3d3 100644 --- a/src/events/discord/channel.ts +++ b/src/events/discord/channel.ts @@ -5,13 +5,11 @@ import { MEETING_CHANNEL, MEETING_DELAS_CHANNEL, LEARNING_DIARY_CHANNEL, - ADVERTS_CHANNEL, - EVENT_CODING + ADVERTS_CHANNEL } from '@/defines/ids.json' -import CODING_EVENT from '@/defines/localisation/commands/event_coding.json' import { HE4RT_EMOJI_ID } from '@/defines/ids.json' -import { isAdministrator, isImageHTTPUrl, isValidProxyContent, js, embedTemplate } from '@/utils' -import { ChannelType, GuildMember, Message, MessageType, TextBasedChannel } from 'discord.js' +import { isAdministrator, isImageHTTPUrl, isValidProxyContent, js } from '@/utils' +import { ChannelType, GuildMember, Message, MessageType } from 'discord.js' import { He4rtClient, MessagePOST } from '@/types' export const MessageListener = (client: He4rtClient, message: Message) => { @@ -81,32 +79,6 @@ export const sendGoodMessagesInBusyChannels = (message: Message) => { } } -export const sendStartQuizInBusyChannels = (message: Message) => { - const validChannels = [CHAT_CHANNEL, EVENT_CODING] - - if (validChannels.some((v) => v.id === message.channel.id)) { - const isChatChannel = message.channel.id === CHAT_CHANNEL.id - const content = message.content.toLowerCase().trim() - - const eventCodingChannel = message.channel.client.channels.cache.find((channel) => channel.id === EVENT_CODING.id) - - const myEmbed = embedTemplate({ - title: 'Evento de código', - description: CODING_EVENT.INTRO, - color: '#581c87', - }) - - if (content.length > 50 && isChatChannel) return - - - if (content.match(/(iniciar quiz)/gi)) { - (eventCodingChannel).send({ - embeds: [myEmbed], - }) - } - } -} - export const bussinOrCap = async (message: Message) => { const validChannels = [CHAT_CHANNEL, MEETING_CHANNEL, MEETING_DELAS_CHANNEL] From 5a0607ab6f368a624e17adbf7a11e19da2fb5e37 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Mon, 5 Jun 2023 21:50:30 -0300 Subject: [PATCH 12/16] fix: cron job to make the event avaliable automatic --- src/commands/quiz_event.ts | 9 ++++--- src/defines/ids_development.json | 4 +-- src/events/cron/index.ts | 4 +-- src/events/cron/quiz.ts | 42 +++++++++++++++++++++++--------- src/http/firebase.ts | 21 +++++++++++++--- src/types.ts | 5 ++-- 6 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index 781cfeb..255e1ea 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -163,7 +163,7 @@ export const useQuizEvent = (): Command => { const channel = getChannel({ id: QUIZ_EVENT.id, client }) const claimedReward = await claimEventReward(client, activeEventId, interaction.user.id) - + await member.roles.add(claimedReward.badge) let participantMessage = QUIZ.REWARD_PARTICIPANT @@ -178,9 +178,10 @@ export const useQuizEvent = (): Command => { description: `Como recompensa do ${places[claimedReward.place]} lugar ganhou **${claimedReward.he4rt_xp}** de experiência!` }) - claimedReward.place === 'participant' - ? await dm.send({ embeds: [(participantEmbed)]}) - : await channel?.send({ embeds: [winnerEmbed] }) + claimedReward.place !== 'participant' + ? channel.send({ embeds: [winnerEmbed] }) + : dm.send({ embeds: [(participantEmbed)]}) + }) .catch(() => {}) .finally(async () => {}) diff --git a/src/defines/ids_development.json b/src/defines/ids_development.json index dfc0341..185fd7e 100644 --- a/src/defines/ids_development.json +++ b/src/defines/ids_development.json @@ -80,9 +80,9 @@ "id": "1042817315965763600", "title": "🏆|voluntários" }, - "EVENT_CODING": { + "QUIZ_EVENT": { "id": "1050944705610911774", - "name": "evento-he4rt" + "name": "evento_q&" }, "VALID_PRESENTATION_DEV_ROLES": [ { diff --git a/src/events/cron/index.ts b/src/events/cron/index.ts index d978214..6342562 100644 --- a/src/events/cron/index.ts +++ b/src/events/cron/index.ts @@ -1,8 +1,8 @@ import { He4rtClient } from '@/types' import { verifyApoiaseMembers } from './apoiase' -import { verifyEventCode } from './quiz' +import { everyQuizEvent } from './quiz' export const cronEvents = async (client: He4rtClient) => { await verifyApoiaseMembers(client) - await verifyEventCode(client) + await everyQuizEvent(client) } diff --git a/src/events/cron/quiz.ts b/src/events/cron/quiz.ts index 296de2b..988279f 100644 --- a/src/events/cron/quiz.ts +++ b/src/events/cron/quiz.ts @@ -1,17 +1,37 @@ +import { getEvents, updateEventStatus } from '@/http/firebase' import { He4rtClient } from '@/types' -import { getGuild } from '@/utils' -import { QUIZ_EVENT } from '@/defines/ids.json' +import { embedTemplate, getChannel } from '@/utils' import { CronJob } from 'cron' +import { QUIZ_EVENT } from '@/defines/ids.json' + +export const everyQuizEvent = async (client: He4rtClient) => { + + await new CronJob('0 * * * *', async () => { + const events = await getEvents(client) + const today = new Date() + + const currentEvent = events.reduce((result, event) => { + if (today >= event.date_start.toDate() && today <= event.date_end.toDate()) { + return event + } + return result + }, null) + -export const verifyEventCode = async (client: He4rtClient) => { - const guild = getGuild(client) + if(!currentEvent.is_active) { + updateEventStatus(client, currentEvent) + const chat = getChannel({ id: QUIZ_EVENT.id, client }) + const embed = embedTemplate({ + title: 'Novo evento de Q&A disponível 👋', + description: `@everyone ${currentEvent.description}` + }) + await chat.send({ embeds: [embed] }) + } - await new CronJob('*/10 * * * * *', async () => { - // TODO - // BUSCAR TODOS OS EVENTOS QUE A DATA FINAL SEJA SUPERIOR AO DIA DE HOJE - // AO TRAZER OS EVENTOS VERIFICAR SE A DATA INICIAL DO EVENTO É SUPERIOR AO DIA DE HOJE - // SE FOR SUPERIOR ALTERAR O STATUS ACTIVE PARA VERDADEIRO - // AO ATIVAR O EVENT ENVIAR MENSAGEM PARA O CANAL DE QUIZ INFORMANDO QUE O EVENTO ESTÁ DISPONÍVEL PARA PARTICIPAR - // QUANDO A DATA DE HOJE FOR SUPERIOR A DATA FINAL ALTERAR O STATUS DO EVENTO PARA INATIVO + events.forEach((event) => { + if(event.is_active && event.date_end.toDate() < today) { + updateEventStatus(client, event) + } + }) }).start() } diff --git a/src/http/firebase.ts b/src/http/firebase.ts index ff0c507..60afd3f 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -194,9 +194,7 @@ export const getActiveEvent = async (client: He4rtClient) => { .limit(1) .get() - if(!query.empty) return query.docs[0].id - - return ''; + return !query.empty ? query.docs[0].id : '' } export const getEventQuizzesById = async (client: He4rtClient, eventId) => { @@ -208,4 +206,21 @@ export const getEventQuizzesById = async (client: He4rtClient, eventId) => { const questions = query.docs.map((doc) => doc.data()); return questions as FirestoreQuiz[] +} + +export const getEvents = async (client: He4rtClient) => { + const eventsCollection = client.firestore.collection('events') + + const query = await eventsCollection + .get() + + const events = query.docs.map((doc) => doc.data()); + return events as FirestoreEvent[] +} + +export const updateEventStatus = async (client: He4rtClient, event: FirestoreEvent) => { + const collection = client.firestore.collection('events') + const entity = defu({is_active: !event.is_active}, event) + + collection.doc(event.id).set(entity) } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 307b399..f1a4e8d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ import type { ClientBuilder } from 'uncreate' import admin from 'firebase-admin' import { Logger } from './client/logger' import { Ticker } from './client/ticker' +import { Timestamp } from '@google-cloud/firestore' export type Maybe = T | undefined | null export type RESTJson = Record @@ -235,8 +236,8 @@ export interface FirestoreReward { } export interface FirestoreEvent { - date_start: Date - date_end: Date + date_start: Timestamp + date_end: Timestamp description: string is_active: boolean id: string From ac263dd3e7dca1d1595caf7e2c93b01fa05f5d87 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Sun, 2 Jul 2023 20:57:02 -0300 Subject: [PATCH 13/16] fix: coding review changes --- src/commands/quiz_event.ts | 10 +++++----- src/defines/values.json | 3 ++- src/defines/values_development.json | 3 ++- src/http/firebase.ts | 6 +++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index 255e1ea..6c35229 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -1,9 +1,9 @@ -import { CommandInteraction, DMChannel, GuildMember, SlashCommandBuilder } from 'discord.js' +import { CommandInteraction, DMChannel, GuildMember, HexColorString, SlashCommandBuilder } from 'discord.js' import { Command, He4rtClient } from '@/types' import { QUIZ_EVENT } from '@/defines/ids.json' import QUIZ from '-/commands/quiz_event.json' import { START_CODE_CHALLENGE } from '@/defines/commands.json' -import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING } from '@/defines/values.json' +import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING, COLORS } from '@/defines/values.json' import { getChannel, isValidId, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' import { checkUserEventEntry, claimEventReward, getActiveEvent, getEventQuizzesById } from '@/http/firebase' @@ -65,18 +65,18 @@ const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, c const embedWrongAnswer = embedTemplate({ title: 'Excelente tentativa, mas a resposta não está correta.', description: 'Essa foi a resposta errada.', - color: '#FF0000' + color: COLORS.ERROR as HexColorString }) const embedRightAnswer = embedTemplate({ title: 'Ótima resposta, aqui vai a próxima!', description: `Respostas: **${quiz.answer}**`, - color: '#00FF00' + color: COLORS.SUCCESS as HexColorString }) const embedTipQuestion = embedTemplate({ title: quiz.tip, - color: '#00FFFF' + color: COLORS.TIP_ANSWER as HexColorString }) const embedFinishedEvent = embedTemplate({ diff --git a/src/defines/values.json b/src/defines/values.json index ca18760..14109dd 100644 --- a/src/defines/values.json +++ b/src/defines/values.json @@ -13,7 +13,8 @@ "SUCCESS": "#46E363", "ERROR": "#C90B04", "WARNING": "#FFD421", - "INFO": "#7291FA" + "INFO": "#7291FA", + "TIP_ANSWER": "#00FFFF" }, "RESTRICTED_CUSTOM_ROLE_COLORS": [ "#313335", diff --git a/src/defines/values_development.json b/src/defines/values_development.json index e9b7b8d..45ba6cd 100644 --- a/src/defines/values_development.json +++ b/src/defines/values_development.json @@ -13,7 +13,8 @@ "SUCCESS": "#46E363", "ERROR": "#C90B04", "WARNING": "#FFD421", - "INFO": "#7291FA" + "INFO": "#7291FA", + "TIP_ANSWER": "#00FFFF" }, "RESTRICTED_CUSTOM_ROLE_COLORS": [ "#313335", diff --git a/src/http/firebase.ts b/src/http/firebase.ts index 60afd3f..06f9c9d 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -142,11 +142,11 @@ const addUserEvent = async (client: He4rtClient, {user, eventId}): Promise await collection.add({ id: user, event: eventId}) } -const updateEventReward = async (client: He4rtClient, reward: FirebaseFirestore.QueryDocumentSnapshot): Promise => { +const updateEventReward = async (client: He4rtClient, reward: FirebaseFirestore.QueryDocumentSnapshot) => { const collection = client.firestore.collection('rewards') const entity = defu({earned: true}, reward.data() as FirestoreReward) - collection.doc(reward.id).set(entity) + return collection.doc(reward.id).set(entity) } const getReward = async (client: He4rtClient, eventId: string, place?: string) => { @@ -222,5 +222,5 @@ export const updateEventStatus = async (client: He4rtClient, event: FirestoreEve const collection = client.firestore.collection('events') const entity = defu({is_active: !event.is_active}, event) - collection.doc(event.id).set(entity) + return collection.doc(event.id).set(entity) } \ No newline at end of file From fc4805b49c7b92d5cb2e9f27a4acac88cb0b9369 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Wed, 29 Nov 2023 08:09:05 -0300 Subject: [PATCH 14/16] fix: cron job --- src/commands/quiz_event.ts | 2 -- src/events/cron/index.ts | 4 ++-- src/events/cron/quiz.ts | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index 6c35229..af3de79 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -161,9 +161,7 @@ export const useQuizEvent = (): Command => { await nextStringsData(dm, interaction, client, activeEventId) const channel = getChannel({ id: QUIZ_EVENT.id, client }) - const claimedReward = await claimEventReward(client, activeEventId, interaction.user.id) - await member.roles.add(claimedReward.badge) let participantMessage = QUIZ.REWARD_PARTICIPANT diff --git a/src/events/cron/index.ts b/src/events/cron/index.ts index 6342562..6039c8f 100644 --- a/src/events/cron/index.ts +++ b/src/events/cron/index.ts @@ -1,8 +1,8 @@ import { He4rtClient } from '@/types' import { verifyApoiaseMembers } from './apoiase' -import { everyQuizEvent } from './quiz' +import { verifyQuizEvent } from './quiz' export const cronEvents = async (client: He4rtClient) => { await verifyApoiaseMembers(client) - await everyQuizEvent(client) + await verifyQuizEvent(client) } diff --git a/src/events/cron/quiz.ts b/src/events/cron/quiz.ts index 988279f..758aebc 100644 --- a/src/events/cron/quiz.ts +++ b/src/events/cron/quiz.ts @@ -4,9 +4,9 @@ import { embedTemplate, getChannel } from '@/utils' import { CronJob } from 'cron' import { QUIZ_EVENT } from '@/defines/ids.json' -export const everyQuizEvent = async (client: He4rtClient) => { +export const verifyQuizEvent = async (client: He4rtClient) => { - await new CronJob('0 * * * *', async () => { + await new CronJob('0 2 * * *', async () => { const events = await getEvents(client) const today = new Date() From 69a4b7501a695d8be7e8ed3fddcc4639c0b1743d Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Wed, 29 Nov 2023 08:15:30 -0300 Subject: [PATCH 15/16] fix: channel name --- src/defines/ids_development.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defines/ids_development.json b/src/defines/ids_development.json index 185fd7e..7f80a0b 100644 --- a/src/defines/ids_development.json +++ b/src/defines/ids_development.json @@ -82,7 +82,7 @@ }, "QUIZ_EVENT": { "id": "1050944705610911774", - "name": "evento_q&" + "name": "evento_q&a" }, "VALID_PRESENTATION_DEV_ROLES": [ { From dacfd78d30be110896a07a210c5067b5cdac68a3 Mon Sep 17 00:00:00 2001 From: fernanduandrade Date: Wed, 29 Nov 2023 14:54:37 -0300 Subject: [PATCH 16/16] fix: code quality --- src/commands/index.ts | 9 ++-- src/commands/quiz_event.ts | 83 +++++++++++++---------------- src/defines/values.json | 2 +- src/defines/values_development.json | 2 +- src/events/cron/quiz.ts | 4 +- src/events/discord/channel.ts | 1 - src/http/firebase.ts | 2 +- src/types.ts | 2 +- 8 files changed, 46 insertions(+), 59 deletions(-) diff --git a/src/commands/index.ts b/src/commands/index.ts index 3119c1c..6f18a6b 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -92,7 +92,6 @@ export const registerCommands = async ({ client, rest }: Context) => { useWatchList(), useWatchRemove(), useQuizEvent() - // useReputation() ]) await rest.put(Routes.applicationGuildCommands(process.env.DISCORD_CLIENT_ID, process.env.DISCORD_GUILD_ID), { @@ -101,12 +100,12 @@ export const registerCommands = async ({ client, rest }: Context) => { } export const commandsListener = (client: He4rtClient, interaction: CommandInteraction) => { - for (const [key, cb] of client.commands) { - if (key.name === interaction.commandName) { - cb && cb(interaction, client) + for (const [commandSet, commandCallBack] of client.commands) { + if (commandSet.name === interaction.commandName) { + commandCallBack && commandCallBack(interaction, client) client.logger.emit({ - message: `${getTargetMember(interaction.member as GuildMember)} acionou **/${key.name}** no canal **${ + message: `${getTargetMember(interaction.member as GuildMember)} acionou **/${commandSet.name}** no canal **${ interaction.channel.name }**`, type: 'command', diff --git a/src/commands/quiz_event.ts b/src/commands/quiz_event.ts index af3de79..57c9945 100644 --- a/src/commands/quiz_event.ts +++ b/src/commands/quiz_event.ts @@ -4,9 +4,12 @@ import { QUIZ_EVENT } from '@/defines/ids.json' import QUIZ from '-/commands/quiz_event.json' import { START_CODE_CHALLENGE } from '@/defines/commands.json' import { TIMEOUT_ANSWER, TIMEOUT_COMMAND_STRING, COLORS } from '@/defines/values.json' -import { getChannel, isValidId, reply, sendInDM } from '@/utils' +import { getChannel, reply, sendInDM } from '@/utils' import { embedTemplate } from '@/utils' import { checkUserEventEntry, claimEventReward, getActiveEvent, getEventQuizzesById } from '@/http/firebase' + +const HINT_COMMMAND = '!dica' + const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): Promise => { try { const result = await dm.awaitMessages({ @@ -16,7 +19,7 @@ const nextTextMessage = async (dm: DMChannel, interaction: CommandInteraction): }) return result.first()!.content - } catch (e) { + } catch { return TIMEOUT_COMMAND_STRING } } @@ -38,19 +41,6 @@ const nextMultipleRoleSelection = async ( }) await dm.send({ embeds: [myEmbed] }) - - const value = Number(await nextTextMessage(dm, interaction)) - - if (isValidId(value, roles)) { - member.roles.add(roles[value - 1].id) - - await nextMultipleRoleSelection(roles, text, dm, member, interaction) - - return - } - - if (value === 0) return - await nextMultipleRoleSelection(roles, text, dm, member, interaction) } @@ -62,46 +52,39 @@ const nextStringsData = async (dm: DMChannel, interaction: CommandInteraction, c const embed = embedTemplate({ title: quiz.title, description: quiz.question }) await dm.send({ embeds: [embed] }) - const embedWrongAnswer = embedTemplate({ - title: 'Excelente tentativa, mas a resposta não está correta.', - description: 'Essa foi a resposta errada.', - color: COLORS.ERROR as HexColorString - }) + const wrongResponse = createEmbedResponse(COLORS.ERROR as HexColorString, 'Excelente tentativa, mas a resposta não está correta.', 'Essa foi a resposta errada.') + const rightResponse = createEmbedResponse(COLORS.SUCCESS as HexColorString, 'Ótima resposta, aqui vai a próxima!', `Respostas: **${quiz.answer}**`) + const hintResponse = createEmbedResponse(COLORS.HINT_ANSWER as HexColorString, quiz.tip) + const finishEventResponse = createEmbedResponse(null, 'Parabéns!! você conseguiu concluir o evento🎉🎉🎉') - const embedRightAnswer = embedTemplate({ - title: 'Ótima resposta, aqui vai a próxima!', - description: `Respostas: **${quiz.answer}**`, - color: COLORS.SUCCESS as HexColorString - }) - - const embedTipQuestion = embedTemplate({ - title: quiz.tip, - color: COLORS.TIP_ANSWER as HexColorString - }) - - const embedFinishedEvent = embedTemplate({ - title: 'Parabéns!! você conseguiu concluir o evento🎉🎉🎉', - }) - async function retry() { + async function handleUserInput() { const userInput = await nextTextMessage(dm, interaction) const regex = new RegExp('\\b' + userInput.toLowerCase() + '\\b'); - if (userInput === '!dica') { - dm.send({embeds: [embedTipQuestion]}) - await retry() - } else if (regex.test(quiz.answer.toLowerCase())) { - dm.send('💥') - dm.send({embeds: [embedWrongAnswer]}) - await retry() - } else if (!quiz.has_next_question) { - dm.send({ embeds: [embedFinishedEvent]}) - } else { - dm.send({ embeds: [embedRightAnswer]}) + const inputResult = userInput === HINT_COMMMAND ? 'hint' + : !regex.test(quiz.answer.toLowerCase()) ? 'retry' + : quiz.has_next_question ? 'continue' : 'finished' + + const actions = { + hint: () => dm.send({embeds: [hintResponse]}), + retry: () => { + dm.send('💥') + dm.send({embeds: [wrongResponse]}) + }, + continue: () => dm.send({ embeds: [rightResponse]}), + finished: () => dm.send({ embeds: [finishEventResponse]}) } + + const result = actions[inputResult] || actions.finished + result() + + if(inputResult === 'hint' || inputResult === 'retry') + await handleUserInput() + } - await retry() + await handleUserInput() } } @@ -186,3 +169,9 @@ export const useQuizEvent = (): Command => { }, ] } + +const createEmbedResponse = (color: HexColorString, title: string, description?: string) => embedTemplate({ + title, + description, + color +}) diff --git a/src/defines/values.json b/src/defines/values.json index 14109dd..ffcf9da 100644 --- a/src/defines/values.json +++ b/src/defines/values.json @@ -14,7 +14,7 @@ "ERROR": "#C90B04", "WARNING": "#FFD421", "INFO": "#7291FA", - "TIP_ANSWER": "#00FFFF" + "HINT_ANSWER": "#00FFFF" }, "RESTRICTED_CUSTOM_ROLE_COLORS": [ "#313335", diff --git a/src/defines/values_development.json b/src/defines/values_development.json index 45ba6cd..e914b13 100644 --- a/src/defines/values_development.json +++ b/src/defines/values_development.json @@ -14,7 +14,7 @@ "ERROR": "#C90B04", "WARNING": "#FFD421", "INFO": "#7291FA", - "TIP_ANSWER": "#00FFFF" + "HINT_ANSWER": "#00FFFF" }, "RESTRICTED_CUSTOM_ROLE_COLORS": [ "#313335", diff --git a/src/events/cron/quiz.ts b/src/events/cron/quiz.ts index 758aebc..5f04181 100644 --- a/src/events/cron/quiz.ts +++ b/src/events/cron/quiz.ts @@ -18,7 +18,7 @@ export const verifyQuizEvent = async (client: He4rtClient) => { }, null) - if(!currentEvent.is_active) { + if (!currentEvent.is_active) { updateEventStatus(client, currentEvent) const chat = getChannel({ id: QUIZ_EVENT.id, client }) const embed = embedTemplate({ @@ -29,7 +29,7 @@ export const verifyQuizEvent = async (client: He4rtClient) => { } events.forEach((event) => { - if(event.is_active && event.date_end.toDate() < today) { + if (event.is_active && event.date_end.toDate() < today) { updateEventStatus(client, event) } }) diff --git a/src/events/discord/channel.ts b/src/events/discord/channel.ts index 018b3d3..fb07c14 100644 --- a/src/events/discord/channel.ts +++ b/src/events/discord/channel.ts @@ -22,7 +22,6 @@ export const MessageListener = (client: He4rtClient, message: Message) => { client.api.he4rt .messages() .discord.post({ - // provider_message_parent_id: message.parentId provider_id: member.id, provider_message_id: message.id, channel_id: message.channelId, diff --git a/src/http/firebase.ts b/src/http/firebase.ts index 06f9c9d..6a77d97 100644 --- a/src/http/firebase.ts +++ b/src/http/firebase.ts @@ -223,4 +223,4 @@ export const updateEventStatus = async (client: He4rtClient, event: FirestoreEve const entity = defu({is_active: !event.is_active}, event) return collection.doc(event.id).set(entity) -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index f1a4e8d..f4ac36c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -250,4 +250,4 @@ export interface FirestoreQuiz { fk_event: number title: string, question: string -} \ No newline at end of file +}