Skip to content

Commit

Permalink
Merge pull request #40 from Thomasparsley/main
Browse files Browse the repository at this point in the history
Reactions
  • Loading branch information
slunimara authored Sep 17, 2023
2 parents 78dc3c9 + 74bb302 commit fa3e738
Show file tree
Hide file tree
Showing 28 changed files with 425 additions and 312 deletions.
2 changes: 1 addition & 1 deletion bot/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ COPY --from=build /app/bot/dist /app/bot
COPY --from=build /app/bot/package.json /app/bot
COPY ./templates /app/templates

RUN npm install --production
RUN npm install --omit=dev

CMD [ "node", "index.js" ]
98 changes: 92 additions & 6 deletions bot/src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
Client,
Role,
Partials,
Snowflake,
Emoji,
GuildTextBasedChannel,
} from "discord.js"

import {
Expand All @@ -31,6 +34,9 @@ import {
OnReactionAddAction,
OnReactionRemoveAction,
} from "./types"
import {
MessageReaction
} from "./models"

const REST_VERSION = "10"

Expand All @@ -41,7 +47,7 @@ export class Bot {
buttonCommands = new Map<string, ICommand<ButtonCommand>>()
modalCommands = new Map<string, ICommand<ModalCommand>>()
dropdownCommands = new Map<string, IDropdownCommand<DropdownCommand>>()
reactionMessages = new Map<Message, Map<String, Role>>()
reactionMessages = new Map<Snowflake, Map<string, string>>()
private async onReady(args: OnReadyArgs): Promise<void> {
throw new Error("Event 'onReady' is not implementet")
}
Expand Down Expand Up @@ -87,9 +93,9 @@ export class Bot {
if (config.modalCommands) this.initModalCommands(config.modalCommands)
if (config.dropdownCommands) this.initDropdownCommands(config.dropdownCommands)

/* if (config.reactionMessages) {
this.initReactionMessages(config.reactionMessages);
} */
this.loadReactionMessages().then(value => {
this.reactionMessages = value
})

this.initEvents(config)
this.init()
Expand Down Expand Up @@ -137,6 +143,7 @@ export class Bot {
modals: this.modalCommands,
dropdown: this.dropdownCommands,
db: this.db,
bot: this,
mailer: this.mailer,
commandRegistration: this.registerChatInputGuildCommands,
}
Expand Down Expand Up @@ -187,10 +194,9 @@ export class Bot {
private initOnReactionAdd() {
this.client.on("messageReactionAdd", async (messageReaction, user) => {
const args: OnReactionAddArgs = {
client: this.client,
reaction: messageReaction,
user: user,
db: this.db,
reactionMessages: this.reactionMessages,
}

try {
Expand Down Expand Up @@ -248,6 +254,86 @@ export class Bot {
})
}

/**
* Loads reaction messages from the database
* @returns Promise that resolves to reaction messages
*/
private async loadReactionMessages(): Promise<Map<Snowflake, Map<string, string>>> {
const ReactionMessages = new Map<Snowflake, Map<string, string>>();

const allReactions = await MessageReaction.find({
select: {
messageId: true,
channelId: true
}
});

// Gets mask of boolean values
const uniqueMask = allReactions
.map(item => item.messageId)
.map((value, index, array) => array.indexOf(value) === index);

// Gets all unique MessageReactions
const uniqueMessage = allReactions.filter((_, index) => uniqueMask[index]);

uniqueMessage.forEach(async ({messageId, channelId}) => {
const messageReactionsBinds = await MessageReaction.find({
select: {
emoji: true,
role: true,
},
where: {
messageId: messageId
}
});

const guild = await this.client.guilds.fetch(this.guildId)
const channel = (await guild.channels.fetch(channelId)!) as GuildTextBasedChannel
const message = await channel.messages.fetch(messageId)

if(message === null){
throw new Error("Invalid message")
}

// Clean old reactions before adding new ones
await message.reactions.removeAll()

// Adding new reactions based on binds specified in database
const reactionBinds = new Map()
messageReactionsBinds.forEach(async row => {
reactionBinds.set(row.emoji, row.role)
await message.react(row.emoji)
});

ReactionMessages.set(messageId, reactionBinds)
});

return ReactionMessages
}

/**
* Adds a new reaction message to the bot config
* @param messageId Reaction message id
* @param reactions Binds that should be added for the message
*/
public async addReactionMessage(messageId: string, channelId: string, reactions: Map<string, string>) {
// Updating new reactions binds
this.reactionMessages.set(messageId, reactions)

// Removing reactions binds
await MessageReaction.delete({ messageId: messageId })

// Adding new reaction binds
reactions.forEach(async (role, emoji) => {
const messageReaction = new MessageReaction()
messageReaction.messageId = messageId
messageReaction.emoji = emoji
messageReaction.role = role
messageReaction.channelId = channelId
await messageReaction.save();
});
}

public async login() {
if (this.isLogedIn)
return
Expand Down
11 changes: 8 additions & 3 deletions bot/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
StringSelectMenuInteraction,
RESTPostAPIApplicationCommandsJSONBody, // TODO: Update to latest discord.js
} from "discord.js"

import {
Bot
} from "./bot"
import {
InvalidChannel,
InvalidGuild,
Expand Down Expand Up @@ -52,10 +54,12 @@ class Command {
description!: string
client!: Client
mailer!: Mailer
bot!: Bot

async execute(client: Client, mailer: Mailer): Promise<void> {
async execute(client: Client, mailer: Mailer, bot: Bot): Promise<void> {
this.client = client
this.mailer = mailer
this.bot = bot

await this.executable()
}
Expand All @@ -69,10 +73,11 @@ export class InteractionCommand<T extends Interaction> extends Command {
interaction!: T

// @ts-ignore
async execute(client: Client, mailer: Mailer, interaction: T): Promise<void> {
async execute(client: Client, mailer: Mailer, bot: Bot, interaction: T): Promise<void> {
this.client = client
this.mailer = mailer
this.interaction = interaction
this.bot = bot

try {
await this.executable()
Expand Down
39 changes: 30 additions & 9 deletions bot/src/commands/messageManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios"

import { ActionRowBuilder, SelectMenuBuilder, SlashCommandBuilder } from "@discordjs/builders"
import { ButtonBuilder, TextBasedChannel } from "discord.js"
import { ButtonBuilder, TextBasedChannel} from "discord.js"


import { TextFile, TextFileMessage } from "../interfaces"
Expand All @@ -22,7 +22,6 @@ import {
InvalidURLError,
} from "../errors"


const maxMessageLength = 2000
const channelTagName = "channel"
const mentionTagName = "mention"
Expand Down Expand Up @@ -213,14 +212,15 @@ export class MessageManagerCommand extends ChatInputCommand {
throw new InvalidTextBasedChannel()

for (const rawMessage of data.messages)
await this.processOneMessage(rawMessage, channel,)
await this.processOneMessage(rawMessage, channel)
}

async processOneMessage(rawMessage: TextFileMessage, channel: TextBasedChannel): Promise<void> {
const messageId = rawMessage.id
const message = await channel.messages.fetch(messageId)

if (!message)
throw "".toError()
throw `Zpráva s id ${messageId} nebyla nalezena v kanále s id ${channel.id}`.toError()
if (message.author !== this.client.user)
throw new BotCanEditOnlySelfMessagesError()

Expand All @@ -244,20 +244,41 @@ export class MessageManagerCommand extends ChatInputCommand {
rows.push(row)
}

// Reactions
const reactionMap = new Map<string, string>()
if (rawMessage.reactions) {

for (const [key, value] of Object.entries(rawMessage.reactions)) {
reactionMap.set(value, key)
}

await this.bot.addReactionMessage(rawMessage.id, channel.id, reactionMap)

// Clean old reactions before adding new ones
await message.reactions.removeAll()

reactionMap.forEach(async (role, emoji) => {
await message.react(emoji)
});
}

const unparsedContent = rawMessage.content.join("\n")
const content = this.handleMentions(unparsedContent)
let content = this.handleMentions(unparsedContent)

if (rows.length > 0) {
// Adds list of reaction and assigned roles
reactionMap.forEach((role, emoji) => {
content += `\n- ${emoji} - ${role}`
})

if (rows.length > 0) {
await message.edit({
content: content,
// @ts-ignore
components: rows
})
return
} else {
await message.edit({ content: content })
}

await message.edit({ content: content })
}

createButtonComponent(raw: { id: string; label: string; style: string; }) {
Expand Down
6 changes: 0 additions & 6 deletions bot/src/commands/modals/verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import { ModalCommand } from "../../command";
import { Validation } from "../../models";

import {
isValidateEmail,
isUpolEmail,
makeRegisterText,
makeRegisterHTML
} from "../../utils";
import {
InvalidEmailFormatError,
UnknownUpolEmailError,
} from "../../errors";
import { VerificationCodeButton } from "../../buttons/verificationCode";
Expand All @@ -25,10 +23,6 @@ export class VerificationModalCommand extends ModalCommand {

email = email.trim()

// validace emailu
if (!isValidateEmail(email))
throw new InvalidEmailFormatError(email as string)

// validace domény emailu
if (!isUpolEmail(email))
throw new UnknownUpolEmailError(email)
Expand Down
1 change: 1 addition & 0 deletions bot/src/commands/requestEveryone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class EveryoneRequestCommand extends ChatInputCommand {
.setName(cd.options[0].name)
.setDescription(cd.options[0].description)
.setRequired(true)
.setMaxLength(256)
})

async executable(): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions bot/src/commands/requestQuote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class QuoteRequestChatCommnad extends ChatInputCommand {
.setName(cd.options[0].name)
.setDescription(cd.options[0].description)
.setRequired(true)
.setMaxLength(256)
})

async executable(): Promise<void> {
Expand Down
4 changes: 3 additions & 1 deletion bot/src/databaseSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { DataSource } from "typeorm"
import {
Validation,
Error,
User
User,
MessageReaction,
} from "./models"

const {
Expand All @@ -27,6 +28,7 @@ export const DatabaseSource = new DataSource({
Validation,
Error,
User,
MessageReaction,
],
synchronize: true,
logging: false,
Expand Down
8 changes: 1 addition & 7 deletions bot/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,9 @@ export class InvalidGuild extends Error {
}
}

export class InvalidEmailFormatError extends Error {
constructor(email: string) {
super(`Email není ve správném tvaru ${email}.`)
}
}

export class UnknownUpolEmailError extends Error {
constructor(email: string) {
super(`${email} napatří do domény Univerzity Palackého. Registrace je jen pro emaily typu \`uživatel@upol.cz\`.`)
super(`\`${email}\` napatří do domény Univerzity Palackého. Registrace je jen pro emaily typu \`jmeno.prijmeniXX@upol.cz\`.`)
}
}

Expand Down
4 changes: 1 addition & 3 deletions bot/src/events/onGuildMemberAdd.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { OnGuildMemberAddAction } from "../types"

export const onGuildMemberAdd: OnGuildMemberAddAction = async ({ member }) => {

}
export const onGuildMemberAdd: OnGuildMemberAddAction = async ({ member }) => {}
4 changes: 2 additions & 2 deletions bot/src/events/onInteractionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { UnknownCommandError } from "../errors"
import { InteractionCommand } from "../command"

export async function onInteractionCreate(args: OnInteractionCreateArgs) {
const { client, interaction, mailer, commands, buttons, modals, dropdown } = args
const { client, interaction, mailer, bot, commands, buttons, modals, dropdown } = args
let command: InteractionCommand<Interaction<CacheType>> | undefined

if (interaction.isButton()) {
Expand Down Expand Up @@ -40,5 +40,5 @@ export async function onInteractionCreate(args: OnInteractionCreateArgs) {
throw new UnknownCommandError()
}

await command.execute(client, mailer, interaction)
await command.execute(client, mailer, bot, interaction)
}
Loading

0 comments on commit fa3e738

Please sign in to comment.