Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/application/core/src/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,8 @@ export class User extends Base {

return null;
}

public async setActivity() {
await this.state.discord.user.setActivity();
}
}
13 changes: 13 additions & 0 deletions src/infrastructure/discord/src/Controller/UserController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ActivityType } from "discord.js";

import type { User } from "../types/User";
import { Base } from "./Base";

Expand Down Expand Up @@ -25,4 +27,15 @@ export class UserController extends Base {
const avatarUrl = this.client.users.cache.get(userId)?.avatarURL() ?? null;
return avatarUrl;
}

public async setActivity() {
if (!this.client.isReady()) {
return;
}

this.client.user.setActivity({
name: `${this.client.guilds.cache.size}server | ${this.client.users.cache.size}users | distopia.top`,
type: ActivityType.Playing,
});
}
}
37 changes: 5 additions & 32 deletions src/presentation/bot/src/EventHandler/MessageCreateHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Message, OmitPartialGroupDMChannel } from "discord.js";

import { detectSpamMessage } from "../utils/message";
import { BaseHandler } from "./BaseHandler";

export class MessageCreateHandler extends BaseHandler<
Expand All @@ -8,42 +9,14 @@ export class MessageCreateHandler extends BaseHandler<
public override async handle(
message: OmitPartialGroupDMChannel<Message<boolean>>,
): Promise<void> {
const content = message.content;
const settings = message.guildId ? await this.core.guild.getSetting(message.guildId) : null;
const isDetected = await detectSpamMessage(this.core, message);

if (settings?.inviteLinkBlock && !message.member?.permissions.has("Administrator")) {
const inviteLinks = await this.core.message.includeInviteLink(content);
if (inviteLinks.length) {
if (message.deletable) {
await message.delete();
}
return;
}

const embedInviteLinks = await this.core.state.discord.embed.detectInviteLinks(
message.embeds,
);
if (embedInviteLinks.length) {
const messages = (await message.channel.messages.fetch({ limit: 30 })).values().toArray();

for (const msg of messages) {
for (const inviteLink of embedInviteLinks) {
if (msg.content.includes(inviteLink) && msg.deletable) {
await msg.delete();
}
}
}

if (message.deletable) {
await message.delete();
}

return;
}
if (isDetected) {
return;
}

if (message.guildId && message.member?.id) {
await this.core.message.increase(message.guildId, message.member.id, content);
await this.core.message.increase(message.guildId, message.member.id, message.content);
}
}
}
22 changes: 22 additions & 0 deletions src/presentation/bot/src/EventHandler/MessageUpdateHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Message, OmitPartialGroupDMChannel, PartialMessage } from "discord.js";

import { detectSpamMessage } from "../utils/message";
import { BaseHandler } from "./BaseHandler";

export class MessageUpdateHandler extends BaseHandler<
(
oldMessage: OmitPartialGroupDMChannel<Message<boolean> | PartialMessage<boolean>>,
newMessage: OmitPartialGroupDMChannel<Message<boolean>>,
) => void
> {
public override async handle(
_oldMessage: OmitPartialGroupDMChannel<Message<boolean> | PartialMessage<boolean>>,
newMessage: OmitPartialGroupDMChannel<Message<boolean>>,
): Promise<void> {
const isDetected = await detectSpamMessage(this.core, newMessage);

if (isDetected) {
return;
}
}
}
14 changes: 9 additions & 5 deletions src/presentation/bot/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import type { AppCore } from "app-core";
import { ActivityType, type Client } from "discord.js";
import type { Client } from "discord.js";

import { GuildMemberAddHandler } from "./EventHandler/GuildMemberAddHandler";
import { InteractionCreateHandler } from "./EventHandler/InteractionCreateHandler/index";
import { MessageCreateHandler } from "./EventHandler/MessageCreateHandler";
import { MessageUpdateHandler } from "./EventHandler/MessageUpdateHandler";

export function handleClient(client: Client, core: AppCore) {
const interactionCreateHandler = new InteractionCreateHandler(core);
const messageCreateHandler = new MessageCreateHandler(core);
const messageUpdateHandler = new MessageUpdateHandler(core);
const guildMemberAddHandler = new GuildMemberAddHandler(core);

client.on("clientReady", async (client) => {
client.user.setActivity({
name: `${client.guilds.cache.size}server | ${client.users.cache.size}users | distopia.top`,
type: ActivityType.Playing,
});
await core.user.setActivity();

const commands = interactionCreateHandler.commands.chatInput.map((command) => command.register);

Expand All @@ -30,6 +29,11 @@ export function handleClient(client: Client, core: AppCore) {

client.on("messageCreate", async (message) => await messageCreateHandler.handle(message));

client.on(
"messageUpdate",
async (oldMsg, newMsg) => await messageUpdateHandler.handle(oldMsg, newMsg),
);

client.on("guildMemberAdd", async (member) => await guildMemberAddHandler.handle(member));

return client;
Expand Down
44 changes: 44 additions & 0 deletions src/presentation/bot/src/utils/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { AppCore } from "app-core";
import type { Message, OmitPartialGroupDMChannel } from "discord.js";

export async function detectSpamMessage(
core: AppCore,
message: OmitPartialGroupDMChannel<Message<boolean>>,
): Promise<boolean> {
const content = message.content;
const settings = message.guildId ? await core.guild.getSetting(message.guildId) : null;

if (settings?.inviteLinkBlock && !message.member?.permissions.has("Administrator")) {
const inviteLinks = await core.message.includeInviteLink(content);
if (inviteLinks.length) {
if (message.deletable) {
await message.delete();
}
return true;
}

const embedInviteLinks = await core.state.discord.embed.detectInviteLinks(message.embeds);
if (embedInviteLinks.length) {
const messages = message.channel.messages.cache
.values()
.toArray()
.filter((msg) => msg.author.id === message.author.id);

for (const msg of messages) {
for (const inviteLink of embedInviteLinks) {
if (msg.content.includes(inviteLink) && msg.deletable) {
await msg.delete();
}
}
}

if (message.deletable) {
await message.delete();
}

return true;
}
}
Comment on lines +11 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file first
ast-grep outline src/presentation/bot/src/utils/message.ts --view expanded

# Find call sites and relevant handlers
rg -n "includeInviteLink|messageCreate|messageUpdate|message\.delete\(\)|msg\.delete\(\)|messages\.fetch\(" src/presentation/bot/src -S

# Read the target file with line numbers
cat -n src/presentation/bot/src/utils/message.ts

# Read likely event handlers if found by rg (adjust paths only if they exist)
for f in $(rg -l "includeInviteLink|messageCreate|messageUpdate" src/presentation/bot/src -S); do
  echo "===== $f ====="
  cat -n "$f"
done

Repository: ThunLights/distopia

Length of output: 6121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the message handlers and the helper usage
ast-grep outline src/presentation/bot/src/EventHandler/MessageCreateHandler --view expanded
ast-grep outline src/presentation/bot/src/EventHandler/MessageUpdateHandler --view expanded

rg -n "detectSpamMessage\(|try \{|catch \(|messages\.fetch\(|message\.delete\(|msg\.delete\(" src/presentation/bot/src/EventHandler src/presentation/bot/src/utils -S

for f in \
  src/presentation/bot/src/EventHandler/MessageCreateHandler/index.ts \
  src/presentation/bot/src/EventHandler/MessageUpdateHandler/index.ts
do
  if [ -f "$f" ]; then
    echo "===== $f ====="
    cat -n "$f"
  fi
done

Repository: ThunLights/distopia

Length of output: 1124


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the handler implementations
cat -n src/presentation/bot/src/EventHandler/MessageCreateHandler.ts
cat -n src/presentation/bot/src/EventHandler/MessageUpdateHandler.ts

# Search for global rejection/exception handling in the bot package
rg -n "unhandledRejection|uncaughtException|process\.on\(|setUncaughtExceptionCaptureCallback|errorHandler|catch\s*\(" src/presentation/bot/src -S

# Inspect any matching files if found
for f in $(rg -l "unhandledRejection|uncaughtException|process\.on\(" src/presentation/bot/src -S); do
  echo "===== $f ====="
  cat -n "$f"
done

Repository: ThunLights/distopia

Length of output: 1869


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n src/presentation/bot/src/EventHandler/BaseHandler.ts

rg -n "process\.on\(|unhandledRejection|uncaughtException|setUncaughtExceptionCaptureCallback" src/presentation/bot/src -S

# If any matches exist, print the files
for f in $(rg -l "process\.on\(|unhandledRejection|uncaughtException|setUncaughtExceptionCaptureCallback" src/presentation/bot/src -S); do
  echo "===== $f ====="
  cat -n "$f"
done

Repository: ThunLights/distopia

Length of output: 447


Wrap the Discord API calls in local error handling. message.delete(), msg.delete(), and message.channel.messages.fetch() can reject on missing permissions, deleted messages, or rate limits; a single failure here can bubble out of the event handler.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presentation/bot/src/utils/message.ts` around lines 11 - 38, Add local
try/catch handling around the Discord API calls in the message moderation flow
inside the invite-link block of the message utility. In the logic that uses
core.message.includeInviteLink, core.state.discord.embed.detectInviteLinks,
message.channel.messages.fetch, message.delete, and msg.delete, swallow or log
expected Discord failures (missing permissions, already-deleted messages, rate
limits) so one rejection does not escape the event handler. Keep the existing
behavior and only guard the API calls in this branch.


return false;
}
1 change: 1 addition & 0 deletions src/presentation/web/src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ async function start() {
);
await core.record.update();
await updatePanels();
await core.user.setActivity();
},
// This job's runtime scales with guild count (sequential Discord API
// calls); without noOverlap a slow run can still be executing when the
Expand Down
Loading