This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233// SPDX-License-Identifier: AGPL-3.0-or-later
import {ReportAlreadyResolvedError} from '@fluxer/errors/src/domains/moderation/ReportAlreadyResolvedError';import {UnknownReportError} from '@fluxer/errors/src/domains/moderation/UnknownReportError';import type {ChannelID, MessageID, ReportID, UserID} from '../BrandedTypes';import {createChannelID, createGuildID, createMessageID, createReportID, createUserID} from '../BrandedTypes';import {fetchMany, fetchOne, upsertOne} from '../database/CassandraQueryExecution';import {Db} from '../database/CassandraTypes';import type { DSAReportEmailVerificationRow, DSAReportTicketRow, MessageReportSubmissionByReporterRow,} from '../database/types/ReportTypes';import { DSAReportEmailVerifications, DSAReportTickets, IARSubmissions, MessageReportSubmissionsByReporter,} from '../Tables';import type { IARMessageContext, IARMessageContextRow, IARSubmission, IARSubmissionRow, IReportRepository,} from './IReportRepository';
const GET_REPORT_QUERY = IARSubmissions.select({ where: IARSubmissions.where.eq('report_id'), limit: 1,});const createFetchAllReportsPaginatedQuery = (limit: number) => IARSubmissions.select({ where: IARSubmissions.where.tokenGt('report_id', 'last_report_id'), limit, });const GET_DSA_EMAIL_VERIFICATION_QUERY = DSAReportEmailVerifications.select({ where: DSAReportEmailVerifications.where.eq('email_lower'), limit: 1,});const GET_DSA_REPORT_TICKET_QUERY = DSAReportTickets.select({ where: DSAReportTickets.where.eq('ticket'), limit: 1,});const GET_MESSAGE_REPORT_SUBMISSION_BY_REPORTER_QUERY = MessageReportSubmissionsByReporter.select({ where: [ MessageReportSubmissionsByReporter.where.eq('reporter_id'), MessageReportSubmissionsByReporter.where.eq('channel_id'), MessageReportSubmissionsByReporter.where.eq('message_id'), ], limit: 1,});
function createFetchAllReportsFirstPageQuery(limit: number) { return IARSubmissions.select({limit});}
export class ReportRepository implements IReportRepository { async createReport(data: IARSubmissionRow): Promise<IARSubmission> { await upsertOne(IARSubmissions.insert(data)); return this.mapRowToSubmission(data); }
async reserveMessageReportByReporter(data: MessageReportSubmissionByReporterRow): Promise<boolean> { const existing = await fetchOne<MessageReportSubmissionByReporterRow>( GET_MESSAGE_REPORT_SUBMISSION_BY_REPORTER_QUERY.bind({ reporter_id: data.reporter_id, channel_id: data.channel_id, message_id: data.message_id, }), ); if (existing) { return false; } await upsertOne(MessageReportSubmissionsByReporter.insert(data)); return true; }
async deleteMessageReportByReporter(reporterId: UserID, channelId: ChannelID, messageId: MessageID): Promise<void> { await MessageReportSubmissionsByReporter.deleteByPk({ reporter_id: reporterId, channel_id: channelId, message_id: messageId, }); }
async getReport(reportId: ReportID): Promise<IARSubmission | null> { const row = await fetchOne<IARSubmissionRow>(GET_REPORT_QUERY.bind({report_id: reportId})); return row ? this.mapRowToSubmission(row) : null; }
async resolveReport( reportId: ReportID, resolvedByAdminId: UserID, publicComment: string | null, auditLogReason: string | null, ): Promise<IARSubmission> { const report = await this.getReport(reportId); if (!report) { throw new UnknownReportError(); } if (report.status !== 0) { throw new ReportAlreadyResolvedError(); } const resolvedAt = new Date(); const newStatus = 1; await upsertOne( IARSubmissions.patchByPk( {report_id: reportId}, { resolved_at: Db.set(resolvedAt), resolved_by_admin_id: Db.set(resolvedByAdminId), public_comment: Db.set(publicComment), audit_log_reason: Db.set(auditLogReason), status: Db.set(newStatus), }, ), ); return { ...report, resolvedAt, resolvedByAdminId, publicComment, auditLogReason, status: newStatus, }; }
private mapRowToSubmission(row: IARSubmissionRow): IARSubmission { return { reportId: createReportID(row.report_id), reporterId: row.reporter_id ? createUserID(row.reporter_id) : null, reporterEmail: row.reporter_email, reporterFullLegalName: row.reporter_full_legal_name, reporterCountryOfResidence: row.reporter_country_of_residence, reportedAt: row.reported_at, status: row.status, reportType: row.report_type, category: row.category, additionalInfo: row.additional_info, reportedUserId: row.reported_user_id ? createUserID(row.reported_user_id) : null, reportedUserAvatarHash: row.reported_user_avatar_hash, reportedGuildId: row.reported_guild_id ? createGuildID(row.reported_guild_id) : null, reportedGuildName: row.reported_guild_name, reportedGuildIconHash: row.reported_guild_icon_hash, reportedMessageId: row.reported_message_id ? createMessageID(row.reported_message_id) : null, reportedChannelId: row.reported_channel_id ? createChannelID(row.reported_channel_id) : null, reportedChannelName: row.reported_channel_name, messageContext: row.message_context ? this.mapMessageContext(row.message_context) : null, guildContextId: row.guild_context_id ? createGuildID(row.guild_context_id) : null, resolvedAt: row.resolved_at, resolvedByAdminId: row.resolved_by_admin_id ? createUserID(row.resolved_by_admin_id) : null, publicComment: row.public_comment, auditLogReason: row.audit_log_reason, reportedGuildInviteCode: row.reported_guild_invite_code, reportedGuildNsfw: row.reported_guild_nsfw ?? null, reportedGuildContentWarningLevel: row.reported_guild_content_warning_level ?? null, reportedGuildContentWarningText: row.reported_guild_content_warning_text ?? null, reportedChannelNsfwOverride: row.reported_channel_nsfw_override ?? null, reportedChannelContentWarningLevel: row.reported_channel_content_warning_level ?? null, reportedChannelContentWarningText: row.reported_channel_content_warning_text ?? null, reportedChannelEffectiveNsfw: row.reported_channel_effective_nsfw ?? null, reportedChannelEffectiveContentWarningLevel: row.reported_channel_effective_content_warning_level ?? null, reportedChannelEffectiveContentWarningText: row.reported_channel_effective_content_warning_text ?? null, }; }
async listAllReportsPaginated(limit: number, lastReportId?: ReportID): Promise<Array<IARSubmission>> { let reports: Array<IARSubmissionRow>; if (lastReportId) { const query = createFetchAllReportsPaginatedQuery(limit); reports = await fetchMany<IARSubmissionRow>(query.bind({last_report_id: lastReportId})); } else { const query = createFetchAllReportsFirstPageQuery(limit); reports = await fetchMany<IARSubmissionRow>(query.bind({})); } return reports.map((report) => this.mapRowToSubmission(report)); }
async upsertDsaEmailVerification(row: DSAReportEmailVerificationRow): Promise<void> { await upsertOne(DSAReportEmailVerifications.insert(row)); }
async getDsaEmailVerification(emailLower: string): Promise<DSAReportEmailVerificationRow | null> { const row = await fetchOne<DSAReportEmailVerificationRow>( GET_DSA_EMAIL_VERIFICATION_QUERY.bind({email_lower: emailLower}), ); return row ?? null; }
async deleteDsaEmailVerification(emailLower: string): Promise<void> { await DSAReportEmailVerifications.deleteByPk({email_lower: emailLower}); }
async createDsaTicket(row: DSAReportTicketRow): Promise<void> { await upsertOne(DSAReportTickets.insert(row)); }
async getDsaTicket(ticket: string): Promise<DSAReportTicketRow | null> { const row = await fetchOne<DSAReportTicketRow>(GET_DSA_REPORT_TICKET_QUERY.bind({ticket})); return row ?? null; }
async deleteDsaTicket(ticket: string): Promise<void> { await DSAReportTickets.deleteByPk({ticket}); }
private mapMessageContext(rawContext: Array<IARMessageContextRow>): Array<IARMessageContext> { const toBigintArray = (collection: ReadonlyArray<bigint> | Set<bigint> | null | undefined): Array<bigint> => collection ? Array.from(collection) : []; return rawContext.map((msg) => ({ messageId: createMessageID(msg.message_id), authorId: createUserID(msg.author_id), channelId: msg.channel_id ? createChannelID(msg.channel_id) : null, authorUsername: msg.author_username, authorDiscriminator: msg.author_discriminator, authorAvatarHash: msg.author_avatar_hash, content: msg.content, timestamp: msg.timestamp, editedTimestamp: msg.edited_timestamp, type: msg.type, flags: msg.flags, mentionEveryone: msg.mention_everyone, mentionUsers: toBigintArray(msg.mention_users), mentionRoles: toBigintArray(msg.mention_roles), mentionChannels: toBigintArray(msg.mention_channels), attachments: msg.attachments ?? [], embeds: msg.embeds ?? [], stickers: msg.sticker_items ?? [], })); }}