@helyx/bot 1.5.4 → 1.7.0
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 +19 -0
- package/README.md +5 -2
- package/command-root-catalogue.json +21 -5
- package/dist/cloud-connector.js +2 -0
- package/dist/cloud-control-dispatcher.js +20 -0
- package/dist/command-registration-policy.js +2 -0
- package/dist/control-directory-contracts.d.ts +50 -0
- package/dist/control-directory-contracts.js +2 -0
- package/dist/control-server-schemas.d.ts +3 -3
- package/dist/control-server.d.ts +10 -46
- package/dist/control-server.js +50 -3
- package/dist/creator-invite-service.d.ts +7 -0
- package/dist/creator-invite-service.js +17 -0
- package/dist/developer-operations-module.js +0 -55
- package/dist/diagnostic-command.js +1 -0
- package/dist/discord-guild-health.d.ts +2 -2
- package/dist/discord-guild-health.js +1 -0
- package/dist/events.d.ts +1 -0
- package/dist/events.js +2 -0
- package/dist/help-credentials.d.ts +25 -0
- package/dist/help-credentials.js +149 -0
- package/dist/help-module.d.ts +27 -0
- package/dist/help-module.js +288 -0
- package/dist/help-runtime.d.ts +3 -0
- package/dist/help-runtime.js +42 -0
- package/dist/help-transport.d.ts +53 -0
- package/dist/help-transport.js +267 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -0
- package/dist/interactions.js +10 -6
- package/dist/process-info-module.d.ts +3 -0
- package/dist/process-info-module.js +97 -0
- package/dist/server-overview-control.d.ts +35 -0
- package/dist/server-overview-control.js +362 -0
- package/dist/server-overview-discord.d.ts +33 -0
- package/dist/server-overview-discord.js +212 -0
- package/dist/server-overview-profile.d.ts +8 -0
- package/dist/server-overview-profile.js +62 -0
- package/dist/server-overview-routes.d.ts +4 -0
- package/dist/server-overview-routes.js +41 -0
- package/dist/shard-contracts.d.ts +66 -0
- package/dist/shard-contracts.js +23 -0
- package/dist/shard-coordinator.d.ts +1 -1
- package/dist/shard-router.d.ts +9 -0
- package/dist/shard-router.js +6 -0
- package/dist/shard-supervisor.d.ts +1 -1
- package/dist/shard-supervisor.js +8 -0
- package/dist/shard-worker.d.ts +1 -1
- package/dist/shard-worker.js +8 -0
- package/dist/verification-service.d.ts +4 -0
- package/dist/verification-service.js +10 -0
- package/documentation/README.md +1 -0
- package/documentation/examples/.env.example +29 -85
- package/documentation/guides/help-command.md +62 -0
- package/documentation/reference/environment.md +7 -6
- package/documentation/runbooks/installation-and-upgrades.md +4 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add the built-in private /help command with module selection, reviewed answer choices and local self-host fallback. Keep question selection and its answer in the same private message, show the original question above choices, and use a compact FAQ ID footer. Add opt-in encrypted online Help enrolment and fail closed for unavailable hosted publication state. Integrate authenticated Server Overview reads, bounded Discord profiles and protected invite provenance across direct, Cloud Connect and sharded runtimes.
|
|
8
|
+
|
|
9
|
+
## 1.6.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- Add public `/proc` process information for hosted and self-hosted bots, replacing
|
|
14
|
+
the private `/dev proc` path. Keep the reply private and independent of database
|
|
15
|
+
availability.
|
|
16
|
+
|
|
17
|
+
Add a PebbleHost starter profile with direct Node CLI scripts, flat starter ZIPs,
|
|
18
|
+
compact quoted environment examples and matching guided environment builder
|
|
19
|
+
output. Preserve existing custom settings and encryption keys when rebuilding
|
|
20
|
+
an environment file, and recognize quoted placeholders during setup.
|
|
21
|
+
|
|
3
22
|
## 1.5.4
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -164,8 +164,11 @@ paths are read-only:
|
|
|
164
164
|
|
|
165
165
|
- `/dev shards` reports bounded shard health, including the unsharded
|
|
166
166
|
compatibility baseline;
|
|
167
|
-
- `/dev bot` reports the bot build and bounded installed-module versions
|
|
168
|
-
|
|
167
|
+
- `/dev bot` reports the bot build and bounded installed-module versions.
|
|
168
|
+
|
|
169
|
+
The public `/proc` command reports the same safe process metrics on hosted and
|
|
170
|
+
self-hosted bots, in a server or bot DM. Anyone can run it; replies are ephemeral.
|
|
171
|
+
It works independently of database and module enablement checks.
|
|
169
172
|
|
|
170
173
|
On the official hosted bot only,
|
|
171
174
|
`HELYX_HOSTED_MODERATION_ENABLED=true` adds `/dev leave` and `/dev blacklist`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"revision":
|
|
3
|
+
"revision": 6,
|
|
4
4
|
"roots": [
|
|
5
5
|
{ "name": "access", "type": 1, "ownerModuleId": "helyx.account-gate" },
|
|
6
6
|
{ "name": "access", "type": 1, "ownerModuleId": "helyx.verification" },
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
{ "name": "games", "type": 1, "ownerModuleId": "helyx.counting" },
|
|
18
18
|
{ "name": "games", "type": 1, "ownerModuleId": "helyx.trivia" },
|
|
19
19
|
{ "name": "giveaway", "type": 1, "ownerModuleId": "helyx.giveaways" },
|
|
20
|
+
{ "name": "help", "type": 1, "ownerModuleId": "helyx.help" },
|
|
20
21
|
{ "name": "helyx", "type": 1, "ownerModuleId": "helyx.cloud-connect" },
|
|
21
22
|
{ "name": "helyx", "type": 1, "ownerModuleId": "helyx.management" },
|
|
22
23
|
{ "name": "helyx", "type": 1, "ownerModuleId": "helyx.oauth" },
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
{ "name": "mod", "type": 1, "ownerModuleId": "helyx.moderation" },
|
|
27
28
|
{ "name": "nexus", "type": 1, "ownerModuleId": "helyx.nexus-core" },
|
|
28
29
|
{ "name": "poll", "type": 1, "ownerModuleId": "helyx.polls" },
|
|
30
|
+
{ "name": "proc", "type": 1, "ownerModuleId": "helyx.process-info" },
|
|
29
31
|
{ "name": "profile", "type": 1, "ownerModuleId": "helyx.achievements" },
|
|
30
32
|
{ "name": "profile", "type": 1, "ownerModuleId": "helyx.levels" },
|
|
31
33
|
{ "name": "submit", "type": 1, "ownerModuleId": "helyx.feedback" },
|
|
@@ -61,6 +63,13 @@
|
|
|
61
63
|
"sourcePath": "apps/bot/src/diagnostic-command.ts",
|
|
62
64
|
"version": "0.1.0"
|
|
63
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"commands": ["help"],
|
|
68
|
+
"moduleId": "helyx.help",
|
|
69
|
+
"releaseStatus": "live",
|
|
70
|
+
"sourcePath": "apps/bot/src/help-module.ts",
|
|
71
|
+
"version": "0.1.0"
|
|
72
|
+
},
|
|
64
73
|
{
|
|
65
74
|
"commands": ["helyx.modules"],
|
|
66
75
|
"moduleId": "helyx.management",
|
|
@@ -96,6 +105,13 @@
|
|
|
96
105
|
"sourcePath": "apps/bot/src/privacy-module.ts",
|
|
97
106
|
"version": "0.1.0"
|
|
98
107
|
},
|
|
108
|
+
{
|
|
109
|
+
"commands": ["proc"],
|
|
110
|
+
"moduleId": "helyx.process-info",
|
|
111
|
+
"releaseStatus": "live",
|
|
112
|
+
"sourcePath": "apps/bot/src/process-info-module.ts",
|
|
113
|
+
"version": "0.1.0"
|
|
114
|
+
},
|
|
99
115
|
{
|
|
100
116
|
"commands": ["helyx.bug"],
|
|
101
117
|
"moduleId": "helyx.support",
|
|
@@ -116,7 +132,7 @@
|
|
|
116
132
|
"moduleId": "helyx.account-gate",
|
|
117
133
|
"packageName": "@helyx/module-account-gate",
|
|
118
134
|
"releaseStatus": "live",
|
|
119
|
-
"version": "2.1.
|
|
135
|
+
"version": "2.1.3"
|
|
120
136
|
},
|
|
121
137
|
{
|
|
122
138
|
"commands": [
|
|
@@ -127,7 +143,7 @@
|
|
|
127
143
|
"moduleId": "helyx.achievements",
|
|
128
144
|
"packageName": "@helyx/module-achievements",
|
|
129
145
|
"releaseStatus": "live",
|
|
130
|
-
"version": "2.1.
|
|
146
|
+
"version": "2.1.4"
|
|
131
147
|
},
|
|
132
148
|
{
|
|
133
149
|
"commands": ["afk"],
|
|
@@ -276,7 +292,7 @@
|
|
|
276
292
|
"moduleId": "helyx.reactionroles",
|
|
277
293
|
"packageName": "@helyx/module-reactionroles",
|
|
278
294
|
"releaseStatus": "live",
|
|
279
|
-
"version": "1.1.
|
|
295
|
+
"version": "1.1.3"
|
|
280
296
|
},
|
|
281
297
|
{
|
|
282
298
|
"commands": [],
|
|
@@ -448,5 +464,5 @@
|
|
|
448
464
|
}
|
|
449
465
|
]
|
|
450
466
|
},
|
|
451
|
-
"fingerprint": "
|
|
467
|
+
"fingerprint": "9bd09abbfbaaac0ddb932b6e25911b9a2eb8a3dd5febe92f5862ef903534b2e5"
|
|
452
468
|
}
|
package/dist/cloud-connector.js
CHANGED
|
@@ -904,6 +904,8 @@ function connectorCapabilities() {
|
|
|
904
904
|
"installation.health.read.v1",
|
|
905
905
|
"guild.state.read.v1",
|
|
906
906
|
"guild.user-identities.read.v1",
|
|
907
|
+
"guild.overview.read.v1",
|
|
908
|
+
"guild.overview.invite.revoke.v1",
|
|
907
909
|
"guild.dashboard.access.read.v1",
|
|
908
910
|
"guild.access-policy.read.v1",
|
|
909
911
|
"guild.access-policy.write.v1",
|
|
@@ -18,6 +18,26 @@ export class CloudControlDispatcher {
|
|
|
18
18
|
: undefined;
|
|
19
19
|
let result;
|
|
20
20
|
switch (request.operation) {
|
|
21
|
+
case "guild.overview.read.v1": {
|
|
22
|
+
const input = parseOperationInput(request.operation, request.input);
|
|
23
|
+
if (!this.control.readServerOverview)
|
|
24
|
+
throw new Error("Overview is unavailable");
|
|
25
|
+
result = await this.control.readServerOverview({
|
|
26
|
+
...input,
|
|
27
|
+
actorUserId: requiredActor(actorUserId),
|
|
28
|
+
});
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case "guild.overview.invite.revoke.v1": {
|
|
32
|
+
const input = parseOperationInput(request.operation, request.input);
|
|
33
|
+
if (!this.control.revokeServerOverviewInvite)
|
|
34
|
+
throw new Error("Invite management is unavailable");
|
|
35
|
+
result = await this.control.revokeServerOverviewInvite({
|
|
36
|
+
...input,
|
|
37
|
+
actorUserId: requiredActor(actorUserId),
|
|
38
|
+
});
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
21
41
|
case "installation.health.read.v1":
|
|
22
42
|
parseOperationInput(request.operation, request.input);
|
|
23
43
|
result = {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DiscordUserIdentityControl } from "@helyx/control-protocol/discord-user-identities";
|
|
2
|
+
import type { MessageResponse, PermissionActor, PublicAchievementProjectionPage } from "@helyx/sdk";
|
|
3
|
+
import type { OverviewDiscordDirectory } from "./server-overview-discord.js";
|
|
4
|
+
export interface BotControlGuildResources {
|
|
5
|
+
botGuildPermissions?: string[];
|
|
6
|
+
channels: Array<{
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
type: "guild_text" | "guild_announcement" | "guild_category" | "guild_forum" | "guild_media";
|
|
10
|
+
}>;
|
|
11
|
+
roles: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
colour: string;
|
|
15
|
+
position: number;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface BotPublicModuleProjection {
|
|
19
|
+
server: {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
memberCount: number;
|
|
23
|
+
};
|
|
24
|
+
projection: PublicAchievementProjectionPage;
|
|
25
|
+
}
|
|
26
|
+
export type BotGuildPresenceState = "installed" | "not_installed" | "unknown" | "unavailable";
|
|
27
|
+
export interface BotGuildPresenceObservation {
|
|
28
|
+
guildId: string;
|
|
29
|
+
state: BotGuildPresenceState;
|
|
30
|
+
observedAt: string;
|
|
31
|
+
}
|
|
32
|
+
export interface BotControlGuildResourceDirectory extends DiscordUserIdentityControl {
|
|
33
|
+
overview?: OverviewDiscordDirectory;
|
|
34
|
+
listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
|
|
35
|
+
getGuildResources(guildId: string): Promise<BotControlGuildResources>;
|
|
36
|
+
getGuildPublicSummary(guildId: string): Promise<{
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
memberCount: number;
|
|
40
|
+
}>;
|
|
41
|
+
sendToChannel(channelId: string, response: MessageResponse): Promise<{
|
|
42
|
+
messageId: string;
|
|
43
|
+
}>;
|
|
44
|
+
deleteChannelMessage(channelId: string, messageId: string): Promise<void>;
|
|
45
|
+
editChannelMessage(channelId: string, messageId: string, response: MessageResponse): Promise<void>;
|
|
46
|
+
addChannelMessageReaction(channelId: string, messageId: string, emojiIdentifier: string): Promise<void>;
|
|
47
|
+
resolveActor(guildId: string, actorUserId: string): Promise<PermissionActor>;
|
|
48
|
+
sendPrivateSupportNotification?(guildId: string, channelId: string, response: MessageResponse, notificationId: string): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=control-directory-contracts.d.ts.map
|
|
@@ -8,9 +8,9 @@ export declare const permissionState: z.ZodObject<{
|
|
|
8
8
|
module: z.ZodObject<{
|
|
9
9
|
preset: z.ZodEnum<{
|
|
10
10
|
everyone: "everyone";
|
|
11
|
+
custom: "custom";
|
|
11
12
|
moderators: "moderators";
|
|
12
13
|
administrators: "administrators";
|
|
13
|
-
custom: "custom";
|
|
14
14
|
}>;
|
|
15
15
|
allowedRoleIds: z.ZodArray<z.ZodString>;
|
|
16
16
|
deniedRoleIds: z.ZodArray<z.ZodString>;
|
|
@@ -22,9 +22,9 @@ export declare const permissionState: z.ZodObject<{
|
|
|
22
22
|
policy: z.ZodObject<{
|
|
23
23
|
preset: z.ZodEnum<{
|
|
24
24
|
everyone: "everyone";
|
|
25
|
+
custom: "custom";
|
|
25
26
|
moderators: "moderators";
|
|
26
27
|
administrators: "administrators";
|
|
27
|
-
custom: "custom";
|
|
28
28
|
}>;
|
|
29
29
|
allowedRoleIds: z.ZodArray<z.ZodString>;
|
|
30
30
|
deniedRoleIds: z.ZodArray<z.ZodString>;
|
|
@@ -59,8 +59,8 @@ export declare const managedResourceDetailSchema: z.ZodObject<{
|
|
|
59
59
|
availableActionIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
60
60
|
notices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
61
61
|
kind: z.ZodEnum<{
|
|
62
|
-
info: "info";
|
|
63
62
|
warning: "warning";
|
|
63
|
+
info: "info";
|
|
64
64
|
}>;
|
|
65
65
|
message: z.ZodString;
|
|
66
66
|
}, z.core.$strict>>>;
|
package/dist/control-server.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import type { BotControlGuildResources, BotPublicModuleProjection, BotGuildPresenceObservation, BotControlGuildResourceDirectory } from "./control-directory-contracts.js";
|
|
2
|
+
export type { BotControlGuildResources, BotPublicModuleProjection, BotGuildPresenceState, BotGuildPresenceObservation, BotControlGuildResourceDirectory, } from "./control-directory-contracts.js";
|
|
1
3
|
import { type DiscordUserIdentityControl, type DiscordUserIdentityRequest } from "@helyx/control-protocol/discord-user-identities";
|
|
4
|
+
import type { ServerOverviewControl } from "@helyx/control-protocol";
|
|
5
|
+
import { type OverviewDiscordDirectory } from "./server-overview-discord.js";
|
|
2
6
|
import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, PermissionPolicyRepository, type DatabasePool, type GuildShardOwnership } from "@helyx/database";
|
|
3
7
|
import type { MysqlPool } from "@helyx/database-mysql";
|
|
4
8
|
import type { CreatorOnboardingRequest, CreatorOnboardingResponse } from "@helyx/sdk";
|
|
5
9
|
export { mapBotControlError, safeBotControlFieldError, } from "./control-error-mapping.js";
|
|
6
|
-
import { type DashboardActionInspection, type DashboardActionResult, type FeatureAccessDecision, type GuildAccessPolicy, type ManagedResourceCreationIntent, type ManagedResourceDetail, type ManagedResourcePage, type ManagedResourceRelationPage, type ManagedResourceValue, type MessageResponse, type ModuleDefinition, type OAuthConsumerCleanupResult, type PermissionActor, type PrivacyCapturePage, type PrivacyErasePage, type PrivacyProviderExecutor, type
|
|
10
|
+
import { type DashboardActionInspection, type DashboardActionResult, type FeatureAccessDecision, type GuildAccessPolicy, type ManagedResourceCreationIntent, type ManagedResourceDetail, type ManagedResourcePage, type ManagedResourceRelationPage, type ManagedResourceValue, type MessageResponse, type ModuleDefinition, type OAuthConsumerCleanupResult, type PermissionActor, type PrivacyCapturePage, type PrivacyErasePage, type PrivacyProviderExecutor, type ServiceAccess } from "@helyx/sdk";
|
|
7
11
|
import { type ActionReceipt, type SignedAssessmentEnvelope } from "@helyx/verification-contracts";
|
|
8
12
|
import { type Client } from "discord.js";
|
|
9
13
|
import { type FastifyInstance } from "fastify";
|
|
@@ -29,53 +33,10 @@ interface DashboardLoggingPolicy {
|
|
|
29
33
|
enabled: boolean;
|
|
30
34
|
version: number;
|
|
31
35
|
}
|
|
32
|
-
export interface BotControlGuildResources {
|
|
33
|
-
channels: Array<{
|
|
34
|
-
id: string;
|
|
35
|
-
name: string;
|
|
36
|
-
type: "guild_text" | "guild_announcement" | "guild_category" | "guild_forum" | "guild_media";
|
|
37
|
-
}>;
|
|
38
|
-
roles: Array<{
|
|
39
|
-
id: string;
|
|
40
|
-
name: string;
|
|
41
|
-
colour: string;
|
|
42
|
-
position: number;
|
|
43
|
-
}>;
|
|
44
|
-
}
|
|
45
|
-
export interface BotPublicModuleProjection {
|
|
46
|
-
server: {
|
|
47
|
-
id: string;
|
|
48
|
-
name: string;
|
|
49
|
-
memberCount: number;
|
|
50
|
-
};
|
|
51
|
-
projection: PublicAchievementProjectionPage;
|
|
52
|
-
}
|
|
53
|
-
export type BotGuildPresenceState = "installed" | "not_installed" | "unknown" | "unavailable";
|
|
54
|
-
export interface BotGuildPresenceObservation {
|
|
55
|
-
guildId: string;
|
|
56
|
-
state: BotGuildPresenceState;
|
|
57
|
-
observedAt: string;
|
|
58
|
-
}
|
|
59
|
-
export interface BotControlGuildResourceDirectory extends DiscordUserIdentityControl {
|
|
60
|
-
listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
|
|
61
|
-
getGuildResources(guildId: string): Promise<BotControlGuildResources>;
|
|
62
|
-
getGuildPublicSummary(guildId: string): Promise<{
|
|
63
|
-
id: string;
|
|
64
|
-
name: string;
|
|
65
|
-
memberCount: number;
|
|
66
|
-
}>;
|
|
67
|
-
sendToChannel(channelId: string, response: MessageResponse): Promise<{
|
|
68
|
-
messageId: string;
|
|
69
|
-
}>;
|
|
70
|
-
deleteChannelMessage(channelId: string, messageId: string): Promise<void>;
|
|
71
|
-
editChannelMessage(channelId: string, messageId: string, response: MessageResponse): Promise<void>;
|
|
72
|
-
addChannelMessageReaction(channelId: string, messageId: string, emojiIdentifier: string): Promise<void>;
|
|
73
|
-
resolveActor(guildId: string, actorUserId: string): Promise<PermissionActor>;
|
|
74
|
-
sendPrivateSupportNotification?(guildId: string, channelId: string, response: MessageResponse, notificationId: string): Promise<void>;
|
|
75
|
-
}
|
|
76
36
|
export declare class DiscordGuildResourceDirectory implements BotControlGuildResourceDirectory {
|
|
77
37
|
#private;
|
|
78
38
|
private readonly client;
|
|
39
|
+
readonly overview: OverviewDiscordDirectory;
|
|
79
40
|
constructor(client: Client);
|
|
80
41
|
resolveDiscordUserIdentities(input: DiscordUserIdentityRequest): Promise<{
|
|
81
42
|
guildId: string;
|
|
@@ -110,6 +71,7 @@ export interface SupportNotificationInput {
|
|
|
110
71
|
source: "dashboard_bug" | "dashboard_support" | "dashboard_security" | "discord_command";
|
|
111
72
|
}
|
|
112
73
|
export interface BotControlState {
|
|
74
|
+
botGuildPermissions?: string[];
|
|
113
75
|
discordCapabilities?: {
|
|
114
76
|
messageContent: boolean;
|
|
115
77
|
};
|
|
@@ -134,7 +96,7 @@ export interface DashboardGuildAccessState extends GuildAccessPolicy {
|
|
|
134
96
|
actorHighestRolePosition: number | null;
|
|
135
97
|
updatedAt: string | null;
|
|
136
98
|
}
|
|
137
|
-
export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler, ManagedResourceCreateDefaultsControl, ChannelAuditControl, DiscordUserIdentityControl {
|
|
99
|
+
export interface BotControlHandler extends ServerOverviewControl, FirstTimeCommandSetupHandler, DashboardActionHandler, ManagedResourceCreateDefaultsControl, ChannelAuditControl, DiscordUserIdentityControl {
|
|
138
100
|
creatorOnboarding?(input: CreatorOnboardingRequest): Promise<CreatorOnboardingResponse>;
|
|
139
101
|
cleanupOAuthConsumer(input: {
|
|
140
102
|
consumerBindingId: string;
|
|
@@ -363,6 +325,8 @@ export declare class BotControlService implements BotControlHandler {
|
|
|
363
325
|
private readonly supportNotifications?;
|
|
364
326
|
private readonly presenceOwnership?;
|
|
365
327
|
private readonly presenceShardCount?;
|
|
328
|
+
readonly readServerOverview: NonNullable<ServerOverviewControl["readServerOverview"]>;
|
|
329
|
+
readonly revokeServerOverviewInvite: NonNullable<ServerOverviewControl["revokeServerOverviewInvite"]>;
|
|
366
330
|
readonly readChannelAuditConfiguration: Required<ChannelAuditControl>["readChannelAuditConfiguration"];
|
|
367
331
|
readonly checkChannelAudit: Required<ChannelAuditControl>["checkChannelAudit"];
|
|
368
332
|
constructor(databaseOrPersistence: DatabasePool | BotControlPersistence, modules: readonly ModuleDefinition[], resources: BotControlGuildResourceDirectory, services: ServiceAccess, privacyExecutor?: PrivacyProviderExecutor, supportNotifications?: {
|
package/dist/control-server.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { overviewReadContext } from "./server-overview-profile.js";
|
|
1
2
|
import { discordUserIdentityRequestSchema, } from "@helyx/control-protocol/discord-user-identities";
|
|
3
|
+
import { createServerOverviewControl } from "./server-overview-control.js";
|
|
4
|
+
import { createOverviewDiscordDirectory, } from "./server-overview-discord.js";
|
|
5
|
+
import { registerServerOverviewControlRoutes } from "./server-overview-routes.js";
|
|
2
6
|
import { createDiscordUserIdentityLookup, } from "./discord-user-identities.js";
|
|
3
7
|
import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, OptimisticConcurrencyError, PermissionPolicyRepository, withTransaction, } from "@helyx/database";
|
|
4
8
|
import { safeError } from "@helyx/core";
|
|
@@ -44,9 +48,11 @@ import { validateDiscordUserSettingSelections, validateDiscordSettingSelections,
|
|
|
44
48
|
import { configurationValue, guildAccessPolicy, managedResourceDeleteResultSchema, managedResourceDetailSchema, managedResourceFilters, managedResourceObject, managedResourcePageSchema, managedResourceRelationPageSchema, moduleId, mutationContext, permissionState, resourceActionId, resourceCursor, resourceId, snowflake, } from "./control-server-schemas.js";
|
|
45
49
|
export class DiscordGuildResourceDirectory {
|
|
46
50
|
client;
|
|
51
|
+
overview;
|
|
47
52
|
#identities;
|
|
48
53
|
constructor(client) {
|
|
49
54
|
this.client = client;
|
|
55
|
+
this.overview = createOverviewDiscordDirectory(client);
|
|
50
56
|
this.#identities = createDiscordUserIdentityLookup(client);
|
|
51
57
|
}
|
|
52
58
|
resolveDiscordUserIdentities(input) {
|
|
@@ -107,6 +113,9 @@ export class DiscordGuildResourceDirectory {
|
|
|
107
113
|
colour: role.hexColor,
|
|
108
114
|
position: role.position,
|
|
109
115
|
})),
|
|
116
|
+
...(guild.members?.me
|
|
117
|
+
? { botGuildPermissions: guild.members.me.permissions.toArray() }
|
|
118
|
+
: {}),
|
|
110
119
|
};
|
|
111
120
|
}
|
|
112
121
|
catch (error) {
|
|
@@ -242,6 +251,9 @@ export class BotControlService {
|
|
|
242
251
|
#services;
|
|
243
252
|
#privacyProviders;
|
|
244
253
|
#persistence;
|
|
254
|
+
#overview;
|
|
255
|
+
readServerOverview;
|
|
256
|
+
revokeServerOverviewInvite;
|
|
245
257
|
readChannelAuditConfiguration;
|
|
246
258
|
checkChannelAudit;
|
|
247
259
|
constructor(databaseOrPersistence, modules, resources, services, privacyExecutor, supportNotifications, presenceOwnership, presenceShardCount, statusModules) {
|
|
@@ -276,6 +288,33 @@ export class BotControlService {
|
|
|
276
288
|
});
|
|
277
289
|
this.readChannelAuditConfiguration = audit.readChannelAuditConfiguration;
|
|
278
290
|
this.checkChannelAudit = audit.checkChannelAudit;
|
|
291
|
+
this.#overview = createServerOverviewControl({
|
|
292
|
+
modules: this.#modules,
|
|
293
|
+
services: this.#services,
|
|
294
|
+
permissions: this.#permissions,
|
|
295
|
+
directory: resources.overview,
|
|
296
|
+
auditInviteRevoke: async (input) => {
|
|
297
|
+
await this.#persistence.repositories.audit.append({
|
|
298
|
+
guildId: input.guildId,
|
|
299
|
+
actorUserId: input.actorUserId,
|
|
300
|
+
action: `server.invite.${input.outcome}`,
|
|
301
|
+
source: "dashboard",
|
|
302
|
+
correlationId: input.correlationId,
|
|
303
|
+
targetType: "discord-invite",
|
|
304
|
+
targetId: createHash("sha256")
|
|
305
|
+
.update(`${input.guildId}:${input.code}`)
|
|
306
|
+
.digest("hex"),
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
resolveActor: (guildId, actorUserId) => resources.resolveActor(guildId, actorUserId),
|
|
310
|
+
readGuildPolicy: (guildId) => this.#readGuildAccessPolicy(guildId),
|
|
311
|
+
listModules: (guildId) => this.#persistence.repositories.installations.listModules(guildId),
|
|
312
|
+
requireAccessible: (guildId, moduleId) => this.#requireModuleAccessible(guildId, moduleId),
|
|
313
|
+
context: (guildId, actor) => overviewReadContext(guildId, actor, this.#services),
|
|
314
|
+
});
|
|
315
|
+
this.readServerOverview = this.#overview.readServerOverview.bind(this.#overview);
|
|
316
|
+
this.revokeServerOverviewInvite =
|
|
317
|
+
this.#overview.revokeServerOverviewInvite.bind(this.#overview);
|
|
279
318
|
}
|
|
280
319
|
async creatorOnboarding(input) {
|
|
281
320
|
return executeCreatorOnboarding(input, {
|
|
@@ -994,7 +1033,9 @@ export class BotControlService {
|
|
|
994
1033
|
const declaredIntents = declaration?.creationIntents ?? ["publish"];
|
|
995
1034
|
if (!declaredIntents.includes(input.intent))
|
|
996
1035
|
throw new DashboardActionValidationError("This creation intent is not declared by the module.");
|
|
997
|
-
|
|
1036
|
+
const result = normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.create(context, input.value, input.intent)), managedResourceColumnKeys(module, input.resourceType));
|
|
1037
|
+
this.#overview.invalidate(input.guildId);
|
|
1038
|
+
return result;
|
|
998
1039
|
}
|
|
999
1040
|
async updateManagedResource(input) {
|
|
1000
1041
|
await this.#requireModuleAccessible(input.guildId, input.moduleId);
|
|
@@ -1005,11 +1046,13 @@ export class BotControlService {
|
|
|
1005
1046
|
declaration?.featureId,
|
|
1006
1047
|
...resourceFieldFeatureIds(declaration?.fields ?? [], input.value),
|
|
1007
1048
|
]);
|
|
1008
|
-
|
|
1049
|
+
const result = normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.update(await this.#managedResourceContext(input, module, "update"), {
|
|
1009
1050
|
resourceId: input.resourceId,
|
|
1010
1051
|
expectedRevision: input.expectedRevision,
|
|
1011
1052
|
value: input.value,
|
|
1012
1053
|
})), managedResourceColumnKeys(module, input.resourceType));
|
|
1054
|
+
this.#overview.invalidate(input.guildId);
|
|
1055
|
+
return result;
|
|
1013
1056
|
}
|
|
1014
1057
|
async deleteManagedResource(input) {
|
|
1015
1058
|
await this.#requireModuleAccessible(input.guildId, input.moduleId);
|
|
@@ -1017,10 +1060,12 @@ export class BotControlService {
|
|
|
1017
1060
|
this.#requireManagedResourceOperation(module, input.resourceType, "delete");
|
|
1018
1061
|
const declaration = module.manifest.dashboard?.resources?.find(({ id }) => id === input.resourceType);
|
|
1019
1062
|
await requireDashboardFeatures(this.#services, input.guildId, input.moduleId, [declaration?.featureId]);
|
|
1020
|
-
|
|
1063
|
+
const result = managedResourceDeleteResultSchema.parse(await resource.delete(await this.#managedResourceContext(input, module, "delete"), {
|
|
1021
1064
|
resourceId: input.resourceId,
|
|
1022
1065
|
expectedRevision: input.expectedRevision,
|
|
1023
1066
|
}));
|
|
1067
|
+
this.#overview.invalidate(input.guildId);
|
|
1068
|
+
return result;
|
|
1024
1069
|
}
|
|
1025
1070
|
async executeManagedResourceAction(input) {
|
|
1026
1071
|
await this.#requireModuleAccessible(input.guildId, input.moduleId);
|
|
@@ -1046,6 +1091,7 @@ export class BotControlService {
|
|
|
1046
1091
|
expectedRevision: input.expectedRevision,
|
|
1047
1092
|
value: input.value,
|
|
1048
1093
|
});
|
|
1094
|
+
this.#overview.invalidate(input.guildId);
|
|
1049
1095
|
return result === null
|
|
1050
1096
|
? null
|
|
1051
1097
|
: normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
@@ -1523,6 +1569,7 @@ export async function createBotControlServer(input) {
|
|
|
1523
1569
|
return input.service.setModuleStatus({ ...params, ...body });
|
|
1524
1570
|
});
|
|
1525
1571
|
registerDashboardActionRoutes(server, input.service);
|
|
1572
|
+
registerServerOverviewControlRoutes(server, input.service);
|
|
1526
1573
|
registerChannelAuditRoutes(server, input.service);
|
|
1527
1574
|
server.get("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType", async (request) => {
|
|
1528
1575
|
const params = z
|
|
@@ -8,6 +8,13 @@ export declare class DiscordCreatorInviteService implements CreatorInviteService
|
|
|
8
8
|
private readonly repository;
|
|
9
9
|
private readonly services;
|
|
10
10
|
private readonly module;
|
|
11
|
+
identifyInvites(input: {
|
|
12
|
+
guildId: string;
|
|
13
|
+
codes: readonly string[];
|
|
14
|
+
}): Promise<readonly {
|
|
15
|
+
code: string;
|
|
16
|
+
reference: string;
|
|
17
|
+
}[]>;
|
|
11
18
|
constructor(client: Client, repository: CreatorInviteRepository, services: ServiceAccess, module: ModuleDefinition | undefined, encryption: string | {
|
|
12
19
|
inviteEncryptionKey: string;
|
|
13
20
|
});
|
|
@@ -16,6 +16,23 @@ export class DiscordCreatorInviteService {
|
|
|
16
16
|
repository;
|
|
17
17
|
services;
|
|
18
18
|
module;
|
|
19
|
+
async identifyInvites(input) {
|
|
20
|
+
validateId(input.guildId);
|
|
21
|
+
if (input.codes.length > 15 ||
|
|
22
|
+
input.codes.some((code) => !/^[A-Za-z0-9_-]{1,100}$/u.test(code)))
|
|
23
|
+
throw new CreatorInviteOperationError("unavailable");
|
|
24
|
+
this.#ownedGuild(input.guildId);
|
|
25
|
+
if (!(await this.repository.isInstalled()))
|
|
26
|
+
return [];
|
|
27
|
+
const requested = new Set(input.codes);
|
|
28
|
+
const found = [];
|
|
29
|
+
for (const record of await this.repository.listOwnedForGuild(input.guildId)) {
|
|
30
|
+
const code = this.#decrypt(record);
|
|
31
|
+
if (requested.has(code))
|
|
32
|
+
found.push({ code, reference: record.reference });
|
|
33
|
+
}
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
19
36
|
#key;
|
|
20
37
|
#rest = null;
|
|
21
38
|
#cleanupCursor;
|
|
@@ -27,7 +27,6 @@ export function createDeveloperOperationsModule(input) {
|
|
|
27
27
|
"dev.shards",
|
|
28
28
|
"dev.bot",
|
|
29
29
|
"dev.modules",
|
|
30
|
-
"dev.proc",
|
|
31
30
|
"dev.trivia-data",
|
|
32
31
|
"dev.leave",
|
|
33
32
|
"dev.blacklist",
|
|
@@ -166,41 +165,6 @@ export function createDeveloperOperationsModule(input) {
|
|
|
166
165
|
});
|
|
167
166
|
},
|
|
168
167
|
},
|
|
169
|
-
{
|
|
170
|
-
name: "proc",
|
|
171
|
-
permissionId: "dev.proc",
|
|
172
|
-
description: "Show safe process resource and uptime information",
|
|
173
|
-
acknowledgement: "immediate-response",
|
|
174
|
-
authorization: "self-authorized",
|
|
175
|
-
async execute(context) {
|
|
176
|
-
if (!(await authorizeDeveloperCommand(context)))
|
|
177
|
-
return;
|
|
178
|
-
const memory = process.memoryUsage();
|
|
179
|
-
const cpu = process.cpuUsage();
|
|
180
|
-
await context.reply({
|
|
181
|
-
ephemeral: true,
|
|
182
|
-
componentsV2: {
|
|
183
|
-
accentColor: 0x2563eb,
|
|
184
|
-
text: [
|
|
185
|
-
{
|
|
186
|
-
content: [
|
|
187
|
-
"## Process information",
|
|
188
|
-
`Runtime: ${input.processLabel}`,
|
|
189
|
-
`Uptime: ${formatDuration(process.uptime())}`,
|
|
190
|
-
`RSS memory: ${formatBytes(memory.rss)}`,
|
|
191
|
-
`Heap: ${formatBytes(memory.heapUsed)} used / ${formatBytes(memory.heapTotal)} allocated`,
|
|
192
|
-
`External memory: ${formatBytes(memory.external)}`,
|
|
193
|
-
`CPU time: ${formatCpu(cpu.user)} user / ${formatCpu(cpu.system)} system`,
|
|
194
|
-
`Node: ${safeInlineCode(process.version)}`,
|
|
195
|
-
`Platform: ${safeInlineCode(`${process.platform}/${process.arch}`)}`,
|
|
196
|
-
`PID: ${process.pid}`,
|
|
197
|
-
].join("\n"),
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
168
|
{
|
|
205
169
|
name: "trivia-data",
|
|
206
170
|
permissionId: "dev.trivia-data",
|
|
@@ -411,25 +375,6 @@ function categoryLabel(category) {
|
|
|
411
375
|
.join(" ")
|
|
412
376
|
.replace("And", "and");
|
|
413
377
|
}
|
|
414
|
-
function formatBytes(value) {
|
|
415
|
-
return `${(value / (1024 * 1024)).toFixed(1)} MiB`;
|
|
416
|
-
}
|
|
417
|
-
function formatCpu(microseconds) {
|
|
418
|
-
return `${(microseconds / 1_000_000).toFixed(2)}s`;
|
|
419
|
-
}
|
|
420
|
-
function formatDuration(seconds) {
|
|
421
|
-
const total = Math.max(0, Math.floor(seconds));
|
|
422
|
-
const days = Math.floor(total / 86_400);
|
|
423
|
-
const hours = Math.floor((total % 86_400) / 3_600);
|
|
424
|
-
const minutes = Math.floor((total % 3_600) / 60);
|
|
425
|
-
const remainder = total % 60;
|
|
426
|
-
return [
|
|
427
|
-
...(days > 0 ? [`${days}d`] : []),
|
|
428
|
-
...(days > 0 || hours > 0 ? [`${hours}h`] : []),
|
|
429
|
-
`${minutes}m`,
|
|
430
|
-
`${remainder}s`,
|
|
431
|
-
].join(" ");
|
|
432
|
-
}
|
|
433
378
|
export class WorkerShardHealthProvider {
|
|
434
379
|
#pending = new Map();
|
|
435
380
|
getHealth() {
|
|
@@ -163,8 +163,8 @@ export declare class DiscordGuildHealthInspection implements DiscordGuildHealthI
|
|
|
163
163
|
resourceValidationValid: boolean | null;
|
|
164
164
|
activationValid: boolean | null;
|
|
165
165
|
catalogueMatches: boolean;
|
|
166
|
-
requiredCapabilities: readonly ("
|
|
167
|
-
missingCapabilities: ("
|
|
166
|
+
requiredCapabilities: readonly ("guild-members" | "message-content")[];
|
|
167
|
+
missingCapabilities: ("guild-members" | "message-content")[];
|
|
168
168
|
missingPermissions: string[];
|
|
169
169
|
healthStatus: "healthy" | "degraded" | "unhealthy";
|
|
170
170
|
healthMessage: string | null;
|
package/dist/events.d.ts
CHANGED
package/dist/events.js
CHANGED
|
@@ -1802,6 +1802,7 @@ export class DiscordResourceInspection {
|
|
|
1802
1802
|
const member = members.get(userId) ?? null;
|
|
1803
1803
|
const cachedUser = this.client.users?.cache?.get(userId);
|
|
1804
1804
|
const avatarUrl = memberAvatarUrl(member);
|
|
1805
|
+
const isBot = member?.user?.bot ?? cachedUser?.bot;
|
|
1805
1806
|
return {
|
|
1806
1807
|
userId,
|
|
1807
1808
|
displayName: member?.displayName ??
|
|
@@ -1809,6 +1810,7 @@ export class DiscordResourceInspection {
|
|
|
1809
1810
|
cachedUser?.username ??
|
|
1810
1811
|
null,
|
|
1811
1812
|
...(avatarUrl ? { avatarUrl } : {}),
|
|
1813
|
+
...(typeof isBot === "boolean" ? { isBot } : {}),
|
|
1812
1814
|
};
|
|
1813
1815
|
});
|
|
1814
1816
|
}
|