feat: add multimodal analysis support to LLM moderation client by processing image attachments

This commit is contained in:
MythEclipse
2026-05-17 23:56:04 +07:00
parent 059d569566
commit 51dc1f8869
21 changed files with 443 additions and 83 deletions
+15 -4
View File
@@ -41,7 +41,10 @@ export class Streamer {
this.dankStreamer = new DankStreamer(client);
}
async createSession(guildId: string, channelId: string): Promise<StreamSession> {
async createSession(
guildId: string,
channelId: string,
): Promise<StreamSession> {
await this.dankStreamer.joinVoice(guildId, channelId);
let stopped = false;
@@ -62,7 +65,10 @@ export class Streamer {
return {
connection: {} as any,
stream: {} as any,
play: async (source: string | Readable, options: StreamPlayOptions = {}) => {
play: async (
source: string | Readable,
options: StreamPlayOptions = {},
) => {
if (stopped) return;
let targetSource: string | Readable = source;
@@ -75,7 +81,12 @@ export class Streamer {
const bitrateStr = String(options.bitrate ?? 8000).replace(/k$/i, "");
const bitrateVideo = parseInt(bitrateStr, 10) || 8000;
console.log("[Streamer] Starting screen share for source:", typeof targetSource === "string" ? targetSource.slice(0, 50) + "..." : "ReadableStream");
console.log(
"[Streamer] Starting screen share for source:",
typeof targetSource === "string"
? targetSource.slice(0, 50) + "..."
: "ReadableStream",
);
const { command, output } = dankPrepareStream(targetSource, {
encoder: Encoders.software({
x264: { preset: (options.presetH26x as any) ?? "ultrafast" },
@@ -100,7 +111,7 @@ export class Streamer {
const webOutput = new PassThrough();
const discordOutput = new PassThrough();
output.pipe(webOutput);
output.pipe(discordOutput);
+20 -8
View File
@@ -21,7 +21,10 @@ export class Transcoder {
restartTimer: NodeJS.Timeout | null = null;
maxRestarts = 6;
constructor(private source: string, private opts: TranscoderOptions = {}) {}
constructor(
private source: string,
private opts: TranscoderOptions = {},
) {}
start(): { command: ChildProcess; output: Readable } {
const fps = this.opts.fps ?? 30;
@@ -99,13 +102,19 @@ export class Transcoder {
scheduleRestart() {
if (this.restartAttempts >= this.maxRestarts) {
logger.error({ attempts: this.restartAttempts }, "transcoder reached max restart attempts");
logger.error(
{ attempts: this.restartAttempts },
"transcoder reached max restart attempts",
);
return;
}
const delay = Math.min(30000, 1000 * Math.pow(2, this.restartAttempts));
this.restartAttempts += 1;
transcoderRestartsCounter.inc();
logger.info({ delay, attempt: this.restartAttempts }, "scheduling transcoder restart");
logger.info(
{ delay, attempt: this.restartAttempts },
"scheduling transcoder restart",
);
this.restartTimer = setTimeout(() => {
try {
this.start();
@@ -129,9 +138,9 @@ export class Transcoder {
clearTimeout(this.restartTimer);
this.restartTimer = null;
}
if (this.proc && !this.proc.killed) {
return new Promise<void>((resolve) => {
this.proc?.once("exit", () => resolve());
if (this.proc && !this.proc.killed) {
return new Promise<void>((resolve) => {
this.proc?.once("exit", () => resolve());
try {
this.proc?.kill("SIGTERM");
} catch {
@@ -141,7 +150,7 @@ export class Transcoder {
resolve();
}
}
setTimeout(() => resolve(), 5000);
setTimeout(() => resolve(), 5000);
}).then(() => {
this.proc = null;
this.output = null;
@@ -151,7 +160,10 @@ export class Transcoder {
}
}
export function prepareTranscoder(source: string, options: TranscoderOptions = {}) {
export function prepareTranscoder(
source: string,
options: TranscoderOptions = {},
) {
const t = new Transcoder(source, options);
const { command, output } = t.start();
return { transcoder: t, command, output };