From 2a0c89b44f7808264e2487a88a2e8a3456936fd1 Mon Sep 17 00:00:00 2001 From: petrspirka <91654267+petrspirka@users.noreply.github.com> Date: Fri, 8 Sep 2023 06:17:15 +0200 Subject: [PATCH 1/5] Limited description size for everyone and quote requests (#37) --- bot/src/commands/requestEveryone.ts | 1 + bot/src/commands/requestQuote.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/bot/src/commands/requestEveryone.ts b/bot/src/commands/requestEveryone.ts index bb18ace..13a4528 100644 --- a/bot/src/commands/requestEveryone.ts +++ b/bot/src/commands/requestEveryone.ts @@ -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 { diff --git a/bot/src/commands/requestQuote.ts b/bot/src/commands/requestQuote.ts index b401964..2de6f2a 100644 --- a/bot/src/commands/requestQuote.ts +++ b/bot/src/commands/requestQuote.ts @@ -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 { From 0ccac202cfa812604cc138c9a28875933279ff3a Mon Sep 17 00:00:00 2001 From: slunimara Date: Thu, 14 Sep 2023 19:13:59 +0200 Subject: [PATCH 2/5] Init --- bot/src/bot.ts | 3 ++- bot/src/events/onReactionAdd.ts | 1 + bot/src/events/onReactionRemove.ts | 1 + bot/src/interfaces/botConfig.ts | 3 +++ bot/src/interfaces/textFile.ts | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bot/src/bot.ts b/bot/src/bot.ts index 2b1680a..6e98329 100644 --- a/bot/src/bot.ts +++ b/bot/src/bot.ts @@ -41,7 +41,7 @@ export class Bot { buttonCommands = new Map>() modalCommands = new Map>() dropdownCommands = new Map>() - reactionMessages = new Map>() + reactionMessages = new Map>() // TODO private async onReady(args: OnReadyArgs): Promise { throw new Error("Event 'onReady' is not implementet") } @@ -87,6 +87,7 @@ export class Bot { if (config.modalCommands) this.initModalCommands(config.modalCommands) if (config.dropdownCommands) this.initDropdownCommands(config.dropdownCommands) + // TODO /* if (config.reactionMessages) { this.initReactionMessages(config.reactionMessages); } */ diff --git a/bot/src/events/onReactionAdd.ts b/bot/src/events/onReactionAdd.ts index 8d48dba..d72a4a5 100644 --- a/bot/src/events/onReactionAdd.ts +++ b/bot/src/events/onReactionAdd.ts @@ -2,4 +2,5 @@ import { OnReactionAddAction } from "../types" export const onReactionAdd: OnReactionAddAction = async ({ reaction, user }) => { console.log(`${user.username} použil reakci ${reaction.emoji}!`) + // TODO } diff --git a/bot/src/events/onReactionRemove.ts b/bot/src/events/onReactionRemove.ts index f31c589..892fa3d 100644 --- a/bot/src/events/onReactionRemove.ts +++ b/bot/src/events/onReactionRemove.ts @@ -2,4 +2,5 @@ import { OnReactionRemoveAction } from "../types" export const onReactionRemove: OnReactionRemoveAction = async ({ reaction, user }) => { console.log(`${user.username} smazal reakci ${reaction.emoji}!`) + // TODO } diff --git a/bot/src/interfaces/botConfig.ts b/bot/src/interfaces/botConfig.ts index 03e6106..9133d99 100644 --- a/bot/src/interfaces/botConfig.ts +++ b/bot/src/interfaces/botConfig.ts @@ -24,4 +24,7 @@ export interface BotConfig { onReactionRemove?: OnReactionRemoveAction onInteractionCreate?: OnInteractionCreateAction onGuildMemberAdd?: OnGuildMemberAddAction + // TODO } + + diff --git a/bot/src/interfaces/textFile.ts b/bot/src/interfaces/textFile.ts index 7ee5e00..e8efff7 100644 --- a/bot/src/interfaces/textFile.ts +++ b/bot/src/interfaces/textFile.ts @@ -7,6 +7,7 @@ export interface TextFile { export interface TextFileMessage { id: string content: string[] + // TODO reactions components?: { dropdowns?: { id: string From b7a8cf822ec7b391fd5d59bcb99a238290c38517 Mon Sep 17 00:00:00 2001 From: slunimara Date: Sat, 16 Sep 2023 16:37:05 +0200 Subject: [PATCH 3/5] Replace dropdowns with reactions Co-Authored-By: petrspirka <91654267+petrspirka@users.noreply.github.com> --- bot/Dockerfile | 2 +- bot/src/bot.ts | 95 +++++++++++++++++-- bot/src/command.ts | 11 ++- bot/src/commands/messageManager.ts | 29 ++++-- bot/src/databaseSource.ts | 4 +- bot/src/events/onGuildMemberAdd.ts | 4 +- bot/src/events/onInteractionCreate.ts | 4 +- bot/src/events/onReactionAdd.ts | 34 ++++++- bot/src/events/onReactionRemove.ts | 5 +- bot/src/interfaces/botConfig.ts | 3 - bot/src/interfaces/onInteractionCreateArgs.ts | 2 + bot/src/interfaces/onReactionAddArgs.ts | 6 +- bot/src/interfaces/textFile.ts | 2 +- bot/src/models/index.ts | 1 + bot/src/models/messageReactions.ts | 25 +++++ bot/src/models/users.ts | 2 +- texts/role.json | 20 ++++ 17 files changed, 207 insertions(+), 42 deletions(-) create mode 100644 bot/src/models/messageReactions.ts diff --git a/bot/Dockerfile b/bot/Dockerfile index 05fb11f..a490389 100644 --- a/bot/Dockerfile +++ b/bot/Dockerfile @@ -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" ] diff --git a/bot/src/bot.ts b/bot/src/bot.ts index 6e98329..8bb7331 100644 --- a/bot/src/bot.ts +++ b/bot/src/bot.ts @@ -7,6 +7,9 @@ import { Client, Role, Partials, + Snowflake, + Emoji, + GuildTextBasedChannel, } from "discord.js" import { @@ -31,6 +34,9 @@ import { OnReactionAddAction, OnReactionRemoveAction, } from "./types" +import { + MessageReaction +} from "./models" const REST_VERSION = "10" @@ -41,7 +47,7 @@ export class Bot { buttonCommands = new Map>() modalCommands = new Map>() dropdownCommands = new Map>() - reactionMessages = new Map>() // TODO + reactionMessages = new Map>() private async onReady(args: OnReadyArgs): Promise { throw new Error("Event 'onReady' is not implementet") } @@ -87,10 +93,9 @@ export class Bot { if (config.modalCommands) this.initModalCommands(config.modalCommands) if (config.dropdownCommands) this.initDropdownCommands(config.dropdownCommands) - // TODO - /* if (config.reactionMessages) { - this.initReactionMessages(config.reactionMessages); - } */ + this.loadReactionMessages().then(value => { + this.reactionMessages = value + }) this.initEvents(config) this.init() @@ -138,6 +143,7 @@ export class Bot { modals: this.modalCommands, dropdown: this.dropdownCommands, db: this.db, + bot: this, mailer: this.mailer, commandRegistration: this.registerChatInputGuildCommands, } @@ -188,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 { @@ -249,6 +254,82 @@ export class Bot { }) } + /** + * Loads reaction messages from the database + * @returns Promise that resolves to reaction messages + */ + private async loadReactionMessages(): Promise>> { + const ReactionMessages = new Map>(); + + 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") + } + + 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) { + // 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 diff --git a/bot/src/command.ts b/bot/src/command.ts index 01b9557..8815939 100644 --- a/bot/src/command.ts +++ b/bot/src/command.ts @@ -17,7 +17,9 @@ import { StringSelectMenuInteraction, RESTPostAPIApplicationCommandsJSONBody, // TODO: Update to latest discord.js } from "discord.js" - +import { + Bot +} from "./bot" import { InvalidChannel, InvalidGuild, @@ -52,10 +54,12 @@ class Command { description!: string client!: Client mailer!: Mailer + bot!: Bot - async execute(client: Client, mailer: Mailer): Promise { + async execute(client: Client, mailer: Mailer, bot: Bot): Promise { this.client = client this.mailer = mailer + this.bot = bot await this.executable() } @@ -69,10 +73,11 @@ export class InteractionCommand extends Command { interaction!: T // @ts-ignore - async execute(client: Client, mailer: Mailer, interaction: T): Promise { + async execute(client: Client, mailer: Mailer, bot: Bot, interaction: T): Promise { this.client = client this.mailer = mailer this.interaction = interaction + this.bot = bot try { await this.executable() diff --git a/bot/src/commands/messageManager.ts b/bot/src/commands/messageManager.ts index 055e3b5..8c8b475 100644 --- a/bot/src/commands/messageManager.ts +++ b/bot/src/commands/messageManager.ts @@ -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" @@ -22,7 +22,6 @@ import { InvalidURLError, } from "../errors" - const maxMessageLength = 2000 const channelTagName = "channel" const mentionTagName = "mention" @@ -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 { 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() @@ -244,20 +244,33 @@ export class MessageManagerCommand extends ChatInputCommand { rows.push(row) } + // Reactions + if (rawMessage.reactions) { + const reactionMap = new Map() + + for (const [key, value] of Object.entries(rawMessage.reactions)) { + reactionMap.set(value, key) + } + + await this.bot.addReactionMessage(rawMessage.id, channel.id, reactionMap) + + reactionMap.forEach(async (_, emoji) => { + await message.react(emoji) + }); + } + const unparsedContent = rawMessage.content.join("\n") const content = this.handleMentions(unparsedContent) 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; }) { diff --git a/bot/src/databaseSource.ts b/bot/src/databaseSource.ts index b45bf0d..f4f88f4 100644 --- a/bot/src/databaseSource.ts +++ b/bot/src/databaseSource.ts @@ -5,7 +5,8 @@ import { DataSource } from "typeorm" import { Validation, Error, - User + User, + MessageReaction, } from "./models" const { @@ -27,6 +28,7 @@ export const DatabaseSource = new DataSource({ Validation, Error, User, + MessageReaction, ], synchronize: true, logging: false, diff --git a/bot/src/events/onGuildMemberAdd.ts b/bot/src/events/onGuildMemberAdd.ts index 8d88f17..d63264b 100644 --- a/bot/src/events/onGuildMemberAdd.ts +++ b/bot/src/events/onGuildMemberAdd.ts @@ -1,5 +1,3 @@ import { OnGuildMemberAddAction } from "../types" -export const onGuildMemberAdd: OnGuildMemberAddAction = async ({ member }) => { - -} +export const onGuildMemberAdd: OnGuildMemberAddAction = async ({ member }) => {} \ No newline at end of file diff --git a/bot/src/events/onInteractionCreate.ts b/bot/src/events/onInteractionCreate.ts index 692fadf..6055867 100644 --- a/bot/src/events/onInteractionCreate.ts +++ b/bot/src/events/onInteractionCreate.ts @@ -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> | undefined if (interaction.isButton()) { @@ -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) } diff --git a/bot/src/events/onReactionAdd.ts b/bot/src/events/onReactionAdd.ts index d72a4a5..30b807e 100644 --- a/bot/src/events/onReactionAdd.ts +++ b/bot/src/events/onReactionAdd.ts @@ -1,6 +1,32 @@ import { OnReactionAddAction } from "../types" -export const onReactionAdd: OnReactionAddAction = async ({ reaction, user }) => { - console.log(`${user.username} použil reakci ${reaction.emoji}!`) - // TODO -} +export const onReactionAdd: OnReactionAddAction = async ({ reactionMessages, reaction, user }) => { + if(user.bot){ + return; + } + + const messageId = reaction.message.id + if(!reactionMessages.has(messageId)){ + return; + } + + const removeAction = reaction.users.remove(user.id) + + const reactionBinds = reactionMessages.get(messageId)! + const guild = reaction.message.guild! + const roleName = reactionBinds.get(reaction.emoji.toString()) + + if(roleName !== undefined){ + const role = await guild.roles.cache.find(role => role.name === roleName)!.id + const gulidMember = await guild.members.fetch(user.id) + + if(gulidMember.roles.cache.has(role)){ + await gulidMember.roles.remove(role) + } + else { + await gulidMember.roles.add(role) + } + } + + await removeAction; +} \ No newline at end of file diff --git a/bot/src/events/onReactionRemove.ts b/bot/src/events/onReactionRemove.ts index 892fa3d..edd6d1d 100644 --- a/bot/src/events/onReactionRemove.ts +++ b/bot/src/events/onReactionRemove.ts @@ -1,6 +1,3 @@ import { OnReactionRemoveAction } from "../types" -export const onReactionRemove: OnReactionRemoveAction = async ({ reaction, user }) => { - console.log(`${user.username} smazal reakci ${reaction.emoji}!`) - // TODO -} +export const onReactionRemove: OnReactionRemoveAction = async ({ reaction, user }) => {} diff --git a/bot/src/interfaces/botConfig.ts b/bot/src/interfaces/botConfig.ts index 9133d99..03e6106 100644 --- a/bot/src/interfaces/botConfig.ts +++ b/bot/src/interfaces/botConfig.ts @@ -24,7 +24,4 @@ export interface BotConfig { onReactionRemove?: OnReactionRemoveAction onInteractionCreate?: OnInteractionCreateAction onGuildMemberAdd?: OnGuildMemberAddAction - // TODO } - - diff --git a/bot/src/interfaces/onInteractionCreateArgs.ts b/bot/src/interfaces/onInteractionCreateArgs.ts index 62d1735..4b2561d 100644 --- a/bot/src/interfaces/onInteractionCreateArgs.ts +++ b/bot/src/interfaces/onInteractionCreateArgs.ts @@ -2,6 +2,7 @@ import { CacheType, Client, Interaction } from "discord.js" import { DataSource } from "typeorm" import { ButtonCommand, ChatInputCommand, DropdownCommand, ICommand, IDropdownCommand, ModalCommand } from "../command" import { Mailer } from "../mailer" +import { Bot } from "../bot" export interface OnInteractionCreateArgs { client: Client @@ -12,5 +13,6 @@ export interface OnInteractionCreateArgs { dropdown: Map> db: DataSource mailer: Mailer + bot: Bot commandRegistration: (commands: ICommand[]) => Promise } diff --git a/bot/src/interfaces/onReactionAddArgs.ts b/bot/src/interfaces/onReactionAddArgs.ts index 7bd317d..05bd4b4 100644 --- a/bot/src/interfaces/onReactionAddArgs.ts +++ b/bot/src/interfaces/onReactionAddArgs.ts @@ -1,9 +1,7 @@ -import { Client, MessageReaction, PartialMessageReaction, PartialUser, User } from "discord.js" -import { DataSource } from "typeorm" +import { MessageReaction, PartialMessageReaction, PartialUser, Snowflake, User } from "discord.js" export interface OnReactionAddArgs { - client: Client; reaction: MessageReaction | PartialMessageReaction; user: User | PartialUser; - db: DataSource; + reactionMessages: Map>; } diff --git a/bot/src/interfaces/textFile.ts b/bot/src/interfaces/textFile.ts index e8efff7..7ceb89b 100644 --- a/bot/src/interfaces/textFile.ts +++ b/bot/src/interfaces/textFile.ts @@ -7,7 +7,7 @@ export interface TextFile { export interface TextFileMessage { id: string content: string[] - // TODO reactions + reactions: object, components?: { dropdowns?: { id: string diff --git a/bot/src/models/index.ts b/bot/src/models/index.ts index 540a6c3..d539f12 100644 --- a/bot/src/models/index.ts +++ b/bot/src/models/index.ts @@ -1,3 +1,4 @@ export * from "./validation"; export * from "./error"; export * from "./users"; +export * from "./messageReactions" \ No newline at end of file diff --git a/bot/src/models/messageReactions.ts b/bot/src/models/messageReactions.ts new file mode 100644 index 0000000..7822c4f --- /dev/null +++ b/bot/src/models/messageReactions.ts @@ -0,0 +1,25 @@ +import { + PrimaryGeneratedColumn, + CreateDateColumn, + BaseEntity, + Column, + Entity, + PrimaryColumn +} from "typeorm" + +/* eslint-disable */ +@Entity() +export class MessageReaction extends BaseEntity { + @PrimaryColumn("varchar", { length: 32 }) + messageId!: string + + @PrimaryColumn("varchar", { length: 32 }) + channelId!: string + + @PrimaryColumn("varchar", { length: 64 }) + emoji!: string + + @Column("varchar", { length: 32 }) + role!: string +} +/* eslint-enable */ \ No newline at end of file diff --git a/bot/src/models/users.ts b/bot/src/models/users.ts index bfdc490..81b3c07 100644 --- a/bot/src/models/users.ts +++ b/bot/src/models/users.ts @@ -13,7 +13,7 @@ export class User extends BaseEntity { id!: number @Column("varchar", { length: 320 }) - email!: number + email!: string @CreateDateColumn() createdAt!: Date diff --git a/texts/role.json b/texts/role.json index fd5aa3d..15e38f7 100644 --- a/texts/role.json +++ b/texts/role.json @@ -17,6 +17,14 @@ "", "**Na výběr máme role:**" ], + "reactions": { + "Aktuality": ":zero:", + "Novinky": ":one:", + "Akce": ":two:", + "Pivní srazy": ":three:", + "Připomínky": ":four:", + "Háčkování": ":five:" + }, "components": { "dropdowns": [ { @@ -46,6 +54,18 @@ "", "**Na výběr máme z jazyků:**" ], + "reactions": { + "C": "0️⃣", + "C++": "1️⃣", + "Java": "2️⃣", + "Web": "3️⃣", + "Webové aplikace": "4️⃣", + "Python": "5️⃣", + "Golang": "6️⃣", + "Assembler": "7️⃣", + "Mobilní aplikace": "8️⃣", + "CommonLisp": "9️⃣" + }, "components": { "dropdowns": [ { From 0d4561983316df1284434126f5c038936b75330c Mon Sep 17 00:00:00 2001 From: slunimara Date: Sun, 17 Sep 2023 12:16:08 +0200 Subject: [PATCH 4/5] Remove reactions and email regex Co-Authored-By: petrspirka <91654267+petrspirka@users.noreply.github.com> --- bot/src/bot.ts | 4 ++++ bot/src/commands/messageManager.ts | 3 +++ bot/src/commands/modals/verification.ts | 6 ------ bot/src/errors.ts | 8 +------- bot/src/utils/index.ts | 1 - bot/src/utils/isUpolEmail.ts | 4 ++-- bot/src/utils/isValidateEmail.ts | 7 ------- bot/src/vocabulary.ts | 4 ++-- 8 files changed, 12 insertions(+), 25 deletions(-) delete mode 100644 bot/src/utils/isValidateEmail.ts diff --git a/bot/src/bot.ts b/bot/src/bot.ts index 8bb7331..741b5eb 100644 --- a/bot/src/bot.ts +++ b/bot/src/bot.ts @@ -295,6 +295,10 @@ export class Bot { 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) diff --git a/bot/src/commands/messageManager.ts b/bot/src/commands/messageManager.ts index 8c8b475..87d427d 100644 --- a/bot/src/commands/messageManager.ts +++ b/bot/src/commands/messageManager.ts @@ -253,6 +253,9 @@ export class MessageManagerCommand extends ChatInputCommand { } await this.bot.addReactionMessage(rawMessage.id, channel.id, reactionMap) + + // Clean old reactions before adding new ones + await message.reactions.removeAll() reactionMap.forEach(async (_, emoji) => { await message.react(emoji) diff --git a/bot/src/commands/modals/verification.ts b/bot/src/commands/modals/verification.ts index 986c90b..9ee0a9a 100644 --- a/bot/src/commands/modals/verification.ts +++ b/bot/src/commands/modals/verification.ts @@ -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"; @@ -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) diff --git a/bot/src/errors.ts b/bot/src/errors.ts index def8d74..ae28244 100644 --- a/bot/src/errors.ts +++ b/bot/src/errors.ts @@ -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\`.`) } } diff --git a/bot/src/utils/index.ts b/bot/src/utils/index.ts index 97abc53..d98c0f5 100644 --- a/bot/src/utils/index.ts +++ b/bot/src/utils/index.ts @@ -3,7 +3,6 @@ export * from "./getButtonStyle" export * from "./isHttp" export * from "./isHttpUrlWithFileExt" export * from "./isUpolEmail" -export * from "./isValidateEmail" export * from "./makeRegisterHTML" export * from "./makeRegisterText" export * from "./parseByTag" diff --git a/bot/src/utils/isUpolEmail.ts b/bot/src/utils/isUpolEmail.ts index c5e5b65..b694a00 100644 --- a/bot/src/utils/isUpolEmail.ts +++ b/bot/src/utils/isUpolEmail.ts @@ -1,5 +1,5 @@ export function isUpolEmail(email: string): boolean { return email .toLowerCase() - .includes("@upol.cz") -} + .match(/^[a-z]*\.[a-z]*[0-9]{2}\@upol\.cz$/)!== null +} \ No newline at end of file diff --git a/bot/src/utils/isValidateEmail.ts b/bot/src/utils/isValidateEmail.ts deleted file mode 100644 index b369b99..0000000 --- a/bot/src/utils/isValidateEmail.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function isValidateEmail(email: string): boolean { - return email - .toLowerCase() - .match( - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - ) !== null -} diff --git a/bot/src/vocabulary.ts b/bot/src/vocabulary.ts index f97d2f4..0a504ab 100644 --- a/bot/src/vocabulary.ts +++ b/bot/src/vocabulary.ts @@ -7,5 +7,5 @@ export const VOC_RequestSended = "Žádost byla odeslána." export const VOC_VerificationCodeSended = (email: string) => `Verifikační kod byl zaslán na email: **${email}**.` export const VOC_RoleAdded = (role: Role | string) => `Role ${role} byla **přidána**.` export const VOC_RoleRemoved = (role: Role) => `Role ${role} byla **odebrána**.` -export const VOC_EveryRequest = (sender: any, room: any, text: string) => `Uživatel ${sender} zažádal v ${room} o everyone. Důvod žádost: ${text}` -export const VOC_QuoteRequest = (sender: any, text: string) => `Uživatel ${sender} zažádal o citát.\n${text}` +export const VOC_EveryRequest = (sender: any, room: any, text: string) => `Uživatel ${sender} zažádal v ${room} o everyone. Důvod žádost: \n\n${text}` +export const VOC_QuoteRequest = (sender: any, text: string) => `Uživatel ${sender} zažádal o citát.\n\n${text}` From 9cc4140c9f4d9575527f8f209ac57d05e0179003 Mon Sep 17 00:00:00 2001 From: slunimara Date: Sun, 17 Sep 2023 15:44:16 +0200 Subject: [PATCH 5/5] Texts Co-Authored-By: petrspirka <91654267+petrspirka@users.noreply.github.com> --- bot/src/commands/messageManager.ts | 11 +- bot/src/modals/verification.ts | 2 +- bot/src/models/messageReactions.ts | 2 +- texts/firewall.json | 56 ++++--- "texts/obecn\303\251-informace.json" | 79 ++++----- texts/role.json | 113 +++++-------- texts/vyber-predmetu.json | 229 +++++++++++++-------------- 7 files changed, 222 insertions(+), 270 deletions(-) diff --git a/bot/src/commands/messageManager.ts b/bot/src/commands/messageManager.ts index 87d427d..38896b3 100644 --- a/bot/src/commands/messageManager.ts +++ b/bot/src/commands/messageManager.ts @@ -245,8 +245,8 @@ export class MessageManagerCommand extends ChatInputCommand { } // Reactions + const reactionMap = new Map() if (rawMessage.reactions) { - const reactionMap = new Map() for (const [key, value] of Object.entries(rawMessage.reactions)) { reactionMap.set(value, key) @@ -257,13 +257,18 @@ export class MessageManagerCommand extends ChatInputCommand { // Clean old reactions before adding new ones await message.reactions.removeAll() - reactionMap.forEach(async (_, emoji) => { + 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) + + // Adds list of reaction and assigned roles + reactionMap.forEach((role, emoji) => { + content += `\n- ${emoji} - ${role}` + }) if (rows.length > 0) { await message.edit({ diff --git a/bot/src/modals/verification.ts b/bot/src/modals/verification.ts index ab20aee..93291b4 100644 --- a/bot/src/modals/verification.ts +++ b/bot/src/modals/verification.ts @@ -8,7 +8,7 @@ export const VerificationModal = new ModalBuilder() .addComponents( new TextInputBuilder() .setCustomId("verificationStudentUpolEmail") - .setLabel("Zadejte Váš studentský email v rámci UPOL") + .setLabel("Zadejte Váš studentský email v úplném tvaru.") .setStyle(TextInputStyle.Short), ) as ActionRowBuilder ) diff --git a/bot/src/models/messageReactions.ts b/bot/src/models/messageReactions.ts index 7822c4f..100e291 100644 --- a/bot/src/models/messageReactions.ts +++ b/bot/src/models/messageReactions.ts @@ -19,7 +19,7 @@ export class MessageReaction extends BaseEntity { @PrimaryColumn("varchar", { length: 64 }) emoji!: string - @Column("varchar", { length: 32 }) + @Column("varchar", { length: 64 }) role!: string } /* eslint-enable */ \ No newline at end of file diff --git a/texts/firewall.json b/texts/firewall.json index 7bc553f..c9bacae 100644 --- a/texts/firewall.json +++ b/texts/firewall.json @@ -1,70 +1,68 @@ { - "channelID": "962442923868315669", + "channelID": " ", "messages": [ { - "id": "1008779009372459058", + "id": " ", "content": [ - "V této místnosti si ověříš svůj účet. Můžeš zažádat jednu z rolí Student ,Návštěva nebo Katedra.", - "", - "**Postup při žádosti o jednotlivé role:**" + "# Ověření účtu", + "V této místnosti ověříš svůj účet a tím získáš jednu z rolí: Student Návštěva nebo Katedra. Tyto role ti zpřístupní základní místnosti tohoto serveru." ] }, { - "id": "1008779057917345862", + "id": " ", "content": [ - "Návštěva - pokud jsi přišel jen na návštěvu nebo nejsi student naší katedry, klikni na tlačítko **\"Jsem návštěvník\"**. Následně se ti přidá nebo odstraní role Návštěva." + "## Jsem Student - student katedry informatiky", + "1. Klikni na tlačítko **\"Jsem student\"**.", + "2. Do kolonky email zadej tvůj univerzitní email v úplném tvaru **`jmeno.prijmeniXX@upol.cz`** (například `tomas.marny01@upol.cz`). Formulář následně odešli.", + "3. Na zadaný univerzitní email ti přijde **verifikační klíč**.\"", + "4. V místnosti se ti objeví zpráva s tlačítkem **\"Mám verifikační klíč\"**. Po kliknutí na tlačítko se ti zobrazí formulář pro ověření účtu do kterého zadáš verifikační klíč." ], "components": { "buttons": [ { - "id": "btnHost", - "label": "Jsem návštěvník", - "style": "Primary" + "id": "btnStuden", + "label": "Jsem student", + "style": "Success" } ] } }, { - "id": "1008779079270535198", + "id": " ", "content": [ - "⠀", - "Student - student katedry informatiky", - "1. Klikni na tlačítko **\"Jsem student\"**.", - "2. Do kolonky email zadej tvůj univerzitní email v úplném tvaru **jmeno.prijmeniXX@upol.cz** a zašli formulář. př. tomas.marny01@upol.cz", - "3. Na zadaný email přijde ověřovací URL odkaz, kterým ověříš svůj účet." + "## Jsem Katedra - učitel naší katedry", + "Pro přidělení této role kontaktujte libovolného uživatele s rolí Katedra nebo Root." ], "components": { "buttons": [ { - "id": "btnStuden", - "label": "Jsem student", - "style": "Success" + "id": "bntDepartment", + "label": "Jsem zaměstnanec", + "style": "Danger" } ] } }, { - "id": "1008779106680328324", + "id": " ", "content": [ - "⠀", - "Katedra - učitel naší katedry", - "Pro přidělení této role kontaktujte libovolného uživatele s rolí Katedra nebo Root." + "## Jsem Návštěva - návštěvník serveru", + "Pokud jsi přišel jen na návštěvu nebo nejsi student naší katedry, klikni na tlačítko **\"Jsem návštěvník\"**. Následně se ti přidá nebo odstraní role Návštěva." ], "components": { "buttons": [ { - "id": "bntDepartment", - "label": "Jsem zaměstnanec", - "style": "Danger" + "id": "btnHost", + "label": "Jsem návštěvník", + "style": "Primary" } ] } }, { - "id": "1008779124032147558", + "id": " ", "content": [ - "⠀", - "_Použitím těchto tlačítek souhlasíte se zpracováním Vašich osobních/školních údajů a s pravidly serveru. (školní email)_" + "*Použitím těchto tlačítek souhlasíte se zpracováním Vašich údajů a s pravidly serveru. **Email není veřejně dostupný, slouží čistě pro účely verifikace statusu studenta.***" ] } ] diff --git "a/texts/obecn\303\251-informace.json" "b/texts/obecn\303\251-informace.json" index c719275..b6484e9 100644 --- "a/texts/obecn\303\251-informace.json" +++ "b/texts/obecn\303\251-informace.json" @@ -1,48 +1,38 @@ { - "channelID": "960522831374975017", + "channelID": " ", "messages": [ { - "id": "1019494961709846548", + "id": " ", "content": [ - "**Vítáme Tě na oficiálním discord serveru Katedry informatiky UP. **", - "Dříve, než začneš na serveru fungovat, jdi do místnosti firewall, kde podle návodu ověříš svůj účet. Bez tohoto kroku ti nebudou zpřístupněny žádné funkce. Poté navštiv místnost role a podle návodu si nastav role tak, aby se ti zpřístupnily ostatní funkce. Pro studenty je určena místnost výběr-předmětů, kde si zvolí své předměty, které zobrazí místnosti k předmětům. Server máme rozdělen na několik kategorií." + "# Vítej na serveru Katedry informatiky UP", + "Tento server slouží jako komunitní centrum pro všechny studenty naší katedry. Jsou zde nejen místnosti pro každý vyučovaný předmět, ale i místnosti pro zábavu a seznámení se s dalšími spolužáky.", + "Dříve, než začneš na serveru fungovat, jdi do místnosti firewall, kde podle návodu ověříš svůj účet. Bez tohoto kroku ti nebudou zpřístupněny žádné funkce. Dále v místnosti role si zvolíš role, které tě zajímají. Pokud jsi student tak nezapomeň navštiv i místnost výběr-předmětů, kde si zvolíš role k předmětům, které chceš. Role předmětů ti zpřístupní i k nim dané místnosti, do kterých mají přístup pouze studenti." ] }, { - "id": "1019494981003653173", + "id": " ", "content": [ - "⠀", - "⠀", - "**SERVER**", - "> V této kategorii najdeš veškeré základní informace ohledně fungování serveru. Rovněž si tu můžeš vybrat role, které ti zpřístupní další funkce.", - "", - "**INFORMAČNÍ TABULE**", - "> Místnosti s aktualitami, novinkami a různými aktivitami spojenými s katedrou. Nejdůležitější místnosti zde jsou aktuality-a-oznámení a místnost akce-soutěže, kde můžeš zjistit, co se kde zrovna děje a co probíhá.", - "", - "**GENERAL**", - "> Hlavní kategorie serveru. Najdeš zde zejména general (hlavní chat), nebo například off-topic. Nezapomeň nám zanechat zpětnou vazbu v <#1039869491250745354>, případně můžeš přidat nějaké citáty vyučujících.", - "", - "**ZÁBAVA**", - "> Máš galerii plnou memes? Chceš si pokecat o gamingu, doporučit film, udělat si dobrý playlist, nebo prostě jen zajít na pivo? Tato kategorie je pro tebe!", - "", - "**TECHNOLOGIE**", - "> Diskuzní místnosti související s technologií. Pokud máš nějaký technický dotaz nebo zajímavost, piš to sem. Dotazy přímo ke konkrétnímu předmětům piš do místnosti pro daný předmět, kterou najdeš v kategoriích ZIMNÍ/LETNÍ SEMESTR a PROGRAMOVACÍ JAZYKY níže.", - "", - "**STUDIUM**", - "> Tady najdeš věci týkající se studia, zejména potom kanál <#1019921995124719616>, kde se můžeš ptát na vše související se studiem na univerzitě.", - "", - "**PROGRAMOVACÍ JAZYKY, ZIMNÍ SEMESTR, LETNÍ SEMESTR**", - "> Do těchto místností mají přístup __pouze studenti__. Veškeré informace k těmto kategoriím najdeš v místnosti role a výběr-předmětů.", - "", - "**BOT**", - "> Samostatná kategorie pro našeho bota." + "## Server máme rozdělen na několik kategorií.", + "### SERVER", + "V této kategorii najdeš veškeré základní informace ohledně fungování serveru. Rovněž si tu můžeš vybrat role, které ti zpřístupní další funkce.", + "### INFORMAČNÍ TABULE", + "Místnosti s aktualitami, novinkami a různými aktivitami spojenými s katedrou. Nejdůležitější místnosti zde jsou aktuality-a-oznámení a místnost akce-soutěže, kde můžeš zjistit, co se kde zrovna děje a co probíhá.", + "### GENERAL", + "Hlavní kategorie serveru. Najdeš zde zejména general (hlavní chat), nebo například off-topic. Nezapomeň nám zanechat zpětnou vazbu v <#1039869491250745354>, případně můžeš přidat nějaké citáty vyučujících.", + "### ZÁBAVA", + "Máš galerii plnou memes? Chceš si pokecat o gamingu, doporučit film, udělat si dobrý playlist, nebo prostě jen zajít na pivo? Tato kategorie je pro tebe!", + "### TECHNOLOGIE", + "Diskuzní místnosti související s technologií. Pokud máš nějaký technický dotaz nebo zajímavost, piš to sem. Dotazy přímo ke konkrétnímu předmětům piš do místnosti pro daný předmět, kterou najdeš v kategoriích ZIMNÍ/LETNÍ SEMESTR a PROGRAMOVACÍ JAZYKY níže.", + "### STUDIUM", + "Tady najdeš věci týkající se studia, zejména potom kanál <#1019921995124719616>, kde se můžeš ptát na vše související se studiem na univerzitě.", + "### PROGRAMOVACÍ JAZYKY, ZIMNÍ SEMESTR, LETNÍ SEMESTR", + "**Do těchto místností mají přístup pouze studenti.** Veškeré informace k těmto kategoriím najdeš v místnosti role a výběr-předmětů." ] }, { - "id": "1019495000641372190", + "id": " ", "content": [ - "⠀", - "**Pravidla**", + "## Pravidla", "1. Zákaz nesmyslného tagování rolí.", "2. Není dovoleno šířit jakékoliv fake news a nepodložené informace.", "3. Zákaz sexuálního, explicitního a NSFW obsahu, diskriminace ostatních ras, pohlaví, náboženství nebo sexuální orientace.", @@ -60,32 +50,33 @@ ] }, { - "id": "1019495022049103872", + "id": " ", "content": [ - "⠀", - "**Tipy a příkazy**", + "##Tipy a příkazy", "", - "**Tipy**", + "### Tipy", " - Při vstupu do místnosti se doporučujeme podívat na informace o místnosti (téma kanálu), abyste věděli, jak s místností pracovat. Na desktopu je najdete na horní liště vedle názvu kanálu. Na mobilu v pravém sidebaru.", " - Než se na něco zeptáte, doporučujeme se podívat do připnutých zpráv, zda už se téma v minulosti už neřešilo.", - "", - "**Příkazy**", + "### Příkazy", " - Pomocí příkazu **/everyrequest popisek** můžete poslat žádost o ping everyone. Prosíme popište podrobně svoji žádost a odešlete. Žádost bude následně zpracována a následně schválena našimi Moderátor.", " - Pomocí příkazu **/citát popisek** můžete poslat žádost o přidání citátu do místnosti citáty. Prosíme posílejte citáty ve formátu: \"Text citátu. - Jméno učitele \". Citát bude následně zpracován a následně schválen našimi Moderátor.", "", "Doufáme, že na serveru najdeš všechny potřebné informace a taky skvělou komunitu studentů. V případě jakýchkoliv dotazů, napiš do <#1039869491250745354>.", "", - "**Pozvánka na server, kterou doporučujeme používat:**", - "https://discord.gg/zHqmCzPMEW", - "", "**Základní příručka pro Discord:**", "https://support.discord.com/hc/en-us/articles/360045138571-Beginner-s-Guide-to-Discord", "Co to jsou vlákna a jak fungují?", "https://support.discord.com/hc/en-us/articles/4403205878423-Threads-FAQ", "Jak vypnu notifikace konkrétní místnosti?", "https://support.discord.com/hc/en-us/articles/209791877-How-do-I-mute-and-disable-notifications-for-specific-channels-", - "", - "**Enjoy!**" + "## Enjoy!" + ] + }, + { + "id": " ", + "content": [ + "**Pozvánka na server, kterou doporučujeme používat:**", + "https://discord.gg/zHqmCzPMEW" ] } ] diff --git a/texts/role.json b/texts/role.json index 15e38f7..5e426e5 100644 --- a/texts/role.json +++ b/texts/role.json @@ -1,94 +1,63 @@ { - "channelID": "960452395312234545", + "channelID": " ", "messages": [ { - "id": "964816468346822666", + "id": " ", "content": [ - "**Výběr rolí**", - "V této místnosti si můžeš vybrat role. Role ti zpřístupní mnoho funkcí, umožní ti vstup do speciálních místností a zobrazí se ti na profilu, takže každý ví co tě zajímá. Pro výber role stačí zakliknout příslušné role v \"dropdown menu\", který je vždy pod výpisem možností." + "# Výběr rolí", + "V této místnosti si můžeš vybrat role, které tě zajímají. Role ti zpřístupní mnoho funkcí, umožní ti vstup do speciálních místností a zobrazí se ti na profilu. Pro přidání a odebrání role slouží vždy příslušný **emoji**, který je pod výpisem možností." ] }, { - "id": "964817512942419998", + "id": " ", "content": [ - "⠀", - "**Speciální role**", - "Chceš dostávat info o novinkách nebo pivních srazech? Po výběru role budeš dostávat upozornění na tagy této role.", - "", - "**Na výběr máme role:**" + "## Speciální role", + "Chceš dostávat informace o novinkách nebo upozornění o pivních srazech? Po výběru role budeš dostávat upozornění na tagy této role.", + "### Na výběr máme:" ], "reactions": { - "Aktuality": ":zero:", - "Novinky": ":one:", - "Akce": ":two:", - "Pivní srazy": ":three:", - "Připomínky": ":four:", - "Háčkování": ":five:" - }, - "components": { - "dropdowns": [ - { - "id": "dropdownAddRole", - "flag": "novinky", - "placeholder": "Vyber si jaké novinky chceš odebírat", - "min": 1, - "max": -1, - "options": [ - "Aktuality", - "Novinky", - "Akce", - "Pivní srazy", - "Připomínky", - "Háčkování" - ] - } - ] + "Aktuality": ":newspaper:", + "Novinky": ":calendar_spiral", + "Akce": ":fire:", + "Pivní srazy": ":beers:", + "Připomínky": ":interrobang:", + "Háčkování": ":knot:" } }, { - "id": "964817827225804871", + "id": " ", "content": [ - "⠀", - "**Programovací jazyky**", - "Role sloužící pro nadšence daného jazyka. Každý na tvém profilu uvidí, jaké programovací jazyky tě zajímají. Po výběru role budeš dostávat upozornění na tagy této role.", - "", - "**Na výběr máme z jazyků:**" + "## Zájmy", + "Role sloužící pro nadšence programovacího jazyka či technologie. Každý na tvém profilu uvidí, jaké máš zájmy. ", + "### Na výběr máme:" ], "reactions": { "C": "0️⃣", "C++": "1️⃣", - "Java": "2️⃣", - "Web": "3️⃣", - "Webové aplikace": "4️⃣", - "Python": "5️⃣", - "Golang": "6️⃣", - "Assembler": "7️⃣", - "Mobilní aplikace": "8️⃣", + "C#": "2️⃣", + "Java": "3️⃣", + "JavaScript": "4️⃣", + "Rust": "5️⃣", + "Python": "6️⃣", + "Golang": "7️⃣", + "Assembler": "8️⃣", "CommonLisp": "9️⃣" - }, - "components": { - "dropdowns": [ - { - "id": "dropdownAddRole", - "flag": "jazyky", - "placeholder": "Vyber si jaké programovací jazyky tě zajímají", - "min": 1, - "max": -1, - "options": [ - "C", - "C#", - "C++", - "Java", - "Web", - "Webové aplikace", - "Python", - "Golang", - "Assembler", - "Mobilní aplikace", - "CommonLisp" - ] - } - ] + } + }, + { + "id": " ", + "content": [], + "reactions": { + "Data science a AI": "0️⃣", + "Weby": "1️⃣", + "Mobilní aplikace": "2️⃣", + "Hardware": "3️⃣", + "Docker": "4️⃣", + "Sítě": "5️⃣", + "Databáze": "6️⃣", + "IoT": "7️⃣", + "Robotika": "8️⃣", + "Shell": "9️⃣" } } ] diff --git a/texts/vyber-predmetu.json b/texts/vyber-predmetu.json index b3f1aa7..f0293be 100644 --- a/texts/vyber-predmetu.json +++ b/texts/vyber-predmetu.json @@ -1,141 +1,130 @@ { - "channelID": "1019489224719872010", + "channelID": " ", "messages": [ { - "id": "1019489596108701746", + "id": " ", "content": [ - "**Výběr předmětů**", - "V této místnosti si zvolíš obor a předměty, které studuješ. Tyto volby ti následně zobrazí nové místnosti, které se vztahují ke konkrétnímu přemětu. Pro výber role stačí zakliknout příslušné role v \"dropdown menu\", který je vždy pod výpisem možností." + "# Výběr oborů a předmětů", + "V této místnosti si zvolíš obor a předměty, které studuješ. Tyto volby ti následně zobrazí nové místnosti, které se vztahují ke konkrétnímu předmětu. Výběr oborů a předmětů je zpřístupněn pouze studentům. To znamená, že učitelé do těchto místností nemají přístup. Pro přidání a odebrání role slouží vždy příslušný **emoji**, který je pod výpisem možností." ] }, { - "id": "1019489623099060246", + "id": " ", "content": [ - "⠀", - "**Výběr oborů**", - "Nejprve si vyber obor, který tě zajímá. Po výběru oboru se ti zobrazí, v kategorii **ZIMNÍ/LETNÍ SEMESTR** a **PROGRAMOVACÍ JAZYKY**, všechny místnosti s povinnými předměty (A) pro daný obor za celé studium. Volba je volná, takže si klidně můžeš vybrat všechny obory. Předměty povinně volitelné a volitelné si volíš až později.", + "## Výběr oborů", + "Nejprve si vyber jeden či více oborů, které tě zajímají. Po výběru oborů se ti zobrazí, v kategoriích **ZIMNÍ/LETNÍ SEMESTR** a **PROGRAMOVACÍ JAZYKY**, všechny místnosti s povinnými předměty **(A)** pro daný obor za celé studium. Volba je volná, takže si klidně můžeš vybrat všechny obory. Předměty povinně volitelné a volitelné si volíš až později.", "", "(Role Bc. Informatika přidělí předměty jen z prvního ročníku. Poté je nutné vybrat specializaci.)", - "", - "**Na výber máme obory:**" + "### Na výber máme bakalářské obory:" + ], + "reactions": { + "Bc. Informatika": "0️⃣", + "Bc. IT": "1️⃣", + "Bc. Informatika pro vzdělávání": "2️⃣", + "Bc. Programování a vývoj sw": "3️⃣", + "Bc. Obecná informatika": "4️⃣", + "Bc. Bioinformatika": "5️⃣", + "Bc. IT KOMBI": "6️⃣" + } + }, + { + "id": " ", + "content": [ + "### Na výber máme magisterské obory:" ], - "components": { - "dropdowns": [ - { - "id": "dropdownAddRoleOnlyStudent", - "flag": "obor", - "placeholder": "Vyber si obory", - "min": 1, - "max": 1, - "options": [ - "Bc. Informatika", - "Bc. IT", - "Bc. Informatika pro vzdělávání", - "Bc. Programování a vývoj sw", - "Bc. Obecná informatika", - "Bc. Bioinformatika", - "Bc. IT KOMBI", - "Mgr. Obecná informatika", - "Mgr. Počítačové systémy a technologie", - "Mgr. Vývoj software", - "Mgr. Umělá inteligence", - "Mgr. Učitelství informatiky pro střední školy" - ] - } - ] + "reactions": { + "Mgr. Obecná informatika": "0️⃣", + "Mgr. Umělá inteligence": "1️⃣", + "Mgr. Vývoj software": "2️⃣", + "Mgr. Počítačové systémy a technologie": "3️⃣", + "Mgr. Bioinformatika": "4️⃣", + "Mgr. Učitelství informatiky pro střední školy": "5️⃣" } }, { - "id": "1019489738962505829", + "id": " ", "content": [ - "⠀", - "**Výběr předmětů**", - "Aby se ti otevřely i místností pro povinně volitelné a volitelné předměty, vyber si z těchto rolí ty předměty, k jejímž místnostem chceš mít přístup. Navolené předměty si také můžeš kdykoliv změnit.**Tento výběr slouží pouze studentům.**", + "## Výběr předmětů", + "Aby ti byly zpřístupněny i místností pro povinně volitelné a volitelné předměty, vyber si z těchto rolí ty předměty, k jejímž místnostem chceš mít přístup. Navolené předměty si také můžeš kdykoliv změnit. Kanál pro diskuzi závěrečných prací naleznete v kategorii **STUDIUM**.", "", - "Některé předměty se stejným obsahem mají napříč studijními obory jiné názvy a zkratky. Vždy jsme z nich vybrali jednu a ostatní pod ní sjednotili. Při vyhledávání jsou jména všech zkratek pospolu.", + "Některé předměty se stejným obsahem mají napříč studijními obory jiné názvy a zkratky. Vždy jsme je sjednotili pod jednu roli.", "Příklad: **pp1 (papr1)**", - "", - "Kanál pro diskuzi závěrečných prací naleznete v kategorii **STUDIUM**.", - "", - "Můžeš vybírat podle abecedy, ale předměty ze skupiny **Programovací jazyky a programování** se nachází v posledním menu.", - "", "**Na výběr máme z předmětů:**" - ], - "components": { - "dropdowns": [ - { - "id": "dropdownAddRoleOnlyStudent", - "flag": "predmety_0n", - "placeholder": "Vyber si predmety (0 - N)", - "min": 1, - "max": -1, - "options": [ - "3dt", - "aiii1", - "aiii2", - "akti", - "algo1 (algo)", - "algo2 (zads)", - "algo3 (alm3)", - "bmtkq", - "dastr", - "grafa", - "infos", - "kombi", - "mr", - "napvs" - ] - }, - { - "id": "dropdownAddRoleOnlyStudent", - "flag": "predmety_oz", - "placeholder": "Vyber si predmety (O - Z)", - "min": 1, - "max": -1, - "options": [ - "os1", - "os2", - "pmv", - "pogr (pg)", - "pois", - "pos1 (pos)", - "pos2", - "pp1 (papr1)", - "pp2 (papr2)", - "pp3 (papr3)", - "pp4", - "prast", - "soft", - "sprda", - "texza", - "tempa", - "unixs", - "uroz (uro)", - "vytal", - "znm" - ] - }, - { - "id": "dropdownAddRoleOnlyStudent", - "flag": "predmety_prg", - "placeholder": "Vyber si predmety PRG", - "min": 1, - "max": -1, - "options": [ - "asm", - "jc", - "jcs1", - "jcs2", - "jcpp", - "jj1", - "jj2", - "jp", - "tmap", - "web", - "weba" - ] - } - ] + ] + }, + { + "id": " ", + "content": [], + "reactions": { + "3dt": "0️⃣", + "aiii1": "1️⃣", + "aiii2": "2️⃣", + "akti": "3️⃣", + "algo1 (algo)": "4️⃣", + "algo2 (zads)": "5️⃣", + "algo3 (alm3)": "6️⃣", + "bmtkq": "7️⃣", + "dastr": "8️⃣", + "grafa": "9️⃣" + } + }, + { + "id": " ", + "content": [], + "reactions": { + "infos": "0️⃣", + "kombi": "1️⃣", + "mr": "2️⃣", + "napvs": "3️⃣", + "os1": "4️⃣", + "os2": "5️⃣", + "pmv": "6️⃣", + "pogr (pg)": "7️⃣", + "pois": "8️⃣", + "pos1 (pos)": "9️⃣" + } + }, + { + "id": " ", + "content": [], + "reactions": { + "pos2": "0️⃣", + "pp1 (papr1)": "1️⃣", + "pp2 (papr2)": "2️⃣", + "pp3 (papr3)": "3️⃣", + "pp4": "4️⃣", + "prast": "5️⃣", + "soft": "6️⃣", + "sprda": "7️⃣", + "texza": "8️⃣", + "tempa": "9️⃣" + } + }, + { + "id": " ", + "content": [], + "reactions": { + "unixs": "0️⃣", + "uroz (uro)": "1️⃣", + "vytal": "2️⃣", + "znm": "3️⃣", + "asm": "4️⃣", + "jc": "5️⃣", + "jcs1": "6️⃣", + "jcs2": "7️⃣", + "jcpp": "8️⃣", + "jj1": "9️⃣" + } + }, + { + "id": " ", + "content": [], + "reactions": { + "jj2": "0️⃣", + "jp": "1️⃣", + "tmap": "2️⃣", + "web": "3️⃣", + "weba": "4️⃣" } } ]