#!/usr/bin/env node /** * One-shot backfill for Ticket.creatorId on Discord-originated tickets. * * Modal-created tickets (`discord-${ts}-${userId}`): tail segment is the user ID — extract it. * Context-menu tickets (`discord-msg-${ts}-${msgId}`): tail segment is the *message* ID, not the * user ID. Set creatorId = null and let runtime code fall through to the default-name path. * Recovering these would require a Discord API fetch per message, which is unreliable for * already-deleted ticket channels. * * Idempotent: skips tickets that already have creatorId set. * * Usage: * node scripts/backfill-creatorId.js # dry-run, prints summary only * node scripts/backfill-creatorId.js --apply # writes */ require('dotenv').config(); const { connectMongoDB, closeMongoDB, mongoose } = require('../db-connection'); const APPLY = process.argv.includes('--apply'); const MODAL_RE = /^discord-\d+-(\d{17,20})$/; async function main() { if (!process.env.MONGODB_URI) { console.error('MONGODB_URI not set'); process.exit(1); } await connectMongoDB(process.env.MONGODB_URI); const Ticket = mongoose.model('Ticket'); const candidates = await Ticket.find({ gmailThreadId: /^discord-/, creatorId: { $in: [null, undefined, ''] } }).select('gmailThreadId creatorId').lean(); let modalHits = 0; let msgSkipped = 0; let unknown = 0; const ops = []; for (const t of candidates) { const id = t.gmailThreadId; const modalMatch = id.match(MODAL_RE); if (modalMatch) { modalHits++; ops.push({ updateOne: { filter: { _id: t._id }, update: { $set: { creatorId: modalMatch[1] } } } }); continue; } if (id.startsWith('discord-msg-')) { msgSkipped++; continue; } unknown++; } console.log(`Scanned ${candidates.length} Discord-originated tickets without creatorId.`); console.log(` Modal-pattern recoverable: ${modalHits}`); console.log(` Context-menu (unrecoverable, leaving null): ${msgSkipped}`); console.log(` Unknown shape: ${unknown}`); if (!APPLY) { console.log('\nDry-run only. Re-run with --apply to write changes.'); await closeMongoDB(); return; } if (ops.length === 0) { console.log('Nothing to write.'); await closeMongoDB(); return; } const res = await Ticket.bulkWrite(ops, { ordered: false }); console.log(`Wrote ${res.modifiedCount} updates.`); await closeMongoDB(); } main().catch(err => { console.error(err); process.exit(1); });