diff --git a/src/bot/commands/checkin/handlers/checkin-audit.ts b/src/bot/commands/checkin/handlers/checkin-audit.ts index 9cd3556..1e322d7 100644 --- a/src/bot/commands/checkin/handlers/checkin-audit.ts +++ b/src/bot/commands/checkin/handlers/checkin-audit.ts @@ -3,7 +3,7 @@ import { registerCommand } from '@commands/registry' import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' import { CHECKIN_AUDIT_ID } from '@events/interaction-create/checkin/handlers/audit-modal' import { createCheckinReviewModal, encodeSnowflake, getCustomId } from '@utils/component' -import { getChannelOrThread, sendReply } from '@utils/discord' +import { getChannel, sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { log } from '@utils/logger' import { SlashCommandBuilder } from 'discord.js' @@ -30,7 +30,7 @@ registerCommand({ if (!interaction.inCachedGuild()) throw new CheckinAuditError(CheckinAudit.ERR.NotGuild) - const channel = await getChannelOrThread(interaction.guild, AUDIT_FLAME_CHANNEL) as TextChannel + const channel = await getChannel(interaction.guild, AUDIT_FLAME_CHANNEL) as TextChannel CheckinAudit.assertMissPerms(interaction.client.user, channel) const thread = await CheckinAudit.assertThreadUnderChannel(interaction.guild, interaction.channelId, channel) CheckinAudit.assertNotArchivedThread(thread) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index b9ccd73..d2f010c 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -76,10 +76,10 @@ ${checkin.public_id} โœ๐Ÿป **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} > *"[Api Tuan/Nona](${checkin.link}) <@${userDiscordId}> meredup hari ini, namun belum padam sepenuhnya. Perbaiki, dan nyalakan kembali percikan yang benar."* `, - LastCheckin: (userDiscordId: string, checkin: Checkin, flamewarden?: GuildMember) => ` + LastCheckin: (guildName: string, userDiscordId: string, checkin: Checkin, flamewarden?: GuildMember) => ` Wahai Tuan/Nona <@${userDiscordId}>, tercatat bahwa rangkaian nyala api Tuan/Nona telah terputus pada pergantian hari sebelumnya. -Namun demikian, percikan terakhir masih tersimpan dalam arsip Aksaria dan dapat ditinjau kembali. +Namun demikian, percikan terakhir masih tersimpan dalam arsip ${guildName} dan dapat ditinjau kembali. Berikut adalah *check-in* terakhir yang pernah Tuan/Nona torehkan: ๐Ÿ†” **Check-In ID**: @@ -99,9 +99,9 @@ ${flamewarden?.displayName : ''} > *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* `, - LastCheckinNote: (checkinLink: string, statusLink: string) => ` + LastCheckinNote: (guildName: string, checkinLink: string, statusLink: string) => ` Apabila Tuan/Nona meyakini bahwa [*check-in*](${checkinLink}) belum sempat ditinjau oleh <@&${FLAMEWARDEN_ROLE}>, -maka Aksaria membuka ruang klarifikasi dengan tata cara sebagai berikut: +maka ${guildName} membuka ruang klarifikasi dengan tata cara sebagai berikut: โ… . Berikan reaksi โ“ pada pesan [*status check-in*](${statusLink}) ini. โ…ก. Sebuah *thread* khusus akan tercipta secara otomatis. โ…ข. Gunakan *thread* tersebut untuk berkomunikasi dan mengajukan peninjauan kepada <@&${FLAMEWARDEN_ROLE}>. diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index 521320c..616dd01 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -56,7 +56,7 @@ export class CheckinStatus extends CheckinStatusMessage { `๐Ÿงญ Check-In #${checkin.public_id}`, CheckinStatus.MSG.WaitingCheckin(userDiscordId, checkin), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(guild.name) }, ) break } @@ -66,7 +66,7 @@ export class CheckinStatus extends CheckinStatusMessage { `๐Ÿ”ฅ Check-In #${checkin.public_id}`, CheckinStatus.MSG.ApprovedCheckin(userDiscordId, flamewarden, checkin), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(guild.name) }, ) break } @@ -76,7 +76,7 @@ export class CheckinStatus extends CheckinStatusMessage { `โŒ Check-In #${checkin.public_id}`, CheckinStatus.MSG.RejectedCheckin(userDiscordId, flamewarden, checkin), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(guild.name) }, ) break } @@ -91,7 +91,7 @@ export class CheckinStatus extends CheckinStatusMessage { `๐Ÿง Check-In`, CheckinStatus.MSG.NoCheckin(userDiscordId, checkinStreak), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(guild.name) }, ) return { content, embed } @@ -101,9 +101,9 @@ export class CheckinStatus extends CheckinStatusMessage { const buttons = this.generateButtons(guild.id, checkin) embed = createEmbed( `๐Ÿ•ฏ๏ธ Check-In #${checkin.public_id}`, - CheckinStatus.MSG.LastCheckin(userDiscordId, checkin, flamewarden), + CheckinStatus.MSG.LastCheckin(guild.name, userDiscordId, checkin, flamewarden), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(guild.name) }, ) return { content, embed, buttons } diff --git a/src/bot/commands/embed/handlers/role-grant-create.ts b/src/bot/commands/embed/handlers/role-grant-create.ts index 4551ae2..7e0ef6c 100644 --- a/src/bot/commands/embed/handlers/role-grant-create.ts +++ b/src/bot/commands/embed/handlers/role-grant-create.ts @@ -87,8 +87,8 @@ registerCommand({ .setTextInputComponent( new TextInputBuilder() .setCustomId('footer') - .setPlaceholder(DUMMY.FOOTER) - .setValue(DUMMY.FOOTER) + .setPlaceholder(DUMMY.FOOTER(interaction.guild.name)) + .setValue(DUMMY.FOOTER(interaction.guild.name)) .setStyle(TextInputStyle.Short) .setRequired(false), ), diff --git a/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts index 782567e..7ff831e 100644 --- a/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts @@ -3,7 +3,7 @@ import process from 'node:process' import { GRIND_ASHES_CHANNEL } from '@config/discord' import { registerClientReadyHandler } from '@events/client-ready/registry' import { EVENT_PATH } from '@events/index' -import { getChannelOrThread } from '@utils/discord' +import { getChannel } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { getModuleName } from '@utils/io' import { log } from '@utils/logger' @@ -27,7 +27,7 @@ registerClientReadyHandler({ log.check(ResetGrinderRoles.MSG.JobRunning) const guild = await client.guilds.fetch(process.env.GUILD_ID!) - const channel = await getChannelOrThread(guild, GRIND_ASHES_CHANNEL) as TextChannel + const channel = await getChannel(guild, GRIND_ASHES_CHANNEL) as TextChannel ResetGrinderRoles.assertChannel(channel) const users = await ResetGrinderRoles.getUsersWithLatestStreak(client.prisma) diff --git a/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts index 21ec8b4..77d4ae0 100644 --- a/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts @@ -13,15 +13,15 @@ export class ResetGrinderRolesMessage extends DiscordAssert { JobRunning: '[JOB] Running daily grinder reset...', JobSuccess: '[JOB] Grinder daily reset finished successfully', RemoveGrinderRoleFrom: (member: GuildMember) => `[JOB] Removed Grinder role from ${member.user.tag}`, - GoodBye: (member: GuildMember) => ` + GoodBye: (guildName: string, member: GuildMember) => ` # ๐Ÿ’” Nyala Api Tuan/Nona <@${member.id}> Telah Gugur -Tatkala hari telah berganti dan lonceng waktu menunjukkan pergantian malam, tercatat bahwa tiada *check-in* yang sah diterima pada hari yang telah berlalu. Maka, sesuai hukum Aksaria, peran Grinder untuk saat ini harus dilepaskan. +Tatkala hari telah berganti dan lonceng waktu menunjukkan pergantian malam, tercatat bahwa tiada *check-in* yang sah diterima pada hari yang telah berlalu. Maka, sesuai hukum ${guildName}, peran Grinder untuk saat ini harus dilepaskan. Api bukanlah padam karena kelemahan, melainkan karena ia tak disirami pada waktunya. Namun jangan berduka, jalan ini selalu terbuka bagi mereka yang bersedia memulai kembali. Apabila Tuan/Nona berkehendak menyalakan api kembali, silakan kembali ke <#${IGNITE_PATH_CHANNEL}> dan bangkitlah dari awal. -*Aksaria menanti mereka yang konsisten.* +*${guildName} menanti mereka yang konsisten.* `, GoodByeNotes: ` > Apabila *check-in* Tuan/Nona masih berada dalam status menunggu peninjauan (*waiting*) dan belum memperoleh keputusan hingga mendekati pergantian hari, maka dengan ini disampaikan ketentuan berikut: diff --git a/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts index def0b3d..0d341da 100644 --- a/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts @@ -79,7 +79,7 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage { await sendAsBot( null, channel, - { content: ResetGrinderRoles.MSG.GoodBye(member), components: [button], allowedMentions: { users: [member.id], roles: [] } }, + { content: ResetGrinderRoles.MSG.GoodBye(guild.name, member), components: [button], allowedMentions: { users: [member.id], roles: [] } }, ) log.info(this.MSG.RemoveGrinderRoleFrom(member)) diff --git a/src/bot/events/guild-member-update/grinder-role/handlers/index.ts b/src/bot/events/guild-member-update/grinder-role/handlers/index.ts index c4cc1ed..c593ffb 100644 --- a/src/bot/events/guild-member-update/grinder-role/handlers/index.ts +++ b/src/bot/events/guild-member-update/grinder-role/handlers/index.ts @@ -1,8 +1,8 @@ import type { TextChannel } from 'discord.js' -import { GRIND_ASHES_CHANNEL, GRINDER_ROLE } from '@config/discord' +import { AURA_FARMING_CHANNEL, GRINDER_ROLE } from '@config/discord' import { registerGuildMemberUpdateHandler } from '@events/guild-member-update/registry' import { EVENT_PATH } from '@events/index' -import { getChannelOrThread, sendAsBot } from '@utils/discord' +import { getChannel, sendAsBot } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { getModuleName } from '@utils/io' import { GrinderRole } from '../validators' @@ -18,7 +18,6 @@ const moduleName = getModuleName(EVENT_PATH, __filename) registerGuildMemberUpdateHandler({ desc: 'Watches grinder role assignment/removal for members on guild member update.', errorTag: () => `${moduleName}: ${GrinderRole.ERR.UnexpectedGrinderRole}`, - match: (_, newMember) => GrinderRole.isMemberHasRole(newMember, GRINDER_ROLE), async exec(_, oldMember, newMember) { try { if (!newMember.guild) @@ -27,7 +26,7 @@ registerGuildMemberUpdateHandler({ const newHasGrinderRole = GrinderRole.isMemberHasRole(newMember, GRINDER_ROLE) const oldHasGrinderRole = GrinderRole.isMemberHasRole(oldMember, GRINDER_ROLE) if (newHasGrinderRole && !oldHasGrinderRole) { - const channel = await getChannelOrThread(newMember.guild, GRIND_ASHES_CHANNEL) as TextChannel + const channel = await getChannel(newMember.guild, AURA_FARMING_CHANNEL) as TextChannel GrinderRole.assertChannel(channel) const button = GrinderRole.generateButton(newMember.guild.id) diff --git a/src/bot/events/guild-member-update/server-booster/handlers/index.ts b/src/bot/events/guild-member-update/server-booster/handlers/index.ts new file mode 100644 index 0000000..104f06f --- /dev/null +++ b/src/bot/events/guild-member-update/server-booster/handlers/index.ts @@ -0,0 +1,48 @@ +import type { TextChannel } from 'discord.js' +import { AURA_FARMING_CHANNEL } from '@config/discord' +import { registerGuildMemberUpdateHandler } from '@events/guild-member-update/registry' +import { EVENT_PATH } from '@events/index' +import { getChannel, sendAsBot } from '@utils/discord' +import { DiscordBaseError } from '@utils/discord/error' +import { getModuleName } from '@utils/io' +import { ServerBooster } from '../validators' + +export class ServerBoosterError extends DiscordBaseError { + constructor(message: string, options?: { cause?: unknown }) { + super('ServerBoosterError', message, options) + } +} + +const moduleName = getModuleName(EVENT_PATH, __filename) + +registerGuildMemberUpdateHandler({ + desc: 'Watches server booster for members on guild member update.', + errorTag: () => `${moduleName}: ${ServerBooster.ERR.UnexpectedServerBooster}`, + async exec(_, oldMember, newMember) { + try { + if (!newMember.guild) + throw new ServerBoosterError(ServerBooster.ERR.NotGuild) + + const wasBoosting = !!oldMember.premiumSince + const isBoosting = !!newMember.premiumSince + + const justBoosted = !wasBoosting && isBoosting + if (!justBoosted) + return + + const channel = await getChannel(newMember.guild, AURA_FARMING_CHANNEL) as TextChannel + ServerBooster.assertChannel(channel) + + const embed = ServerBooster.sayDeeplyThanksTo(newMember) + + await sendAsBot(null, channel, { + content: ServerBooster.MSG.SpecialThanks, + embeds: [embed], + }) + } + catch (err: any) { + if (!(err instanceof DiscordBaseError)) + throw err + } + }, +}) diff --git a/src/bot/events/guild-member-update/server-booster/messages/index.ts b/src/bot/events/guild-member-update/server-booster/messages/index.ts new file mode 100644 index 0000000..a9d99dd --- /dev/null +++ b/src/bot/events/guild-member-update/server-booster/messages/index.ts @@ -0,0 +1,46 @@ +import type { GuildMember } from 'discord.js' +import { BEARER_LOUNGE_CHANNEL, GRINDER_ROLE } from '@config/discord' +import { createEmbed } from '@utils/component' +import { DiscordAssert } from '@utils/discord' +import { DUMMY } from '@utils/placeholder' + +export class ServerBoosterMessage extends DiscordAssert { + static override readonly ERR = { + ...DiscordAssert.ERR, + UnexpectedServerBooster: 'โŒ Something went wrong while managing the server booster', + } + + static override readonly MSG = { + ...DiscordAssert.MSG, + SpecialThanks: `# โค๏ธโ€๐Ÿ”ฅ Sebuah Nyala Telah Diperkuat`, + Really: ` +Api tidak selalu membesar karena banyak kayu, +kadang karena satu jiwa yang rela memberi nyala!`, + ItMeansALot: (guildName: string, userDiscordId: string, boostCount: number) => ` +Tuan/Nona <@${userDiscordId}> telah mempersembahkan aura mereka untuk menguatkan nyala ${guildName} beserta para <@&${GRINDER_ROLE}>๐Ÿ”ฅ! + +**โœจ Maklumat Anugerah** +- Jumlah *Server Boost* ${guildName} kini bertambah menjadi **\`${boostCount}\`**. +- Tuan/Nona resmi diakui sebagai *Bearer of the Flame*. +- Gerbang khusus โ <#${BEARER_LOUNGE_CHANNEL}> kini terbuka bagi Tuan/Nona; sebuah ruang kehormatan untuk para penjaga nyala. + +Kami sampaikan terima kasih setinggi-tingginya. +Semoga nyala kebaikan ini kembali pada Tuan/Nona +dalam wujud disiplin, keberkahan, dan pertumbuhan. + `, + } + + static sayDeeplyThanksTo(member: GuildMember) { + return createEmbed( + this.MSG.Really, + this.MSG.ItMeansALot(member.guild.name, member.id, member.guild.premiumSubscriptionCount ?? 0), + DUMMY.COLOR, + { text: DUMMY.FOOTER(member.guild.name) }, + { + name: member.user.tag, + iconURL: member.user.displayAvatarURL(), + }, + member.user.displayAvatarURL({ size: 512 }), + ) + } +} diff --git a/src/bot/events/guild-member-update/server-booster/validators/index.ts b/src/bot/events/guild-member-update/server-booster/validators/index.ts new file mode 100644 index 0000000..9622c8c --- /dev/null +++ b/src/bot/events/guild-member-update/server-booster/validators/index.ts @@ -0,0 +1,8 @@ +import { DiscordAssert } from '@utils/discord' +import { ServerBoosterMessage } from '../messages' + +export class ServerBooster extends ServerBoosterMessage { + static override BASE_PERMS = [ + ...DiscordAssert.BASE_PERMS, + ] +} diff --git a/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts b/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts index 0e30fab..f33acb3 100644 --- a/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts +++ b/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts @@ -58,9 +58,9 @@ registerInteractionHandler({ const embed = createEmbed( `๐Ÿ”ฅ Audit Check-In Telah Diselesaikan`, - CheckinAudit.MSG.AuditSuccess(updatedCheckin.link!, flamewarden.id, updatedCheckin.user!.discord_id), + CheckinAudit.MSG.AuditSuccess(interaction.guild.name, updatedCheckin.link!, flamewarden.id, updatedCheckin.user!.discord_id), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(interaction.guild.name) }, ) await sendReply(interaction, '', false, { embeds: [embed], allowedMentions: { users: [updatedCheckin.user!.discord_id] } }) diff --git a/src/bot/events/interaction-create/checkin/handlers/modal.ts b/src/bot/events/interaction-create/checkin/handlers/modal.ts index ff52429..dbf4e10 100644 --- a/src/bot/events/interaction-create/checkin/handlers/modal.ts +++ b/src/bot/events/interaction-create/checkin/handlers/modal.ts @@ -47,7 +47,7 @@ registerInteractionHandler({ const msg = await sendReply( interaction, - Checkin.MSG.CheckinSuccess(todo), + Checkin.MSG.CheckinSuccess(interaction.guild.name, todo), false, { files: attachments.length ? attachments : undefined, diff --git a/src/bot/events/interaction-create/checkin/handlers/status-note-button.ts b/src/bot/events/interaction-create/checkin/handlers/status-note-button.ts index 21550cc..6072a95 100644 --- a/src/bot/events/interaction-create/checkin/handlers/status-note-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/status-note-button.ts @@ -37,7 +37,7 @@ registerInteractionHandler({ const statusMessageLink = messageLink(interaction.channelId, interaction.message.id, interaction.guildId) - await sendReply(interaction, CheckinStatus.MSG.LastCheckinNote(checkinLink, statusMessageLink)) + await sendReply(interaction, CheckinStatus.MSG.LastCheckinNote(interaction.guild.name, checkinLink, statusMessageLink)) } catch (err: any) { if (err instanceof DiscordBaseError) diff --git a/src/bot/events/interaction-create/checkin/messages/audit.ts b/src/bot/events/interaction-create/checkin/messages/audit.ts index 0dbd9cd..1ea0197 100644 --- a/src/bot/events/interaction-create/checkin/messages/audit.ts +++ b/src/bot/events/interaction-create/checkin/messages/audit.ts @@ -16,13 +16,13 @@ ${waitingCheckinList} static override readonly MSG = { ...DiscordAssert.MSG, - AuditSuccess: (checkinLink: string, flamewardenId: string, userDiscordId: string) => ` + AuditSuccess: (guildName: string, checkinLink: string, flamewardenId: string, userDiscordId: string) => ` Wahai Tuan/Nona <@${userDiscordId}>, [percikan](${checkinLink}) yang Tuan/Nona titipkan telah selesai ditakar dan ditetapkan. ๐Ÿ—“ **Audited At**: ${getParsedNow(getNow())} ๐Ÿ‘€ **Audited By**: <@${flamewardenId}> -> *"Api telah diuji, dan keputusannya kini tercatat dalam Aksaria."* +> *"Api telah diuji, dan keputusannya kini tercatat dalam ${guildName}."* `, } } diff --git a/src/bot/events/interaction-create/checkin/messages/index.ts b/src/bot/events/interaction-create/checkin/messages/index.ts index b491cf6..521753b 100644 --- a/src/bot/events/interaction-create/checkin/messages/index.ts +++ b/src/bot/events/interaction-create/checkin/messages/index.ts @@ -19,14 +19,14 @@ export class CheckinMessage extends DiscordAssert { static override readonly MSG = { ...DiscordAssert.MSG, - CheckinSuccess: (todo: string) => ` + CheckinSuccess: (guildName: string, todo: string) => ` # โœ… Check-In Baru Terdeteksi! *Kindly take a look and do a review for this one, <@&${FLAMEWARDEN_ROLE}>* โ‹†๏ฝกหš โ˜๏ธŽ หš๏ฝกโ‹†๏ฝกหšโ˜ฝหš๏ฝกโ‹† ${todo} -> ${DUMMY.FOOTER}`, +> ${DUMMY.FOOTER(guildName)}`, GrinderDetails: (checkin: Checkin, lastCheckin?: Checkin) => ` โœจโ”€โ”€โ”€โ”€โ”€โœจ/โœจโ”โ”โ”โ”โœจ diff --git a/src/bot/events/interaction-create/checkin/validators/index.ts b/src/bot/events/interaction-create/checkin/validators/index.ts index 3d983ba..72c4fc9 100644 --- a/src/bot/events/interaction-create/checkin/validators/index.ts +++ b/src/bot/events/interaction-create/checkin/validators/index.ts @@ -11,7 +11,7 @@ import { AURA_FARMING_CHANNEL, CHECKIN_CHANNEL, GRINDER_ROLE } from '@config/dis import { SubmittedCheckinError } from '@events/message-reaction-add/checkin/handlers/submitted' import { createEmbed, decodeSnowflakes, encodeSnowflake, getCustomId } from '@utils/component' import { isDateToday, isDateYesterday } from '@utils/date' -import { DiscordAssert, getChannelOrThread, sendAsBot } from '@utils/discord' +import { DiscordAssert, getChannel, sendAsBot } from '@utils/discord' import { attachNewGrindRole, getGrindRoleByStreakCount } from '@utils/discord/roles' import { DUMMY } from '@utils/placeholder' import { ActionRowBuilder, ButtonBuilder, ButtonStyle, messageLink, PermissionsBitField } from 'discord.js' @@ -155,7 +155,7 @@ export class Checkin extends CheckinMessage { return const hasGrindRole = this.isMemberHasRole(member, newRole.id) - const channel = await getChannelOrThread(guild, AURA_FARMING_CHANNEL) as TextChannel + const channel = await getChannel(guild, AURA_FARMING_CHANNEL) as TextChannel this.assertChannel(channel) if (!hasGrindRole) { @@ -166,7 +166,7 @@ export class Checkin extends CheckinMessage { }) } else { - const checkinChannel = await getChannelOrThread(guild, CHECKIN_CHANNEL) as TextChannel + const checkinChannel = await getChannel(guild, CHECKIN_CHANNEL) as TextChannel await sendAsBot(null, checkinChannel, { content: `Hey, <@${member.id}>. You already have <@&${newRole.id}>`, allowedMentions: { users: [member.id], roles: [] }, @@ -516,7 +516,7 @@ export class Checkin extends CheckinMessage { `๐ŸŽ‰ *Check-In* Berhasil`, this.MSG.CheckinSuccessToMember(checkin), DUMMY.COLOR, - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(member.guild.name) }, ) await member.send({ embeds: [embed] }) @@ -531,7 +531,7 @@ export class Checkin extends CheckinMessage { `โš ๏ธ *Check-In* Ditolak`, this.MSG.CheckinRejected(flamewarden, checkin), '#D9534F', - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(member.guild.name) }, ) break @@ -540,7 +540,7 @@ export class Checkin extends CheckinMessage { `๐Ÿ”ฅ *Check-In* Disetujui`, this.MSG.CheckinApproved(flamewarden, checkin), '#4CAF50', - { text: DUMMY.FOOTER }, + { text: DUMMY.FOOTER(member.guild.name) }, ) break diff --git a/src/bot/events/interaction-create/embed/handlers/role-grant-create-modal.ts b/src/bot/events/interaction-create/embed/handlers/role-grant-create-modal.ts index 4a1f719..8faed0f 100644 --- a/src/bot/events/interaction-create/embed/handlers/role-grant-create-modal.ts +++ b/src/bot/events/interaction-create/embed/handlers/role-grant-create-modal.ts @@ -2,7 +2,7 @@ import type { TextChannel } from 'discord.js' import { EVENT_PATH } from '@events/index' import { registerInteractionHandler } from '@events/interaction-create/registry' import { createEmbed, encodeSnowflake, generateCustomId, getCustomId } from '@utils/component' -import { getChannelOrThread, getRole, sendAsBot, sendReply } from '@utils/discord' +import { getChannel, getRole, sendAsBot, sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { getModuleName } from '@utils/io' import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js' @@ -31,7 +31,7 @@ registerInteractionHandler({ throw new EmbedRoleGrantModalError(RoleGrantCreate.ERR.NotGuild) const { channelId, roleId, buttonName } = RoleGrantCreate.getModalId(interaction, interaction.customId) - const channel = await getChannelOrThread(interaction.guild, channelId) as TextChannel + const channel = await getChannel(interaction.guild, channelId) as TextChannel RoleGrantCreate.assertChannel(channel) RoleGrantCreate.assertMissPerms(interaction.client.user, channel) const role = await getRole(interaction.guild, roleId) diff --git a/src/bot/events/interaction-create/message/handlers/send-modal.ts b/src/bot/events/interaction-create/message/handlers/send-modal.ts index 2f66cae..7347be4 100644 --- a/src/bot/events/interaction-create/message/handlers/send-modal.ts +++ b/src/bot/events/interaction-create/message/handlers/send-modal.ts @@ -2,7 +2,7 @@ import type { Attachment, TextChannel } from 'discord.js' import { EVENT_PATH } from '@events/index' import { registerInteractionHandler } from '@events/interaction-create/registry' import { generateCustomId, tempStore } from '@utils/component' -import { getChannelOrThread, sendAsBot, sendReply } from '@utils/discord' +import { getChannel, sendAsBot, sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { getModuleName } from '@utils/io' import { Send } from '../validators/send' @@ -29,7 +29,7 @@ registerInteractionHandler({ throw new SendModalError(Send.ERR.NotGuild) const { channelId, tempToken } = Send.getModalId(interaction, interaction.customId) - const channel = await getChannelOrThread(interaction.guild, channelId) as TextChannel + const channel = await getChannel(interaction.guild, channelId) as TextChannel Send.assertChannel(channel) Send.assertMissPerms(interaction.client.user, channel) const attachments = tempStore.get(tempToken) as Attachment[] diff --git a/src/config/discord.ts b/src/config/discord.ts index 8e031c1..42ebd41 100644 --- a/src/config/discord.ts +++ b/src/config/discord.ts @@ -6,6 +6,7 @@ export const GRIND_ASHES_CHANNEL = '1405165926600409148' export const WARDEN_DUTY_CHANNEL = '1404086708051509318' export const AUDIT_FLAME_CHANNEL = '1447816805329539162' export const IGNITE_PATH_CHANNEL = '1405159743596793997' +export const BEARER_LOUNGE_CHANNEL = '1404087168552403044' export interface GrindRole { name?: string diff --git a/src/types/placeholder.d.ts b/src/types/placeholder.d.ts index 315b8d7..a3d6bf7 100644 --- a/src/types/placeholder.d.ts +++ b/src/types/placeholder.d.ts @@ -2,6 +2,6 @@ export interface PlaceholderDummy { TITLE: string DESC: string COLOR: string - FOOTER: string + FOOTER: (guildName: string) => string MARKDOWN: string } diff --git a/src/utils/component.ts b/src/utils/component.ts index cebe097..8ec8429 100644 --- a/src/utils/component.ts +++ b/src/utils/component.ts @@ -1,6 +1,6 @@ import type { Checkin } from '@type/checkin' import type { DiscordCustomIdMetadata } from '@type/discord-component' -import type { EmbedFooterOptions } from 'discord.js' +import type { EmbedAuthorOptions, EmbedFooterOptions } from 'discord.js' import { ALPHABETS, CUSTOM_ID_SEPARATOR, SNOWFLAKE_MARKER } from '@constants' import { EmbedBuilder, LabelBuilder, ModalBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextInputBuilder, TextInputStyle } from 'discord.js' import { parseHexColor } from './color' @@ -73,10 +73,13 @@ export const getTempToken = () => Math.random().toString(36).slice(2, 8) export const tempStore = new Map() export function createEmbed( - title?: string | null | undefined, - desc?: string | null | undefined, - color?: string | null, - footer?: EmbedFooterOptions | null | undefined, + title: string | null | undefined = null, + desc: string | null | undefined = null, + color: string | null = null, + footer: EmbedFooterOptions | null | undefined = null, + author: EmbedAuthorOptions | null | undefined = null, + thumbnail: string | null | undefined = null, + image: string | null | undefined = null, date: boolean = true, ): EmbedBuilder { const embed = new EmbedBuilder() @@ -89,10 +92,16 @@ export function createEmbed( embed.setDescription(desc) if (parsedColor) embed.setColor(parsedColor) - if (date) - embed.setTimestamp(new Date()) if (footer) embed.setFooter(footer) + if (author) + embed.setAuthor(author) + if (thumbnail) + embed.setThumbnail(thumbnail) + if (image) + embed.setImage(image) + if (date) + embed.setTimestamp(new Date()) return embed } diff --git a/src/utils/discord/assert.ts b/src/utils/discord/assert.ts index 105be25..83bda9c 100644 --- a/src/utils/discord/assert.ts +++ b/src/utils/discord/assert.ts @@ -1,7 +1,7 @@ import type { ClientUser, Guild, GuildMember, Message, Role, TextChannel, ThreadAutoArchiveDuration, ThreadChannel } from 'discord.js' import { getTempToken, parseMessageLink, tempStore } from '@utils/component' import { ChannelType, PermissionsBitField } from 'discord.js' -import { getBotPerms, getChannelOrThread, getMissPerms } from '.' +import { getBotPerms, getChannel, getMissPerms } from '.' import { DiscordBaseError } from './error' import { DiscordMessage } from './message' @@ -102,7 +102,7 @@ export class DiscordAssert extends DiscordMessage { throw new DiscordAssertError(this.ERR.AllowedChannel(channelId)) } - const channel = await getChannelOrThread(guild, channelId) as TextChannel + const channel = await getChannel(guild, channelId) as TextChannel this.assertChannel(channel) return channel @@ -126,7 +126,7 @@ export class DiscordAssert extends DiscordMessage { } static async assertThreadUnderChannel(guild: Guild, currentChannelId: string, parentChannel: TextChannel) { - const thread = await getChannelOrThread(guild, currentChannelId) as ThreadChannel + const thread = await getChannel(guild, currentChannelId) as ThreadChannel if (!thread.isThread()) throw new DiscordAssertError(this.ERR.MustBeThread(parentChannel.id)) diff --git a/src/utils/discord/index.ts b/src/utils/discord/index.ts index 7877fc1..a393b61 100644 --- a/src/utils/discord/index.ts +++ b/src/utils/discord/index.ts @@ -1,7 +1,7 @@ import type { Attachment, ChatInputCommandInteraction, ClientUser, Guild, GuildMember, Interaction, InteractionDeferReplyOptions, InteractionReplyOptions, MessageCreateOptions, PermissionsBitField, Role, TextChannel, ThreadChannel } from 'discord.js' import { MessageFlags } from 'discord.js' -export async function getChannelOrThread(guild: Guild, id: string, isThread: boolean = false): Promise { +export async function getChannel(guild: Guild, id: string, isThread: boolean = false): Promise { if (isThread) { return guild!.channels.cache.get(id) as TextChannel ?? await guild!.channels.fetch(id).then(channel => channel as TextChannel) } diff --git a/src/utils/placeholder.ts b/src/utils/placeholder.ts index 175d181..0408f87 100644 --- a/src/utils/placeholder.ts +++ b/src/utils/placeholder.ts @@ -4,6 +4,6 @@ export const DUMMY: PlaceholderDummy = { TITLE: 'Pengumuman Penting', DESC: 'Halo, teman-teman! Hari ini...', COLOR: '#FF7518', - FOOTER: 'Aksaria โ€ข Where discipline meets destiny', + FOOTER: (guildName: string) => `${guildName} โ€ข Where discipline meets destiny`, MARKDOWN: `Kamu dapat menggunakan Discord formatting untuk memperindah atau memperjelas rangkaian kata yang telah kamu buat. Untuk panduan lengkap, silakan merujuk ke [Markdown Text 101](https://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline).`, }