Skip to content

Commit

Permalink
feat: report embed gen
Browse files Browse the repository at this point in the history
  • Loading branch information
didinele committed Jul 27, 2024
1 parent 20bc0bc commit 590e042
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@naval-base/ms": "^3.1.0",
"@sapphire/bitfield": "^1.2.2",
"@sapphire/discord-utilities": "^3.3.0",
"@sapphire/snowflake": "^3.5.3",
"bin-rw": "^0.1.0",
"coral-command": "^0.10.0",
"inversify": "^6.0.2",
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/database/IDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
ModCase,
ModCaseKind,
ModCaseLogMessage,
Report,
Settings,
} from '../db.js';

Expand Down Expand Up @@ -36,6 +37,17 @@ export type CaseWithLogMessage = Selectable<ModCase> & { logMessage: Selectable<

export type UpdateModCaseOptions = Partial<Omit<Selectable<ModCase>, 'id'>> & { references?: number[] };

export interface CreateReporterOptions {
reason: string;
reportId: number;
userId: string;
}

export interface CreateReportOptions {
reportMessageId: string;
reporter: Omit<CreateReporterOptions, 'reportId'>;
}

/**
* Abstraction over all database interactions
*/
Expand Down Expand Up @@ -66,4 +78,6 @@ export abstract class IDatabase {
public abstract getLogWebhook(guildId: string, kind: LogWebhookKind): Promise<Selectable<LogWebhook> | undefined>;

public abstract getSettings(guildId: string): Promise<Selectable<Settings>>;

public abstract createReport(options: CreateReportOptions): Promise<Selectable<Report>>;
}
23 changes: 22 additions & 1 deletion packages/core/src/database/KyselyPostgresDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { sql, Kysely, type Selectable, PostgresDialect, type ExpressionBuilder,
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import type { Logger } from 'pino';
import { INJECTION_TOKENS } from '../container.js';
import type { DB, Incident, LogWebhook, LogWebhookKind, ModCase, ModCaseLogMessage, Settings } from '../db.js';
import type { DB, Incident, LogWebhook, LogWebhookKind, ModCase, ModCaseLogMessage, Report, Settings } from '../db.js';
import { Env } from '../util/Env.js';
import {
IDatabase,
type CaseWithLogMessage,
type CreateModCaseOptions,
type CreateReportOptions,
type ExperimentWithOverrides,
type GetModCasesAgainstOptions,
type GetRecentModCasesAgainstOptions,
Expand Down Expand Up @@ -245,6 +246,26 @@ export class KyselyPostgresDatabase extends IDatabase {
return this.#database.insertInto('Settings').values({ guildId }).returningAll().executeTakeFirstOrThrow();
}

public override async createReport(options: CreateReportOptions): Promise<Selectable<Report>> {
return this.#database.transaction().execute(async (trx) => {
const report = await trx
.insertInto('Report')
.values({
reportMessageId: options.reportMessageId,
})
.returningAll()
.executeTakeFirstOrThrow();

await trx
.insertInto('Reporter')
.values({ ...options.reporter, reportId: report.id })
.returningAll()
.executeTakeFirstOrThrow();

return report;
});
}

private readonly withLogMessage = (query: ExpressionBuilder<DB, 'ModCase'>) => [
jsonObjectFrom(
query
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ export type ModCaseLogMessage = {
};
export type Report = {
id: Generated<number>;
userId: string;
messageId: string | null;
reportMessageId: string;
acknowledged: Generated<boolean>;
};
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export * from './util/Env.js';
export * from './util/PermissionsBitField.js';
export * from './util/promiseAllObject.js';
export * from './util/setupCrashLogs.js';
export * from './util/userMessageToEmbed.js';
export * from './util/userToEmbedData.js';

export * from './container.js';
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/notifications/INotifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@ export abstract class INotifier {
public abstract logModCase(options: LogModCaseOptions): Promise<void>;
public abstract tryNotifyTargetModCase(modCase: Selectable<ModCase>): Promise<boolean>;
public abstract generateHistoryEmbed(options: HistoryEmbedOptions): APIEmbed;
public abstract logReport(guildId: Snowflake, message: APIMessage): Promise<void>;
}
14 changes: 13 additions & 1 deletion packages/core/src/notifications/Notifier.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { addFields, truncateEmbed } from '@chatsift/discord-utils';
import { API, type APIEmbed, type APIMessage } from '@discordjs/core';
import { API, type APIEmbed, type APIMessage, type Snowflake } from '@discordjs/core';
import { messageLink, time, TimestampStyles } from '@discordjs/formatters';
import { DiscordSnowflake } from '@sapphire/snowflake';
import { inject, injectable } from 'inversify';
import type { Selectable } from 'kysely';
import type { Logger } from 'pino';
import { INJECTION_TOKENS } from '../container.js';
import { IDatabase } from '../database/IDatabase.js';
import { LogWebhookKind, ModCaseKind, type ModCase } from '../db.js';
import { computeAvatarUrl } from '../util/computeAvatar.js';
import { formatMessageToEmbed } from '../util/userMessageToEmbed.js';
import { userToEmbedAuthor } from '../util/userToEmbedData.js';
import { INotifier, type DMUserOptions, type HistoryEmbedOptions, type LogModCaseOptions } from './INotifier.js';

Expand Down Expand Up @@ -202,4 +204,14 @@ export class Notifier extends INotifier {

return embed;
}

public override async logReport(guildId: Snowflake, message: APIMessage): Promise<void> {
const { reportChannelId } = await this.database.getSettings(guildId);
if (!reportChannelId) {
throw new Error('No report channel has been set up in this community; the caller is expected to assert this');
}

const embed = formatMessageToEmbed(message);
embed.color = 0xf04848;
}
}
39 changes: 39 additions & 0 deletions packages/core/src/util/userMessageToEmbed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Sourced from https://github.com/Naval-Base/yuudachi/blob/e398023952eeb2451af2c29884d9b848a5051985/apps/yuudachi/src/functions/logging/formatMessageToEmbed.ts#L6

// Copyright (C) 2021 Noel Buechler

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.

import { truncateEmbed } from '@chatsift/discord-utils';
import type { APIMessage } from '@discordjs/core';
import { DiscordSnowflake } from '@sapphire/snowflake';
import { userToEmbedAuthor } from './userToEmbedData.js';

export function formatMessageToEmbed(message: APIMessage) {
const embed = truncateEmbed({
author: userToEmbedAuthor(message.author, message.author.id),
description: message.content.length ? message.content : 'No content',
timestamp: new Date(DiscordSnowflake.timestampFrom(message.id)).toISOString(),
});

const attachment = message.attachments[0];

const attachmentIsImage = ['image/jpeg', 'image/png', 'image/gif'].includes(attachment?.content_type ?? '');
const attachmentIsImageNaive = ['.jpg', '.png', '.gif'].some((ext) => attachment?.filename?.endsWith(ext));

if (attachment && (attachmentIsImage || attachmentIsImageNaive)) {
embed.image = {
url: attachment.url,
};
}

return embed;
}
2 changes: 0 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ model Settings {

model Report {
id Int @id @default(autoincrement())
userId String
messageId String?
reportMessageId String
acknowledged Boolean @default(false)
Expand Down
3 changes: 2 additions & 1 deletion services/interactions/src/handlers/report.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HandlerModule, ICommandHandler, IDatabase } from '@automoderator/core';
import type { HandlerModule, ICommandHandler, IDatabase, INotifier } from '@automoderator/core';
import { computeModalFields } from '@automoderator/core/src/util/computeModalFields.js';
import {
API,
Expand All @@ -19,6 +19,7 @@ export default class ReportHandler implements HandlerModule<CoralInteractionHand
public constructor(
private readonly database: IDatabase,
private readonly api: API,
private readonly notifier: INotifier,
) {}

public register(handler: ICommandHandler<CoralInteractionHandler>) {
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ __metadata:
"@naval-base/ms": "npm:^3.1.0"
"@sapphire/bitfield": "npm:^1.2.2"
"@sapphire/discord-utilities": "npm:^3.3.0"
"@sapphire/snowflake": "npm:^3.5.3"
"@types/node": "npm:^20.14.10"
"@types/pg": "npm:^8.11.6"
bin-rw: "npm:^0.1.0"
Expand Down

0 comments on commit 590e042

Please sign in to comment.