| const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); |
| const { Octokit } = require('@octokit/rest'); |
| const { createEmbed } = require('../utils/embeds'); |
| const { Colors } = require('../config'); |
|
|
| |
| |
| |
| |
| |
| |
| module.exports = { |
| async execute(client, message, args) { |
| const channelId = args[0]?.replace(/[<#>]/g, ''); |
| if (!channelId) { |
| return message.reply({ content: 'β Usage: `fix downloads <channel_id>`' }); |
| } |
|
|
| const guild = client.guilds.cache.first(); |
| const channel = await guild.channels.fetch(channelId).catch(() => null); |
| if (!channel) { |
| return message.reply({ content: 'β Channel not found.' }); |
| } |
|
|
| const statusMsg = await message.reply({ |
| embeds: [createEmbed({ |
| title: 'π§ Fixing Downloads', |
| description: `Scanning <#${channelId}> for broken GitHub links...`, |
| color: Colors.INFO |
| })] |
| }); |
|
|
| try { |
| const octokit = new Octokit({ auth: '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' }); |
| const [owner, repo] = 'APRK01/WSB-Storage'.split('/'); |
|
|
| |
| const assetMap = new Map(); |
| let page = 1; |
| let hasMore = true; |
|
|
| while (hasMore) { |
| const { data: releases } = await octokit.rest.repos.listReleases({ |
| owner, |
| repo, |
| per_page: 100, |
| page |
| }); |
|
|
| if (releases.length === 0) { |
| hasMore = false; |
| break; |
| } |
|
|
| for (const release of releases) { |
| for (const asset of release.assets) { |
| |
| assetMap.set(`${release.tag_name}/${asset.name}`, asset.id); |
| |
| assetMap.set(asset.browser_download_url, asset.id); |
| } |
| } |
|
|
| page++; |
| } |
|
|
| await statusMsg.edit({ |
| embeds: [createEmbed({ |
| title: 'π§ Fixing Downloads', |
| description: `Found **${assetMap.size}** GitHub assets. Now scanning messages in <#${channelId}>...`, |
| color: Colors.INFO |
| })] |
| }); |
|
|
| |
| let fixedCount = 0; |
| let skippedCount = 0; |
| let lastId = null; |
| let scannedCount = 0; |
|
|
| while (true) { |
| const options = { limit: 100 }; |
| if (lastId) options.before = lastId; |
|
|
| const messages = await channel.messages.fetch(options); |
| if (messages.size === 0) break; |
|
|
| for (const [, msg] of messages) { |
| scannedCount++; |
|
|
| |
| if (msg.author.id !== client.user.id) continue; |
| if (!msg.components || msg.components.length === 0) continue; |
|
|
| |
| let needsFix = false; |
| const newRows = []; |
|
|
| for (const row of msg.components) { |
| const newComponents = []; |
|
|
| for (const component of row.components) { |
| |
| if (component.type === 2 && component.style === ButtonStyle.Link && |
| component.url && component.url.includes('APRK01/WSB-Storage')) { |
|
|
| |
| const assetId = assetMap.get(component.url); |
|
|
| if (assetId) { |
| |
| newComponents.push( |
| new ButtonBuilder() |
| .setCustomId(`dl_${assetId}`) |
| .setLabel(component.label || 'π₯ Download Drop') |
| .setStyle(ButtonStyle.Success) |
| ); |
| needsFix = true; |
| } else { |
| |
| |
| const urlMatch = component.url.match(/\/releases\/download\/([^/]+)\/(.+)$/); |
| if (urlMatch) { |
| const lookupKey = `${decodeURIComponent(urlMatch[1])}/${decodeURIComponent(urlMatch[2])}`; |
| const foundId = assetMap.get(lookupKey); |
|
|
| if (foundId) { |
| newComponents.push( |
| new ButtonBuilder() |
| .setCustomId(`dl_${foundId}`) |
| .setLabel(component.label || 'π₯ Download Drop') |
| .setStyle(ButtonStyle.Success) |
| ); |
| needsFix = true; |
| } else { |
| |
| newComponents.push(ButtonBuilder.from(component)); |
| skippedCount++; |
| } |
| } else { |
| newComponents.push(ButtonBuilder.from(component)); |
| skippedCount++; |
| } |
| } |
| } else if (component.type === 2 && component.style !== ButtonStyle.Link) { |
| |
| newComponents.push(ButtonBuilder.from(component)); |
| } else { |
| newComponents.push(ButtonBuilder.from(component)); |
| } |
| } |
|
|
| newRows.push(new ActionRowBuilder().addComponents(newComponents)); |
| } |
|
|
| if (needsFix) { |
| try { |
| await msg.edit({ components: newRows }); |
| fixedCount++; |
| } catch (editErr) { |
| console.error(`[Fix Downloads] Failed to edit message ${msg.id}:`, editErr.message); |
| skippedCount++; |
| } |
| } |
| } |
|
|
| lastId = messages.last().id; |
|
|
| |
| if (scannedCount % 200 === 0) { |
| await statusMsg.edit({ |
| embeds: [createEmbed({ |
| title: 'π§ Fixing Downloads', |
| description: `Scanned **${scannedCount}** messages... Fixed **${fixedCount}** so far.`, |
| color: Colors.INFO |
| })] |
| }).catch(() => { }); |
| } |
| } |
|
|
| await statusMsg.edit({ |
| embeds: [createEmbed({ |
| title: 'β
Downloads Fixed!', |
| description: [ |
| `**Channel:** <#${channelId}>`, |
| `**Messages scanned:** ${scannedCount}`, |
| `**Buttons fixed:** ${fixedCount}`, |
| skippedCount > 0 ? `**Skipped (no match):** ${skippedCount}` : '', |
| '', |
| fixedCount > 0 |
| ? '> All download buttons now use the private ephemeral system. π' |
| : '> No broken buttons found β everything looks good!', |
| ].filter(Boolean).join('\n'), |
| color: Colors.SUCCESS |
| })] |
| }); |
|
|
| } catch (err) { |
| console.error('[Fix Downloads Error]', err); |
| await statusMsg.edit({ |
| embeds: [createEmbed({ |
| title: 'β Fix Failed', |
| description: `Error: ${err.message}`, |
| color: Colors.ACCENT |
| })] |
| }); |
| } |
| } |
| }; |
|
|