From 465a45e6a94039295d637e23bac50cd3528bc98e Mon Sep 17 00:00:00 2001 From: Ali Hammoud Date: Sun, 21 Jun 2026 11:56:30 +0300 Subject: [PATCH 1/2] refractor: update regex and remove old structure --- src/features/showcase/util.ts | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/features/showcase/util.ts b/src/features/showcase/util.ts index b39d64d..1d7daf6 100644 --- a/src/features/showcase/util.ts +++ b/src/features/showcase/util.ts @@ -18,33 +18,21 @@ export type ShowcaseMessageData = { authorId: string; }; -export const parseShowcaseMessage = (content: string): ShowcaseMessageData => { - const isOldStructure = content.startsWith('## Project Name:'); - if (isOldStructure) { - const [header = '', ...descriptionParts] = content.split(/\n\n+/); - const headerLines = header.split('\n'); +const FOOTER_REGEX = /\n+\*\*Author:\*\* <@(\d+)>(?:\n\*\*Link:\*\* (.+))?\s*$/; - const authorLine = headerLines.find((line) => - line.startsWith('**Author:** ') - ); - const authorId = authorLine?.match(/<@(\d+)>/)?.[1] ?? ''; - const linkLine = headerLines.find((line) => line.startsWith('**Link:** ')); - const link = linkLine?.replace(/^\*\*Link:\*\*\s*/, '') ?? ''; - const description = descriptionParts.join('\n\n').trim(); +export const parseShowcaseMessage = (content: string): ShowcaseMessageData => { + const footerMatch = content.match(FOOTER_REGEX); - return { link, description, authorId }; + if (!footerMatch) { + return { authorId: '', link: '', description: content.trim() }; } - const authorMatch = content.match(/\*\*Author:\*\* <@(\d+)>/); - const linkMatch = content.match(/\*\*Link:\*\* (.+)/); - const description = content - .replace(/\*\*Author:\*\* <@\d+>/, '') - .replace(/\*\*Link:\*\* .+/, '') - .trim(); + const [, authorId, link] = footerMatch; + const description = content.slice(0, footerMatch.index).trim(); return { - authorId: authorMatch?.[1] ?? '', - link: linkMatch?.[1] ?? '', + authorId: authorId ?? '', + link: link?.trim() ?? '', description, }; }; From 249fbc7b9a7a5cd107eef01c6a687829bb6010ad Mon Sep 17 00:00:00 2001 From: Ali Hammoud Date: Sun, 21 Jun 2026 11:56:55 +0300 Subject: [PATCH 2/2] refractor: remove temp showcase command --- src/common/commands/index.ts | 2 - src/features/showcase/temp-restructure.ts | 101 ---------------------- 2 files changed, 103 deletions(-) delete mode 100644 src/features/showcase/temp-restructure.ts diff --git a/src/common/commands/index.ts b/src/common/commands/index.ts index d93088e..568fd1e 100644 --- a/src/common/commands/index.ts +++ b/src/common/commands/index.ts @@ -8,7 +8,6 @@ import { createShowcaseCommand } from '@/features/showcase/create-showcase.js'; import { sendShowcasePinnedMessage } from '@/features/showcase/send-pinned-message.js'; import { tipsCommands } from '@/features/tips/index.js'; import type { Command } from './types.js'; -import { showcaseTempRestructureCommand } from '@/features/showcase/temp-restructure.js'; export const commands = new Map( [ @@ -21,7 +20,6 @@ export const commands = new Map( publicGuidesCommand, createShowcaseCommand, sendShowcasePinnedMessage, - showcaseTempRestructureCommand, ] .flat() .map((command) => [command.data.name, command]) diff --git a/src/features/showcase/temp-restructure.ts b/src/features/showcase/temp-restructure.ts deleted file mode 100644 index 3d81aa2..0000000 --- a/src/features/showcase/temp-restructure.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { createSlashCommand } from '@/common/commands/create-commands.js'; -import { config } from '@/env.js'; -import { PermissionFlagsBits, PermissionsBitField } from 'discord.js'; -import { createShowcaseMessageContent, parseShowcaseMessage } from './util.js'; - -type ThreadResult = { id: string; status: 'changed' | 'skipped' }; - -export const showcaseTempRestructureCommand = createSlashCommand({ - data: { - name: 'showcase_temp_restructure', - description: 'Restructure showcase messages to the new format', - default_member_permissions: new PermissionsBitField( - PermissionFlagsBits.ModerateMembers - ).toJSON(), - }, - execute: async (interaction) => { - await interaction.deferReply(); - const showcaseChannel = interaction.guild?.channels.cache.get( - config.channelIds.showcase - ); - if (!showcaseChannel?.isThreadOnly()) { - await interaction.editReply({ - content: 'Showcase channel not found or is not a thread-only channel.', - }); - return; - } - const [activePosts, archivedPosts] = await Promise.all([ - showcaseChannel.threads.fetchActive(), - showcaseChannel.threads.fetchArchived(), - ]); - const allPosts = new Map([ - ...activePosts.threads, - ...archivedPosts.threads, - ]); - - const results = await Promise.allSettled( - Array.from(allPosts.values()).map( - async (thread): Promise => { - const message = await thread.fetchStarterMessage(); - if (!message) { - return { id: thread.id, status: 'skipped' }; - } - if (message.content.startsWith('## Project Name:')) { - const { link, description, authorId } = parseShowcaseMessage( - message.content - ); - const newContent = createShowcaseMessageContent({ - link, - description, - authorId, - }); - await message.edit({ content: newContent }); - return { id: thread.id, status: 'changed' }; - } - return { id: thread.id, status: 'skipped' }; - } - ) - ); - - const changedThreads: string[] = []; - const skippedThreads: string[] = []; - const failedThreads: { id: string; reason: unknown }[] = []; - - for (const [index, result] of results.entries()) { - if (result.status === 'rejected') { - const thread = Array.from(allPosts.values())[index]; - failedThreads.push({ id: thread.id, reason: result.reason }); - continue; - } - if (result.value.status === 'changed') { - changedThreads.push(result.value.id); - } else { - skippedThreads.push(result.value.id); - } - } - - if (failedThreads.length > 0) { - console.error( - 'Showcase restructure failures:', - failedThreads.map((f) => ({ id: f.id, reason: String(f.reason) })) - ); - } - - const sections = [ - `Restructured ${changedThreads.length} showcase message(s).`, - changedThreads.length > 0 - ? changedThreads.map((id) => `- <#${id}>`).join('\n') - : undefined, - `Skipped ${skippedThreads.length} thread(s) already in the new format.`, - failedThreads.length > 0 - ? `Failed to process ${failedThreads.length} thread(s):\n${failedThreads - .map((f) => `- <#${f.id}>`) - .join('\n')}` - : undefined, - ].filter((line): line is string => line !== undefined); - - await interaction.editReply({ - content: sections.join('\n'), - }); - }, -});