Skip to content

Commit 4345d0f

Browse files
feat: invite tracking in log messages
1 parent ffe7086 commit 4345d0f

File tree

4 files changed

+72
-48
lines changed

4 files changed

+72
-48
lines changed

src/main/typescript/schemas/GuildConfigSchema.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -240,32 +240,6 @@ export const GuildConfigSchema = z.object({
240240
})
241241
.optional()
242242
/*
243-
244-
logging: z
245-
.object({
246-
enabled: z.boolean().default(false),
247-
bulk_delete_send_json: z.boolean().default(true),
248-
primary_channel: zSnowflake.optional(),
249-
message_logging_channel: zSnowflake.optional(),
250-
infraction_logging_channel: zSnowflake.optional(),
251-
join_leave_channel: zSnowflake.optional(),
252-
saved_messages_channel: zSnowflake.optional(),
253-
events: z
254-
.object({
255-
message_edit: z.boolean().default(true),
256-
message_delete: z.boolean().default(true),
257-
member_join: z.boolean().default(true),
258-
member_leave: z.boolean().default(true),
259-
message_bulk_delete: z.boolean().default(true)
260-
})
261-
.default({
262-
message_edit: true,
263-
member_leave: true,
264-
member_join: true,
265-
message_delete: true
266-
})
267-
})
268-
.optional(),
269243
message_reporting: z
270244
.object({
271245
enabled: z.boolean().default(false),
@@ -293,11 +267,6 @@ export const GuildConfigSchema = z.object({
293267
.default({})
294268
})
295269
.optional(),
296-
invite_tracking: z
297-
.object({
298-
enabled: z.boolean().default(false)
299-
})
300-
.optional(),
301270
message_filter: z
302271
.object({
303272
enabled: z.boolean().default(false),

src/main/typescript/services/AuditLoggingService.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,23 @@ class AuditLoggingService extends Service {
10341034
}
10351035
];
10361036

1037+
const invite = await this.application
1038+
.service("inviteTrackingService")
1039+
.findInviteForMember(member);
1040+
1041+
if (invite) {
1042+
let value = `**Link:** ${invite.url}\n`;
1043+
1044+
value += `Inviter: ${invite.inviterId ? `<@${invite.inviterId}> (${invite.inviterId})` : "*Unavailable*"}\n`;
1045+
value += `Uses: ${invite.uses} / ${invite.maxUses ?? "∞"}\n`;
1046+
value += `Expires: ${invite.expiresAt ? time(invite.expiresAt, "R") : "*Unavailable*"}`;
1047+
1048+
fields.push({
1049+
name: "Invite Information",
1050+
value
1051+
});
1052+
}
1053+
10371054
return this.send({
10381055
guildId: member.guild.id,
10391056
messageCreateOptions: {

src/main/typescript/services/InviteTrackingService.ts

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
import { Service } from "@framework/services/Service";
1+
import { GatewayEventListener } from "@framework/events/GatewayEventListener";
22
import { Name } from "@framework/services/Name";
3-
import { Collection, type Guild, type Invite, type ReadonlyCollection, Snowflake } from "discord.js";
3+
import { Service } from "@framework/services/Service";
44
import type { HasEventListeners } from "@framework/types/HasEventListeners";
5-
import { GatewayEventListener } from "@framework/events/GatewayEventListener";
5+
import {
6+
Collection,
7+
GuildMember,
8+
Snowflake,
9+
type Guild,
10+
type Invite,
11+
type ReadonlyCollection
12+
} from "discord.js";
613

714
@Name("inviteTrackingService")
815
class InviteTrackingService extends Service implements HasEventListeners {
916
private readonly _invites = new Collection<`${Snowflake}::${string}`, InviteInfo>();
17+
private readonly _uses = new Collection<`${Snowflake}::${string}`, number>();
1018

1119
public get invites(): ReadonlyCollection<`${Snowflake}::${string}`, InviteInfo> {
1220
return this._invites;
@@ -20,7 +28,8 @@ class InviteTrackingService extends Service implements HasEventListeners {
2028
this._invites.set(`${guild.id}::${invite.code}`, {
2129
type: "general",
2230
invite,
23-
guildId: guild.id
31+
guildId: guild.id,
32+
uses: invite.uses ?? 0
2433
});
2534
}
2635

@@ -43,10 +52,15 @@ class InviteTrackingService extends Service implements HasEventListeners {
4352
return;
4453
}
4554

55+
if (invite.inviterId) {
56+
this._uses.set(`${invite.guild.id}::${invite.code}`, 0);
57+
}
58+
4659
this._invites.set(`${invite.guild.id}::${invite.code}`, {
4760
type: "general",
4861
invite,
49-
guildId: invite.guild.id
62+
guildId: invite.guild.id,
63+
uses: invite.uses ?? 0
5064
});
5165
}
5266

@@ -57,6 +71,7 @@ class InviteTrackingService extends Service implements HasEventListeners {
5771
}
5872

5973
this._invites.delete(`${invite.guild.id}::${invite.code}`);
74+
this._uses.delete(`${invite.guild.id}::${invite.code}`);
6075
}
6176

6277
@GatewayEventListener("guildUpdate")
@@ -76,18 +91,39 @@ class InviteTrackingService extends Service implements HasEventListeners {
7691
}
7792
}
7893
}
79-
}
8094

81-
export type InviteInfo = {
82-
type: "general";
83-
invite: Invite;
84-
guildId: string;
85-
} | {
86-
type: "vanity";
87-
code: string;
88-
uses: number;
89-
guildId: string;
90-
};
95+
public async findInviteForMember(member: GuildMember) {
96+
const invites = await member.guild.invites.fetch();
97+
98+
for (const invite of invites.values()) {
99+
const existingUses = this._uses.get(`${member.guild.id}::${invite.code}`);
100+
101+
if (existingUses === undefined || invite.uses === null) {
102+
continue;
103+
}
104+
105+
if (invite.uses > existingUses) {
106+
this._uses.set(`${member.guild.id}::${invite.code}`, invite.uses);
107+
return invite;
108+
}
109+
}
110+
111+
return null;
112+
}
113+
}
91114

115+
export type InviteInfo =
116+
| {
117+
type: "general";
118+
invite: Invite;
119+
guildId: string;
120+
uses: number;
121+
}
122+
| {
123+
type: "vanity";
124+
code: string;
125+
uses: number;
126+
guildId: string;
127+
};
92128

93-
export default InviteTrackingService;
129+
export default InviteTrackingService;

src/main/typescript/types/Services.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type AntiMemberJoinService from "@main/automod/AntiMemberJoinService";
2323
import type VerificationService from "@main/automod/VerificationService";
2424
import type AuditLoggingService from "@main/services/AuditLoggingService";
2525
import type DirectiveParsingService from "@main/services/DirectiveParsingService";
26+
import type InviteTrackingService from "@main/services/InviteTrackingService";
2627
import type SystemAuditLoggingService from "@main/services/SystemAuditLoggingService";
2728
import type ChannelLockManager from "../services/ChannelLockManager";
2829
import type CommandManager from "../services/CommandManager";
@@ -57,6 +58,7 @@ export interface ServiceRecord {
5758
snippetManagerService: SnippetManagerService;
5859
antiMemberJoinService: AntiMemberJoinService;
5960
verificationService: VerificationService;
61+
inviteTrackingService: InviteTrackingService;
6062
}
6163

6264
interface ServiceRecordLocal extends ServiceRecord {}

0 commit comments

Comments
 (0)