@helyx/bot 0.4.31 → 0.4.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/control-permissions.d.ts +38 -0
- package/dist/control-permissions.js +139 -0
- package/dist/control-server.d.ts +24 -16
- package/dist/control-server.js +149 -166
- package/dist/discord-guild-health.d.ts +174 -0
- package/dist/discord-guild-health.js +370 -0
- package/dist/events.d.ts +15 -0
- package/dist/events.js +338 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +26 -7
- package/dist/interactions.js +36 -6
- package/dist/modules.d.ts +2 -0
- package/dist/modules.js +10 -1
- package/dist/oauth-module.d.ts +2 -0
- package/dist/oauth-module.js +196 -0
- package/dist/oauth-module.test.d.ts +2 -0
- package/dist/oauth-module.test.js +172 -0
- package/dist/oauth-service.d.ts +16 -0
- package/dist/oauth-service.js +40 -0
- package/dist/shard-contracts.d.ts +42 -0
- package/dist/shard-contracts.js +26 -0
- package/dist/shard-router.d.ts +1 -0
- package/dist/shard-router.js +3 -0
- package/dist/shard-supervisor.js +29 -3
- package/dist/shard-worker.d.ts +5 -0
- package/dist/shard-worker.js +16 -1
- package/package.json +1 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { DiscordGuildHealthInspectionService, DiscordGuildHealthUnavailable, ModuleDefinition, ServiceAccess } from "@helyx/sdk";
|
|
2
|
+
import { type Client } from "discord.js";
|
|
3
|
+
export declare function moduleCatalogueMatches(packagedManifest: unknown, executableManifest: unknown): boolean;
|
|
4
|
+
export declare function missingRequiredPermissions(grantedPermissions: readonly string[], requiredPermissions: readonly string[]): string[];
|
|
5
|
+
export declare function hasRequiredModuleConfiguration(exposesConfiguration: boolean, savedConfiguration: unknown): boolean;
|
|
6
|
+
export declare function isHealthInspectableModule(moduleId: string): boolean;
|
|
7
|
+
export declare class DiscordGuildHealthInspection implements DiscordGuildHealthInspectionService {
|
|
8
|
+
#private;
|
|
9
|
+
private readonly client;
|
|
10
|
+
private readonly modules;
|
|
11
|
+
private readonly services?;
|
|
12
|
+
constructor(client: Client, modules?: readonly ModuleDefinition[], services?: ServiceAccess | undefined);
|
|
13
|
+
inspectOverview(input: {
|
|
14
|
+
guildId: string;
|
|
15
|
+
}): Promise<DiscordGuildHealthUnavailable> | Promise<{
|
|
16
|
+
available: true;
|
|
17
|
+
guildId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
ownerId: string;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
memberCount: number;
|
|
22
|
+
verificationLevel: string;
|
|
23
|
+
explicitContentFilter: string;
|
|
24
|
+
defaultMessageNotifications: string;
|
|
25
|
+
mfaLevel: string;
|
|
26
|
+
premiumTier: number;
|
|
27
|
+
features: ("ANIMATED_BANNER" | "ANIMATED_ICON" | "APPLICATION_COMMAND_PERMISSIONS_V2" | "AUTO_MODERATION" | "BANNER" | "COMMUNITY" | "CREATOR_MONETIZABLE_PROVISIONAL" | "CREATOR_STORE_PAGE" | "DEVELOPER_SUPPORT_SERVER" | "DISCOVERABLE" | "FEATURABLE" | "HAS_DIRECTORY_ENTRY" | "HUB" | "INVITES_DISABLED" | "INVITE_SPLASH" | "LINKED_TO_HUB" | "MEMBER_VERIFICATION_GATE_ENABLED" | "MORE_SOUNDBOARD" | "MONETIZATION_ENABLED" | "MORE_STICKERS" | "NEWS" | "PARTNERED" | "PREVIEW_ENABLED" | "PRIVATE_THREADS" | "RAID_ALERTS_DISABLED" | "RELAY_ENABLED" | "ROLE_ICONS" | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE" | "ROLE_SUBSCRIPTIONS_ENABLED" | "SOUNDBOARD" | "TICKETED_EVENTS_ENABLED" | "VANITY_URL" | "VERIFIED" | "VIP_REGIONS" | "WELCOME_SCREEN_ENABLED" | "GUILD_TAGS" | "ENHANCED_ROLE_COLORS" | "GUESTS_ENABLED" | "PIN_PERMISSION_MIGRATION_COMPLETE")[];
|
|
28
|
+
botPermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
29
|
+
channelCount: number;
|
|
30
|
+
roleCount: number;
|
|
31
|
+
emojiCount: number;
|
|
32
|
+
stickerCount: number;
|
|
33
|
+
rulesChannelId: string | null;
|
|
34
|
+
publicUpdatesChannelId: string | null;
|
|
35
|
+
safetyAlertsChannelId: string | null;
|
|
36
|
+
invitesDisabledUntil: Date | null;
|
|
37
|
+
dmsDisabledUntil: Date | null;
|
|
38
|
+
botHighestRolePosition: number;
|
|
39
|
+
}>;
|
|
40
|
+
listChannels(input: {
|
|
41
|
+
guildId: string;
|
|
42
|
+
}): Promise<DiscordGuildHealthUnavailable> | Promise<{
|
|
43
|
+
available: true;
|
|
44
|
+
items: {
|
|
45
|
+
channelId: string;
|
|
46
|
+
name: string;
|
|
47
|
+
type: string;
|
|
48
|
+
parentId: string | null;
|
|
49
|
+
ageRestricted: boolean;
|
|
50
|
+
everyonePermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
51
|
+
botPermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
52
|
+
permissionsLocked: boolean | null;
|
|
53
|
+
}[];
|
|
54
|
+
}>;
|
|
55
|
+
listRoles(input: {
|
|
56
|
+
guildId: string;
|
|
57
|
+
}): Promise<{
|
|
58
|
+
available: true;
|
|
59
|
+
items: {
|
|
60
|
+
roleId: string;
|
|
61
|
+
name: string;
|
|
62
|
+
position: number;
|
|
63
|
+
managed: boolean;
|
|
64
|
+
memberCount: number;
|
|
65
|
+
permissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
66
|
+
}[];
|
|
67
|
+
}>;
|
|
68
|
+
listMembers(input: {
|
|
69
|
+
guildId: string;
|
|
70
|
+
after?: string;
|
|
71
|
+
limit: number;
|
|
72
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
73
|
+
nextAfter?: string;
|
|
74
|
+
available: true;
|
|
75
|
+
items: {
|
|
76
|
+
userId: string;
|
|
77
|
+
bot: boolean;
|
|
78
|
+
system: boolean;
|
|
79
|
+
createdAt: Date;
|
|
80
|
+
joinedAt: Date | null;
|
|
81
|
+
timedOutUntil: Date | null;
|
|
82
|
+
publicFlags: ("Staff" | "Partner" | "Hypesquad" | "BugHunterLevel1" | "MFASMS" | "PremiumPromoDismissed" | "HypeSquadOnlineHouse1" | "HypeSquadOnlineHouse2" | "HypeSquadOnlineHouse3" | "PremiumEarlySupporter" | "TeamPseudoUser" | "HasUnreadUrgentMessages" | "BugHunterLevel2" | "VerifiedBot" | "VerifiedDeveloper" | "CertifiedModerator" | "BotHTTPInteractions" | "Spammer" | "DisablePremium" | "ActiveDeveloper" | "Quarantined" | "Collaborator" | "RestrictedCollaborator")[];
|
|
83
|
+
roleIds: string[];
|
|
84
|
+
}[];
|
|
85
|
+
}>;
|
|
86
|
+
listInvites(input: {
|
|
87
|
+
guildId: string;
|
|
88
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
89
|
+
available: true;
|
|
90
|
+
items: {
|
|
91
|
+
code: string;
|
|
92
|
+
channelId: string | null;
|
|
93
|
+
channelName: string | null;
|
|
94
|
+
channelExists: boolean;
|
|
95
|
+
inviterUserId: string | null;
|
|
96
|
+
createdAt: Date | null;
|
|
97
|
+
expiresAt: Date | null;
|
|
98
|
+
maxUses: number;
|
|
99
|
+
uses: number;
|
|
100
|
+
temporary: boolean;
|
|
101
|
+
}[];
|
|
102
|
+
}>;
|
|
103
|
+
listIntegrations(input: {
|
|
104
|
+
guildId: string;
|
|
105
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
106
|
+
available: true;
|
|
107
|
+
items: {
|
|
108
|
+
integrationId: string;
|
|
109
|
+
name: string;
|
|
110
|
+
type: import("discord.js").IntegrationType;
|
|
111
|
+
enabled: boolean;
|
|
112
|
+
syncing: boolean | null;
|
|
113
|
+
roleId: string | null;
|
|
114
|
+
roleExists: boolean;
|
|
115
|
+
}[];
|
|
116
|
+
}>;
|
|
117
|
+
listWebhooks(input: {
|
|
118
|
+
guildId: string;
|
|
119
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
120
|
+
available: true;
|
|
121
|
+
items: {
|
|
122
|
+
webhookId: string;
|
|
123
|
+
name: string;
|
|
124
|
+
channelId: string;
|
|
125
|
+
ownerUserId: string | null;
|
|
126
|
+
applicationId: null;
|
|
127
|
+
channelName: string | null;
|
|
128
|
+
}[];
|
|
129
|
+
}>;
|
|
130
|
+
listAutoModerationRules(input: {
|
|
131
|
+
guildId: string;
|
|
132
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
133
|
+
available: true;
|
|
134
|
+
items: {
|
|
135
|
+
ruleId: string;
|
|
136
|
+
name: string;
|
|
137
|
+
enabled: boolean;
|
|
138
|
+
triggerType: string;
|
|
139
|
+
eventType: string;
|
|
140
|
+
actionCount: number;
|
|
141
|
+
}[];
|
|
142
|
+
}>;
|
|
143
|
+
inspectAuditSummary(input: {
|
|
144
|
+
guildId: string;
|
|
145
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
146
|
+
available: true;
|
|
147
|
+
inspected: number;
|
|
148
|
+
highImpactActionsLastHour: number;
|
|
149
|
+
}>;
|
|
150
|
+
inspectHelyxModules(input: {
|
|
151
|
+
guildId: string;
|
|
152
|
+
batchSize?: number;
|
|
153
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
154
|
+
available: true;
|
|
155
|
+
items: {
|
|
156
|
+
moduleId: string;
|
|
157
|
+
name: string;
|
|
158
|
+
version: string;
|
|
159
|
+
enabled: boolean;
|
|
160
|
+
configured: boolean;
|
|
161
|
+
configurationValid: boolean;
|
|
162
|
+
resourceValidationValid: boolean | null;
|
|
163
|
+
activationValid: boolean | null;
|
|
164
|
+
catalogueMatches: boolean;
|
|
165
|
+
requiredCapabilities: readonly ("message-content" | "guild-members")[];
|
|
166
|
+
missingCapabilities: ("message-content" | "guild-members")[];
|
|
167
|
+
missingPermissions: string[];
|
|
168
|
+
healthStatus: "healthy" | "degraded" | "unhealthy";
|
|
169
|
+
healthMessage: string | null;
|
|
170
|
+
diagnosticsAvailable: boolean;
|
|
171
|
+
}[];
|
|
172
|
+
}>;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=discord-guild-health.d.ts.map
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { HELYX_SERVICE_NAMES } from "@helyx/sdk";
|
|
2
|
+
import { AuditLogEvent, GatewayIntentBits, } from "discord.js";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
const unavailable = (code) => ({ available: false, code });
|
|
5
|
+
const INTERNAL_HELYX_MODULE_IDS = new Set([
|
|
6
|
+
"helyx.cloud-connect",
|
|
7
|
+
"helyx.management",
|
|
8
|
+
"helyx.permissions",
|
|
9
|
+
"helyx.support",
|
|
10
|
+
]);
|
|
11
|
+
export function moduleCatalogueMatches(packagedManifest, executableManifest) {
|
|
12
|
+
return (packagedManifest !== null &&
|
|
13
|
+
isDeepStrictEqual(packagedManifest, executableManifest));
|
|
14
|
+
}
|
|
15
|
+
export function missingRequiredPermissions(grantedPermissions, requiredPermissions) {
|
|
16
|
+
const granted = new Set(grantedPermissions.map(normalizePermission));
|
|
17
|
+
if (granted.has(normalizePermission("Administrator")))
|
|
18
|
+
return [];
|
|
19
|
+
return requiredPermissions.filter((permission) => !granted.has(normalizePermission(permission)));
|
|
20
|
+
}
|
|
21
|
+
export function hasRequiredModuleConfiguration(exposesConfiguration, savedConfiguration) {
|
|
22
|
+
return !exposesConfiguration || savedConfiguration !== null;
|
|
23
|
+
}
|
|
24
|
+
export function isHealthInspectableModule(moduleId) {
|
|
25
|
+
return !INTERNAL_HELYX_MODULE_IDS.has(moduleId);
|
|
26
|
+
}
|
|
27
|
+
export class DiscordGuildHealthInspection {
|
|
28
|
+
client;
|
|
29
|
+
modules;
|
|
30
|
+
services;
|
|
31
|
+
constructor(client, modules = [], services) {
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.modules = modules;
|
|
34
|
+
this.services = services;
|
|
35
|
+
}
|
|
36
|
+
inspectOverview(input) {
|
|
37
|
+
const guild = this.#guild(input.guildId);
|
|
38
|
+
const bot = guild.members.me;
|
|
39
|
+
if (!bot)
|
|
40
|
+
return Promise.resolve(unavailable("discord_unavailable"));
|
|
41
|
+
return Promise.resolve({
|
|
42
|
+
available: true,
|
|
43
|
+
guildId: guild.id,
|
|
44
|
+
name: guild.name,
|
|
45
|
+
ownerId: guild.ownerId,
|
|
46
|
+
createdAt: guild.createdAt,
|
|
47
|
+
memberCount: guild.memberCount,
|
|
48
|
+
verificationLevel: ["None", "Low", "Medium", "High", "VeryHigh"][Number(guild.verificationLevel)] ?? "Unknown",
|
|
49
|
+
explicitContentFilter: ["Disabled", "MembersWithoutRoles", "AllMembers"][Number(guild.explicitContentFilter)] ?? "Unknown",
|
|
50
|
+
defaultMessageNotifications: ["AllMessages", "OnlyMentions"][Number(guild.defaultMessageNotifications)] ?? "Unknown",
|
|
51
|
+
mfaLevel: Number(guild.mfaLevel) === 1 ? "Elevated" : "None",
|
|
52
|
+
premiumTier: Number(guild.premiumTier),
|
|
53
|
+
features: [...guild.features],
|
|
54
|
+
botPermissions: bot.permissions.toArray(),
|
|
55
|
+
channelCount: guild.channels.cache.size,
|
|
56
|
+
roleCount: guild.roles.cache.size,
|
|
57
|
+
emojiCount: guild.emojis.cache.size,
|
|
58
|
+
stickerCount: guild.stickers.cache.size,
|
|
59
|
+
rulesChannelId: guild.rulesChannelId,
|
|
60
|
+
publicUpdatesChannelId: guild.publicUpdatesChannelId,
|
|
61
|
+
safetyAlertsChannelId: guild.safetyAlertsChannelId,
|
|
62
|
+
invitesDisabledUntil: guild.incidentsData?.invitesDisabledUntil ?? null,
|
|
63
|
+
dmsDisabledUntil: guild.incidentsData?.dmsDisabledUntil ?? null,
|
|
64
|
+
botHighestRolePosition: bot.roles.highest.position,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
listChannels(input) {
|
|
68
|
+
const guild = this.#guild(input.guildId);
|
|
69
|
+
const bot = guild.members.me;
|
|
70
|
+
if (!bot)
|
|
71
|
+
return Promise.resolve(unavailable("discord_unavailable"));
|
|
72
|
+
return Promise.resolve({
|
|
73
|
+
available: true,
|
|
74
|
+
items: guild.channels.cache.map((channel) => ({
|
|
75
|
+
channelId: channel.id,
|
|
76
|
+
name: channel.name,
|
|
77
|
+
type: String(channel.type),
|
|
78
|
+
parentId: "parentId" in channel ? (channel.parentId ?? null) : null,
|
|
79
|
+
ageRestricted: "nsfw" in channel && channel.nsfw === true,
|
|
80
|
+
everyonePermissions: channel.permissionsFor(guild.roles.everyone)?.toArray() ?? [],
|
|
81
|
+
botPermissions: channel.permissionsFor(bot)?.toArray() ?? [],
|
|
82
|
+
permissionsLocked: "permissionsLocked" in channel
|
|
83
|
+
? (channel.permissionsLocked ?? null)
|
|
84
|
+
: null,
|
|
85
|
+
})),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
listRoles(input) {
|
|
89
|
+
const guild = this.#guild(input.guildId);
|
|
90
|
+
return Promise.resolve({
|
|
91
|
+
available: true,
|
|
92
|
+
items: guild.roles.cache.map((role) => ({
|
|
93
|
+
roleId: role.id,
|
|
94
|
+
name: role.name,
|
|
95
|
+
position: role.position,
|
|
96
|
+
managed: role.managed,
|
|
97
|
+
memberCount: role.members.size,
|
|
98
|
+
permissions: role.permissions.toArray(),
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async listMembers(input) {
|
|
103
|
+
const guild = this.#guild(input.guildId);
|
|
104
|
+
try {
|
|
105
|
+
const limit = Math.min(1_000, Math.max(1, input.limit));
|
|
106
|
+
const members = await guild.members.list({
|
|
107
|
+
limit,
|
|
108
|
+
...(input.after ? { after: input.after } : {}),
|
|
109
|
+
});
|
|
110
|
+
const items = members.map((member) => ({
|
|
111
|
+
userId: member.id,
|
|
112
|
+
bot: member.user.bot,
|
|
113
|
+
system: member.user.system === true,
|
|
114
|
+
createdAt: member.user.createdAt,
|
|
115
|
+
joinedAt: member.joinedAt,
|
|
116
|
+
timedOutUntil: member.communicationDisabledUntil,
|
|
117
|
+
publicFlags: member.user.flags?.toArray() ?? [],
|
|
118
|
+
roleIds: [...member.roles.cache.keys()],
|
|
119
|
+
}));
|
|
120
|
+
return {
|
|
121
|
+
available: true,
|
|
122
|
+
items,
|
|
123
|
+
...(members.size === limit && members.last()
|
|
124
|
+
? { nextAfter: members.last().id }
|
|
125
|
+
: {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return this.#unavailable(error, "missing_intent");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async listInvites(input) {
|
|
133
|
+
try {
|
|
134
|
+
const guild = this.#guild(input.guildId);
|
|
135
|
+
const invites = await guild.invites.fetch();
|
|
136
|
+
return {
|
|
137
|
+
available: true,
|
|
138
|
+
items: invites.map((invite) => ({
|
|
139
|
+
code: invite.code,
|
|
140
|
+
channelId: invite.channelId,
|
|
141
|
+
channelName: invite.channelId
|
|
142
|
+
? (guild.channels.cache.get(invite.channelId)?.name ?? null)
|
|
143
|
+
: null,
|
|
144
|
+
channelExists: invite.channelId !== null &&
|
|
145
|
+
guild.channels.cache.has(invite.channelId),
|
|
146
|
+
inviterUserId: invite.inviterId,
|
|
147
|
+
createdAt: invite.createdAt,
|
|
148
|
+
expiresAt: invite.expiresAt,
|
|
149
|
+
maxUses: invite.maxUses ?? 0,
|
|
150
|
+
uses: invite.uses ?? 0,
|
|
151
|
+
temporary: invite.temporary ?? false,
|
|
152
|
+
})),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return this.#unavailable(error, "missing_permission");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async listIntegrations(input) {
|
|
160
|
+
try {
|
|
161
|
+
const guild = this.#guild(input.guildId);
|
|
162
|
+
const integrations = await guild.fetchIntegrations();
|
|
163
|
+
return {
|
|
164
|
+
available: true,
|
|
165
|
+
items: integrations.map((integration) => ({
|
|
166
|
+
integrationId: integration.id,
|
|
167
|
+
name: integration.name,
|
|
168
|
+
type: integration.type,
|
|
169
|
+
enabled: integration.enabled ?? false,
|
|
170
|
+
syncing: "syncing" in integration ? (integration.syncing ?? null) : null,
|
|
171
|
+
roleId: "role" in integration ? (integration.role?.id ?? null) : null,
|
|
172
|
+
roleExists: !("role" in integration) ||
|
|
173
|
+
!integration.role?.id ||
|
|
174
|
+
guild.roles.cache.has(integration.role.id),
|
|
175
|
+
})),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
return this.#unavailable(error, "missing_permission");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async listWebhooks(input) {
|
|
183
|
+
try {
|
|
184
|
+
const webhooks = await this.#guild(input.guildId).fetchWebhooks();
|
|
185
|
+
return {
|
|
186
|
+
available: true,
|
|
187
|
+
items: webhooks.map((webhook) => ({
|
|
188
|
+
webhookId: webhook.id,
|
|
189
|
+
name: webhook.name,
|
|
190
|
+
channelId: webhook.channelId,
|
|
191
|
+
ownerUserId: webhook.owner?.id ?? null,
|
|
192
|
+
applicationId: webhook.applicationId,
|
|
193
|
+
channelName: webhook.channelId
|
|
194
|
+
? (this.#guild(input.guildId).channels.cache.get(webhook.channelId)
|
|
195
|
+
?.name ?? null)
|
|
196
|
+
: null,
|
|
197
|
+
})),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return this.#unavailable(error, "missing_permission");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async listAutoModerationRules(input) {
|
|
205
|
+
try {
|
|
206
|
+
const rules = await this.#guild(input.guildId).autoModerationRules.fetch();
|
|
207
|
+
return {
|
|
208
|
+
available: true,
|
|
209
|
+
items: rules.map((rule) => ({
|
|
210
|
+
ruleId: rule.id,
|
|
211
|
+
name: rule.name,
|
|
212
|
+
enabled: rule.enabled,
|
|
213
|
+
triggerType: String(rule.triggerType),
|
|
214
|
+
eventType: String(rule.eventType),
|
|
215
|
+
actionCount: rule.actions.length,
|
|
216
|
+
})),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
return this.#unavailable(error, "missing_permission");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async inspectAuditSummary(input) {
|
|
224
|
+
try {
|
|
225
|
+
const audit = await this.#guild(input.guildId).fetchAuditLogs({
|
|
226
|
+
limit: 100,
|
|
227
|
+
});
|
|
228
|
+
const cutoff = Date.now() - 60 * 60 * 1_000;
|
|
229
|
+
const highImpact = new Set([
|
|
230
|
+
AuditLogEvent.MemberBanAdd,
|
|
231
|
+
AuditLogEvent.MemberKick,
|
|
232
|
+
AuditLogEvent.RoleCreate,
|
|
233
|
+
AuditLogEvent.RoleDelete,
|
|
234
|
+
AuditLogEvent.RoleUpdate,
|
|
235
|
+
AuditLogEvent.ChannelDelete,
|
|
236
|
+
AuditLogEvent.WebhookCreate,
|
|
237
|
+
AuditLogEvent.WebhookDelete,
|
|
238
|
+
AuditLogEvent.WebhookUpdate,
|
|
239
|
+
]);
|
|
240
|
+
return {
|
|
241
|
+
available: true,
|
|
242
|
+
inspected: audit.entries.size,
|
|
243
|
+
highImpactActionsLastHour: audit.entries.filter((entry) => entry.createdTimestamp >= cutoff && highImpact.has(entry.action)).size,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
return this.#unavailable(error, "missing_permission");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async inspectHelyxModules(input) {
|
|
251
|
+
if (!this.services)
|
|
252
|
+
return unavailable("not_supported");
|
|
253
|
+
try {
|
|
254
|
+
const installations = this.services.get(HELYX_SERVICE_NAMES.installations);
|
|
255
|
+
const configurations = this.services.get(HELYX_SERVICE_NAMES.configuration);
|
|
256
|
+
const catalogue = this.services.has(HELYX_SERVICE_NAMES.moduleCatalogue)
|
|
257
|
+
? this.services.get(HELYX_SERVICE_NAMES.moduleCatalogue)
|
|
258
|
+
: null;
|
|
259
|
+
const resources = this.services.has(HELYX_SERVICE_NAMES.discordResources)
|
|
260
|
+
? this.services.get(HELYX_SERVICE_NAMES.discordResources)
|
|
261
|
+
: null;
|
|
262
|
+
const capabilities = resources
|
|
263
|
+
? await resources.inspectRuntimeCapabilities()
|
|
264
|
+
: {
|
|
265
|
+
guildMembers: this.client.options.intents.has(GatewayIntentBits.GuildMembers),
|
|
266
|
+
messageContent: this.client.options.intents.has(GatewayIntentBits.MessageContent),
|
|
267
|
+
};
|
|
268
|
+
const overview = await this.inspectOverview(input);
|
|
269
|
+
if (!overview.available)
|
|
270
|
+
return overview;
|
|
271
|
+
const items = [];
|
|
272
|
+
const batchSize = Math.min(20, Math.max(1, input.batchSize ?? 20));
|
|
273
|
+
const inspectableModules = this.modules.filter((module) => isHealthInspectableModule(module.manifest.id));
|
|
274
|
+
for (let index = 0; index < inspectableModules.length; index += 1) {
|
|
275
|
+
const module = inspectableModules[index];
|
|
276
|
+
const state = await installations.getModuleState(input.guildId, module.manifest.id);
|
|
277
|
+
const configuration = await configurations.get(input.guildId, module.manifest.id);
|
|
278
|
+
let configurationValid = true;
|
|
279
|
+
let resourceValidationValid = null;
|
|
280
|
+
if (configuration && module.configuration)
|
|
281
|
+
try {
|
|
282
|
+
module.configuration.validate(configuration.value);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
configurationValid = false;
|
|
286
|
+
}
|
|
287
|
+
if (state?.enabled &&
|
|
288
|
+
configuration &&
|
|
289
|
+
module.configuration?.validateUpdate)
|
|
290
|
+
try {
|
|
291
|
+
await module.configuration.validateUpdate({
|
|
292
|
+
guildId: input.guildId,
|
|
293
|
+
value: configuration.value,
|
|
294
|
+
services: this.services,
|
|
295
|
+
});
|
|
296
|
+
resourceValidationValid = true;
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
resourceValidationValid = false;
|
|
300
|
+
}
|
|
301
|
+
let activationValid = null;
|
|
302
|
+
if (state?.enabled && module.validateActivation)
|
|
303
|
+
try {
|
|
304
|
+
await module.validateActivation({
|
|
305
|
+
guildId: input.guildId,
|
|
306
|
+
services: this.services,
|
|
307
|
+
});
|
|
308
|
+
activationValid = true;
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
activationValid = false;
|
|
312
|
+
}
|
|
313
|
+
const health = module.health
|
|
314
|
+
? await Promise.resolve(module.health()).catch(() => ({
|
|
315
|
+
status: "unhealthy",
|
|
316
|
+
message: "Module health check failed",
|
|
317
|
+
}))
|
|
318
|
+
: { status: "healthy", message: null };
|
|
319
|
+
const packaged = catalogue?.get(module.manifest.id) ?? null;
|
|
320
|
+
const requiredCapabilities = module.requiredDiscordCapabilities ?? [];
|
|
321
|
+
const missingCapabilities = requiredCapabilities.filter((capability) => (capability === "guild-members" && !capabilities.guildMembers) ||
|
|
322
|
+
(capability === "message-content" && !capabilities.messageContent));
|
|
323
|
+
const missingPermissions = missingRequiredPermissions(overview.botPermissions, module.manifest.permissions);
|
|
324
|
+
items.push({
|
|
325
|
+
moduleId: module.manifest.id,
|
|
326
|
+
name: module.manifest.name,
|
|
327
|
+
version: module.manifest.version,
|
|
328
|
+
enabled: state?.enabled ?? false,
|
|
329
|
+
configured: hasRequiredModuleConfiguration(module.configuration !== undefined, configuration),
|
|
330
|
+
configurationValid,
|
|
331
|
+
resourceValidationValid,
|
|
332
|
+
activationValid,
|
|
333
|
+
catalogueMatches: moduleCatalogueMatches(packaged, module.manifest),
|
|
334
|
+
requiredCapabilities,
|
|
335
|
+
missingCapabilities,
|
|
336
|
+
missingPermissions,
|
|
337
|
+
healthStatus: health.status,
|
|
338
|
+
healthMessage: health.message ?? null,
|
|
339
|
+
diagnosticsAvailable: module.health !== undefined,
|
|
340
|
+
});
|
|
341
|
+
if ((index + 1) % batchSize === 0)
|
|
342
|
+
await Promise.resolve();
|
|
343
|
+
}
|
|
344
|
+
return { available: true, items };
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return unavailable("not_supported");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
#guild(guildId) {
|
|
351
|
+
const guild = this.client.guilds.cache.get(guildId);
|
|
352
|
+
if (!guild)
|
|
353
|
+
throw new Error("Discord server is not owned by this worker");
|
|
354
|
+
return guild;
|
|
355
|
+
}
|
|
356
|
+
#unavailable(error, fallback) {
|
|
357
|
+
if (typeof error === "object" && error !== null && "code" in error) {
|
|
358
|
+
const code = Number(error.code);
|
|
359
|
+
if (code === 50_001 || code === 50_013)
|
|
360
|
+
return unavailable(fallback);
|
|
361
|
+
}
|
|
362
|
+
throw error instanceof Error
|
|
363
|
+
? error
|
|
364
|
+
: new Error("Discord guild inspection failed");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function normalizePermission(value) {
|
|
368
|
+
return value.replaceAll(/[^a-z0-9]/giu, "").toLowerCase();
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=discord-guild-health.js.map
|
package/dist/events.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface ModuleEventHandle {
|
|
|
7
7
|
queueDepth(): number;
|
|
8
8
|
}
|
|
9
9
|
export declare function reactionEmojiKey(reaction: MessageReaction | PartialMessageReaction): string;
|
|
10
|
+
export declare function reactionEmojiIdentifier(reaction: MessageReaction | PartialMessageReaction): string;
|
|
10
11
|
export declare function reactionCacheKey(emojiIdentifier: string): string;
|
|
11
12
|
interface ApprovedRoleMutation {
|
|
12
13
|
guildId: string;
|
|
@@ -20,6 +21,7 @@ export declare class DiscordMessageDeliveryService implements DiscordMessageServ
|
|
|
20
21
|
constructor(client: Client);
|
|
21
22
|
send(input: Parameters<DiscordMessageService["send"]>[0]): ReturnType<DiscordMessageService["send"]>;
|
|
22
23
|
sendDirect(input: Parameters<DiscordMessageService["sendDirect"]>[0]): ReturnType<DiscordMessageService["sendDirect"]>;
|
|
24
|
+
fetchSource(input: Parameters<DiscordMessageService["fetchSource"]>[0]): ReturnType<DiscordMessageService["fetchSource"]>;
|
|
23
25
|
}
|
|
24
26
|
export declare class DiscordMemberEnforcementService implements MemberEnforcementService {
|
|
25
27
|
#private;
|
|
@@ -110,6 +112,19 @@ export declare class DiscordResourceInspection implements DiscordResourceInspect
|
|
|
110
112
|
avatarUrl?: string;
|
|
111
113
|
isBot: boolean;
|
|
112
114
|
}[]>;
|
|
115
|
+
listMembers(input: {
|
|
116
|
+
guildId: string;
|
|
117
|
+
after?: string;
|
|
118
|
+
limit: number;
|
|
119
|
+
}): Promise<{
|
|
120
|
+
items: readonly {
|
|
121
|
+
userId: string;
|
|
122
|
+
displayName: string;
|
|
123
|
+
avatarUrl?: string;
|
|
124
|
+
isBot: boolean;
|
|
125
|
+
}[];
|
|
126
|
+
nextCursor?: string;
|
|
127
|
+
}>;
|
|
113
128
|
inspectRole(input: {
|
|
114
129
|
guildId: string;
|
|
115
130
|
roleId: string;
|