First upload
This commit is contained in:
129
scripts/map-batch-to-transcript.js
Normal file
129
scripts/map-batch-to-transcript.js
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Map batch tickets (TICKET: guild_channelId_suffix) to transcript channel messages.
|
||||
*
|
||||
* Connection:
|
||||
* - Batch line: TICKET: 798321161082896395_1423340928588054621_indiffe → channelId = 1423340928588054621.
|
||||
* - Transcript channel (🖥️│transcripts): each message is an embed with "Ticket Name: indifferentketchup🍅-claimed-7235".
|
||||
* - Embed does NOT include channel ID, so we match by (1) ticket name (when known) or (2) time: transcript posted when ticket closes.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/map-batch-to-transcript.js list [limit] -- fetch transcript messages, output CSV (messageId, created, ticket_name)
|
||||
* node scripts/map-batch-to-transcript.js find <channelId> -- find transcript message(s) likely for this ticket (by time window)
|
||||
*
|
||||
* Known mapping (from embed): 1423340928588054621 ↔ message 1423400708769579120 (Ticket: indifferentketchup🍅-claimed-7235).
|
||||
*/
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
|
||||
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||
require('dotenv').config({ path: path.join(__dirname, '../../.env') });
|
||||
|
||||
const TOKEN = process.env.MEMBER_BOT_TOKEN || process.env.DISCORD_BOT_TOKEN;
|
||||
const TRANSCRIPT_CHANNEL_ID = '1335424071227281520';
|
||||
const METRICS_CSV = path.join(__dirname, '../../Discord Ticket Transcripts/transcript_metrics_per_ticket.csv');
|
||||
|
||||
function getTicketNameFromEmbed(emb) {
|
||||
const f = emb.fields?.find((x) => x.name && x.name.toLowerCase().includes('ticket name'));
|
||||
return f ? f.value.trim() : null;
|
||||
}
|
||||
|
||||
async function fetchTranscriptMessages(client, limit = 100) {
|
||||
const channel = await client.channels.fetch(TRANSCRIPT_CHANNEL_ID).catch(() => null);
|
||||
if (!channel) return [];
|
||||
const cap = Math.min(limit, 100); // Discord API max 100 per request
|
||||
const messages = await channel.messages.fetch({ limit: cap });
|
||||
const out = [];
|
||||
for (const [, m] of messages) {
|
||||
const emb = m.embeds?.[0];
|
||||
const ticketName = emb ? getTicketNameFromEmbed(emb) : null;
|
||||
out.push({
|
||||
messageId: m.id,
|
||||
created: m.createdAt ? m.createdAt.toISOString() : m.createdTimestamp,
|
||||
createdTs: m.createdTimestamp,
|
||||
ticketName: ticketName || '',
|
||||
});
|
||||
}
|
||||
out.sort((a, b) => b.createdTs - a.createdTs);
|
||||
return out;
|
||||
}
|
||||
|
||||
function loadMetricsCsv() {
|
||||
if (!fs.existsSync(METRICS_CSV)) return [];
|
||||
const text = fs.readFileSync(METRICS_CSV, 'utf8');
|
||||
const lines = text.split(/\r?\n/).filter((l) => l.trim());
|
||||
const header = lines[0].split(',');
|
||||
const ticketIdIdx = header.indexOf('ticket_id');
|
||||
const lastTsIdx = header.indexOf('last_message_ts');
|
||||
if (ticketIdIdx === -1 || lastTsIdx === -1) return [];
|
||||
const rows = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const parts = lines[i].split(',');
|
||||
const ticketId = parts[ticketIdIdx];
|
||||
const lastTs = parseInt(parts[lastTsIdx], 10);
|
||||
if (!ticketId || !ticketId.includes('_')) continue;
|
||||
const channelId = ticketId.split('_')[1];
|
||||
if (channelId && !isNaN(lastTs)) rows.push({ ticketId, channelId, last_message_ts: lastTs });
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const cmd = process.argv[2];
|
||||
const arg = process.argv[3];
|
||||
|
||||
if (!TOKEN) {
|
||||
console.error('No bot token');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
client.once('ready', resolve);
|
||||
client.login(TOKEN).catch(reject);
|
||||
});
|
||||
|
||||
try {
|
||||
if (cmd === 'list') {
|
||||
const limit = Math.min(parseInt(arg, 10) || 100, 100);
|
||||
const list = await fetchTranscriptMessages(client, limit);
|
||||
console.log('transcript_message_id,created_iso,ticket_name');
|
||||
list.forEach((r) => console.log([r.messageId, r.created, r.ticketName].map((c) => `"${String(c).replace(/"/g, '""')}"`).join(',')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd === 'find' && arg) {
|
||||
const channelId = arg.trim();
|
||||
const metrics = loadMetricsCsv();
|
||||
const row = metrics.find((r) => r.channelId === channelId);
|
||||
const closeTs = row ? row.last_message_ts : null;
|
||||
const list = await fetchTranscriptMessages(client, 100);
|
||||
const windowMs = 2 * 60 * 60 * 1000; // ±2 hours
|
||||
const candidates = closeTs
|
||||
? list.filter((r) => Math.abs(r.createdTs - closeTs) <= windowMs)
|
||||
: list.slice(0, 20);
|
||||
console.log('Batch ticket channelId:', channelId);
|
||||
if (row) console.log('Ticket close time (last_message_ts):', closeTs, new Date(closeTs).toISOString());
|
||||
console.log('Transcript channel messages (candidates by time or recent):');
|
||||
candidates.forEach((r) => {
|
||||
const delta = closeTs != null ? (r.createdTs - closeTs) / 60000 : null;
|
||||
console.log(' ', r.messageId, r.created, r.ticketName || '(no name)', delta != null ? `delta ${delta.toFixed(0)} min` : '');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Usage: node scripts/map-batch-to-transcript.js list [limit]');
|
||||
console.log(' node scripts/map-batch-to-transcript.js find <channelId>');
|
||||
} finally {
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user