feat(discord-bot): REPORTS_JSON for reports migration
This commit is contained in:
@@ -33,4 +33,5 @@ export const env = parseEnv({
|
||||
.transform((x) => x.split(",").map((x) => x.trim()))
|
||||
.optional()
|
||||
.default([]),
|
||||
REPORTS_JSON: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -4,12 +4,14 @@ import { env } from "~/env";
|
||||
import { questCheckCron } from "~/quests";
|
||||
import { trackingCron } from "~/tracking";
|
||||
import { commands } from "~/commands";
|
||||
import { handleReportButton, handleReportModal, handleEditButton, handleDeleteButton, handleEditModal, REPORT_BUTTON_ID, REPORT_MODAL_ID, REPORT_EDIT_BUTTON_PREFIX, REPORT_DELETE_BUTTON_PREFIX, REPORT_EDIT_MODAL_PREFIX } from "~/reporting";
|
||||
import { handleReportButton, handleReportModal, handleEditButton, handleDeleteButton, handleEditModal, importReports, REPORT_BUTTON_ID, REPORT_MODAL_ID, REPORT_EDIT_BUTTON_PREFIX, REPORT_DELETE_BUTTON_PREFIX, REPORT_EDIT_MODAL_PREFIX } from "~/reporting";
|
||||
|
||||
const onReady = async (client: Client<true>) => {
|
||||
logger.info(`Client ready`);
|
||||
logger.info(`Connected as @${client.user.username}`);
|
||||
|
||||
await importReports(client);
|
||||
|
||||
await questCheckCron(client);
|
||||
setInterval(() => void questCheckCron(client), env.WOV_FETCH_INTERVAL);
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import {
|
||||
EmbedBuilder, FileUploadBuilder, LabelBuilder, MessageFlags, ModalBuilder, TextInputBuilder, TextInputStyle,
|
||||
type ButtonInteraction, type Client, type FileUploadModalData, type ModalSubmitInteraction, type TextChannel,
|
||||
} from "discord.js";
|
||||
import { z } from "zod";
|
||||
import { createLogger } from "@lbf-bot/utils";
|
||||
import { db, tables, eq } from "@lbf-bot/database";
|
||||
import { db, tables, eq, and } from "@lbf-bot/database";
|
||||
import { env } from "~/env";
|
||||
import { searchPlayer } from "~/wov";
|
||||
|
||||
@@ -167,8 +168,7 @@ export const handleReportModal = async (interaction: ModalSubmitInteraction, cli
|
||||
const [inserted] = await db
|
||||
.insert(tables.reports)
|
||||
.values({
|
||||
reporterId: interaction.user.id,
|
||||
reporterUsername: interaction.user.username,
|
||||
reporterId: interaction.user.id,
|
||||
playerName,
|
||||
playerId: player.id,
|
||||
reason,
|
||||
@@ -242,6 +242,104 @@ export const handleDeleteButton = async (interaction: ButtonInteraction, reportI
|
||||
await interaction.editReply({ content: "Signalement supprimé." });
|
||||
};
|
||||
|
||||
const importEntrySchema = z.object({
|
||||
username: z.string(),
|
||||
author_id: z.string(),
|
||||
reason: z.string(),
|
||||
date: z.string(),
|
||||
});
|
||||
|
||||
const parseImportDate = (date: string): Date => {
|
||||
const [month, day, year] = date.split("/").map(Number);
|
||||
return new Date(2000 + year, month - 1, day);
|
||||
};
|
||||
|
||||
export const importReports = async (client: Client<true>) => {
|
||||
if (!env.REPORTS_JSON) return;
|
||||
|
||||
let rawJson: unknown;
|
||||
try {
|
||||
rawJson = JSON.parse(env.REPORTS_JSON);
|
||||
} catch {
|
||||
logger.error("REPORTS_JSON is not valid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = z.array(importEntrySchema).safeParse(rawJson);
|
||||
if (!parsed.success) {
|
||||
logger.error("Invalid REPORTS_JSON:", parsed.error.issues);
|
||||
return;
|
||||
}
|
||||
|
||||
const reportChannel = await client.channels.fetch(env.DISCORD_REPORT_CHANNEL);
|
||||
if (reportChannel?.type !== ChannelType.GuildText) {
|
||||
logger.error("Invalid 'DISCORD_REPORT_CHANNEL' for import");
|
||||
return;
|
||||
}
|
||||
|
||||
let imported = 0;
|
||||
let skipped = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const entry of parsed.data) {
|
||||
const player = await searchPlayer(entry.username);
|
||||
if (!player) {
|
||||
logger.warn(`Import: player not found, skipping — ${entry.username}`);
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = await db.query.reports.findFirst({
|
||||
columns: { id: true },
|
||||
where: and(eq(tables.reports.playerId, player.id), eq(tables.reports.reason, entry.reason)),
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const [inserted] = await db
|
||||
.insert(tables.reports)
|
||||
.values({
|
||||
reporterId: entry.author_id,
|
||||
playerName: entry.username,
|
||||
playerId: player.id,
|
||||
reason: entry.reason,
|
||||
createdAt: parseImportDate(entry.date),
|
||||
})
|
||||
.returning({ id: tables.reports.id });
|
||||
|
||||
const reportMessage = await reportChannel.send({
|
||||
content: "─────────────────────────────────",
|
||||
embeds: [buildReportEmbed({ playerName: entry.username, playerId: player.id, reason: entry.reason, reporterId: entry.author_id })],
|
||||
components: [reportActionRow(inserted.id)],
|
||||
});
|
||||
|
||||
const messageLink = `https://discord.com/channels/${reportChannel.guild.id}/${reportChannel.id}/${reportMessage.id}`;
|
||||
const screenshotsMessage = await reportChannel.send({ content: "-# Pas de screenshots" });
|
||||
|
||||
await db.update(tables.reports)
|
||||
.set({ messageLink, screenshotsMessageId: screenshotsMessage.id })
|
||||
.where(eq(tables.reports.id, inserted.id));
|
||||
|
||||
imported++;
|
||||
} catch (error) {
|
||||
logger.error(`Import: failed for ${entry.username}:`, error);
|
||||
failed++;
|
||||
}
|
||||
|
||||
// avoid spamming too much the APIs
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
if (imported > 0 && imported % 50 === 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 60000));
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Import complete: ${imported} imported, ${skipped} skipped, ${failed} not found`);
|
||||
};
|
||||
|
||||
export const handleEditModal = async (interaction: ModalSubmitInteraction, client: Client, reportId: string, channelId: string, messageId: string) => {
|
||||
const reason = interaction.fields.getTextInputValue("reason");
|
||||
const { attachments, screenshotUrls } = extractScreenshots(interaction.fields);
|
||||
|
||||
Reference in New Issue
Block a user