@herdctl/discord 0.0.1
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/dist/__tests__/auto-mode-handler.test.d.ts +2 -0
- package/dist/__tests__/auto-mode-handler.test.d.ts.map +1 -0
- package/dist/__tests__/auto-mode-handler.test.js +362 -0
- package/dist/__tests__/auto-mode-handler.test.js.map +1 -0
- package/dist/__tests__/discord-connector.test.d.ts +2 -0
- package/dist/__tests__/discord-connector.test.d.ts.map +1 -0
- package/dist/__tests__/discord-connector.test.js +958 -0
- package/dist/__tests__/discord-connector.test.js.map +1 -0
- package/dist/__tests__/error-handler.test.d.ts +2 -0
- package/dist/__tests__/error-handler.test.d.ts.map +1 -0
- package/dist/__tests__/error-handler.test.js +509 -0
- package/dist/__tests__/error-handler.test.js.map +1 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +152 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/logger.test.d.ts +2 -0
- package/dist/__tests__/logger.test.d.ts.map +1 -0
- package/dist/__tests__/logger.test.js +282 -0
- package/dist/__tests__/logger.test.js.map +1 -0
- package/dist/__tests__/mention-handler.test.d.ts +2 -0
- package/dist/__tests__/mention-handler.test.d.ts.map +1 -0
- package/dist/__tests__/mention-handler.test.js +547 -0
- package/dist/__tests__/mention-handler.test.js.map +1 -0
- package/dist/auto-mode-handler.d.ts +145 -0
- package/dist/auto-mode-handler.d.ts.map +1 -0
- package/dist/auto-mode-handler.js +211 -0
- package/dist/auto-mode-handler.js.map +1 -0
- package/dist/commands/__tests__/command-manager.test.d.ts +2 -0
- package/dist/commands/__tests__/command-manager.test.d.ts.map +1 -0
- package/dist/commands/__tests__/command-manager.test.js +307 -0
- package/dist/commands/__tests__/command-manager.test.js.map +1 -0
- package/dist/commands/__tests__/help.test.d.ts +2 -0
- package/dist/commands/__tests__/help.test.d.ts.map +1 -0
- package/dist/commands/__tests__/help.test.js +105 -0
- package/dist/commands/__tests__/help.test.js.map +1 -0
- package/dist/commands/__tests__/reset.test.d.ts +2 -0
- package/dist/commands/__tests__/reset.test.d.ts.map +1 -0
- package/dist/commands/__tests__/reset.test.js +140 -0
- package/dist/commands/__tests__/reset.test.js.map +1 -0
- package/dist/commands/__tests__/status.test.d.ts +2 -0
- package/dist/commands/__tests__/status.test.d.ts.map +1 -0
- package/dist/commands/__tests__/status.test.js +205 -0
- package/dist/commands/__tests__/status.test.js.map +1 -0
- package/dist/commands/command-manager.d.ts +66 -0
- package/dist/commands/command-manager.d.ts.map +1 -0
- package/dist/commands/command-manager.js +191 -0
- package/dist/commands/command-manager.js.map +1 -0
- package/dist/commands/help.d.ts +8 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +27 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +12 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +13 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/reset.d.ts +9 -0
- package/dist/commands/reset.d.ts.map +1 -0
- package/dist/commands/reset.js +28 -0
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +102 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +87 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +8 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/discord-connector.d.ts +154 -0
- package/dist/discord-connector.d.ts.map +1 -0
- package/dist/discord-connector.js +638 -0
- package/dist/discord-connector.js.map +1 -0
- package/dist/error-handler.d.ts +237 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +433 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +61 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +77 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +119 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +198 -0
- package/dist/logger.js.map +1 -0
- package/dist/mention-handler.d.ts +176 -0
- package/dist/mention-handler.d.ts.map +1 -0
- package/dist/mention-handler.js +236 -0
- package/dist/mention-handler.js.map +1 -0
- package/dist/session-manager/__tests__/errors.test.d.ts +2 -0
- package/dist/session-manager/__tests__/errors.test.d.ts.map +1 -0
- package/dist/session-manager/__tests__/errors.test.js +124 -0
- package/dist/session-manager/__tests__/errors.test.js.map +1 -0
- package/dist/session-manager/__tests__/session-manager.test.d.ts +2 -0
- package/dist/session-manager/__tests__/session-manager.test.d.ts.map +1 -0
- package/dist/session-manager/__tests__/session-manager.test.js +517 -0
- package/dist/session-manager/__tests__/session-manager.test.js.map +1 -0
- package/dist/session-manager/__tests__/types.test.d.ts +2 -0
- package/dist/session-manager/__tests__/types.test.d.ts.map +1 -0
- package/dist/session-manager/__tests__/types.test.js +169 -0
- package/dist/session-manager/__tests__/types.test.js.map +1 -0
- package/dist/session-manager/errors.d.ts +58 -0
- package/dist/session-manager/errors.d.ts.map +1 -0
- package/dist/session-manager/errors.js +70 -0
- package/dist/session-manager/errors.js.map +1 -0
- package/dist/session-manager/index.d.ts +11 -0
- package/dist/session-manager/index.d.ts.map +1 -0
- package/dist/session-manager/index.js +12 -0
- package/dist/session-manager/index.js.map +1 -0
- package/dist/session-manager/session-manager.d.ts +107 -0
- package/dist/session-manager/session-manager.d.ts.map +1 -0
- package/dist/session-manager/session-manager.js +347 -0
- package/dist/session-manager/session-manager.js.map +1 -0
- package/dist/session-manager/types.d.ts +167 -0
- package/dist/session-manager/types.d.ts.map +1 -0
- package/dist/session-manager/types.js +57 -0
- package/dist/session-manager/types.js.map +1 -0
- package/dist/types.d.ts +323 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/__tests__/formatting.test.d.ts +2 -0
- package/dist/utils/__tests__/formatting.test.d.ts.map +1 -0
- package/dist/utils/__tests__/formatting.test.js +571 -0
- package/dist/utils/__tests__/formatting.test.js.map +1 -0
- package/dist/utils/formatting.d.ts +211 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/formatting.js +305 -0
- package/dist/utils/formatting.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto mode handler for Discord DMs and dedicated channels
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for:
|
|
5
|
+
* - Checking if DMs should be processed (auto mode by default)
|
|
6
|
+
* - Filtering users via allowlist/blocklist
|
|
7
|
+
* - Determining channel mode configuration
|
|
8
|
+
*/
|
|
9
|
+
import type { DiscordDM, DiscordChannel, DiscordGuild } from "@herdctl/core";
|
|
10
|
+
/**
|
|
11
|
+
* Result of checking if a user is allowed to DM the bot
|
|
12
|
+
*/
|
|
13
|
+
export interface DMFilterResult {
|
|
14
|
+
/** Whether the user is allowed to send DMs */
|
|
15
|
+
allowed: boolean;
|
|
16
|
+
/** Reason for filtering decision */
|
|
17
|
+
reason: "allowed" | "dm_disabled" | "not_in_allowlist" | "in_blocklist";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for DM handling
|
|
21
|
+
*/
|
|
22
|
+
export interface DMConfig {
|
|
23
|
+
/** Whether DMs are enabled */
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
/** Mode for DM processing */
|
|
26
|
+
mode: "mention" | "auto";
|
|
27
|
+
/** User IDs that are explicitly allowed (if set, only these users can DM) */
|
|
28
|
+
allowlist?: string[];
|
|
29
|
+
/** User IDs that are explicitly blocked */
|
|
30
|
+
blocklist?: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Result of resolving channel configuration
|
|
34
|
+
*/
|
|
35
|
+
export interface ResolvedChannelConfig {
|
|
36
|
+
/** The mode for this channel */
|
|
37
|
+
mode: "mention" | "auto";
|
|
38
|
+
/** Number of context messages to include */
|
|
39
|
+
contextMessages: number;
|
|
40
|
+
/** Whether this is a DM channel */
|
|
41
|
+
isDM: boolean;
|
|
42
|
+
/** The guild ID if applicable */
|
|
43
|
+
guildId: string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if DMs are enabled based on configuration
|
|
47
|
+
*
|
|
48
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
49
|
+
* @returns true if DMs are enabled, false otherwise
|
|
50
|
+
*/
|
|
51
|
+
export declare function isDMEnabled(dmConfig?: DiscordDM): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Get the mode for DM processing
|
|
54
|
+
*
|
|
55
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
56
|
+
* @returns The mode for DM processing (defaults to "auto")
|
|
57
|
+
*/
|
|
58
|
+
export declare function getDMMode(dmConfig?: DiscordDM): "mention" | "auto";
|
|
59
|
+
/**
|
|
60
|
+
* Check if a user is allowed to send DMs to the bot
|
|
61
|
+
*
|
|
62
|
+
* Filtering rules:
|
|
63
|
+
* 1. If DMs are disabled, no users are allowed
|
|
64
|
+
* 2. If a blocklist is defined and user is on it, they are blocked
|
|
65
|
+
* 3. If an allowlist is defined, only users on it are allowed
|
|
66
|
+
* 4. If neither list is defined, all users are allowed
|
|
67
|
+
*
|
|
68
|
+
* @param userId - Discord user ID to check
|
|
69
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
70
|
+
* @returns Filter result with allowed status and reason
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const result = checkDMUserFilter("123456789", dmConfig);
|
|
75
|
+
* if (!result.allowed) {
|
|
76
|
+
* console.log(`User blocked: ${result.reason}`);
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function checkDMUserFilter(userId: string, dmConfig?: DiscordDM): DMFilterResult;
|
|
81
|
+
/**
|
|
82
|
+
* Default number of context messages for DMs
|
|
83
|
+
*/
|
|
84
|
+
export declare const DEFAULT_DM_CONTEXT_MESSAGES = 10;
|
|
85
|
+
/**
|
|
86
|
+
* Default number of context messages for channels
|
|
87
|
+
*/
|
|
88
|
+
export declare const DEFAULT_CHANNEL_CONTEXT_MESSAGES = 10;
|
|
89
|
+
/**
|
|
90
|
+
* Find channel configuration from guild config
|
|
91
|
+
*
|
|
92
|
+
* @param channelId - Discord channel ID
|
|
93
|
+
* @param guilds - Array of guild configurations
|
|
94
|
+
* @returns Channel config and guild ID, or null if not found
|
|
95
|
+
*/
|
|
96
|
+
export declare function findChannelConfig(channelId: string, guilds: DiscordGuild[]): {
|
|
97
|
+
channel: DiscordChannel;
|
|
98
|
+
guildId: string;
|
|
99
|
+
} | null;
|
|
100
|
+
/**
|
|
101
|
+
* Resolve channel configuration for a message
|
|
102
|
+
*
|
|
103
|
+
* Determines the mode and context settings for a given channel,
|
|
104
|
+
* handling both guild channels and DMs appropriately.
|
|
105
|
+
*
|
|
106
|
+
* @param channelId - Discord channel ID
|
|
107
|
+
* @param guildId - Guild ID (null for DMs)
|
|
108
|
+
* @param guilds - Array of guild configurations
|
|
109
|
+
* @param dmConfig - Global DM configuration
|
|
110
|
+
* @returns Resolved channel configuration or null if channel not configured
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const config = resolveChannelConfig(
|
|
115
|
+
* message.channel.id,
|
|
116
|
+
* message.guildId,
|
|
117
|
+
* discordConfig.guilds,
|
|
118
|
+
* discordConfig.dm
|
|
119
|
+
* );
|
|
120
|
+
*
|
|
121
|
+
* if (config) {
|
|
122
|
+
* if (config.mode === 'auto') {
|
|
123
|
+
* // Process all non-bot messages
|
|
124
|
+
* } else {
|
|
125
|
+
* // Only process mentions
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveChannelConfig(channelId: string, guildId: string | null, guilds: DiscordGuild[], dmConfig?: DiscordDM): ResolvedChannelConfig | null;
|
|
131
|
+
/**
|
|
132
|
+
* Check if a message should be processed in auto mode
|
|
133
|
+
*
|
|
134
|
+
* In auto mode:
|
|
135
|
+
* - All non-bot messages are processed
|
|
136
|
+
* - No mention is required
|
|
137
|
+
* - Full conversation context is maintained
|
|
138
|
+
*
|
|
139
|
+
* @param isBot - Whether the message author is a bot
|
|
140
|
+
* @param mode - The channel mode
|
|
141
|
+
* @param wasMentioned - Whether the bot was mentioned
|
|
142
|
+
* @returns true if the message should be processed
|
|
143
|
+
*/
|
|
144
|
+
export declare function shouldProcessInMode(isBot: boolean, mode: "mention" | "auto", wasMentioned: boolean): boolean;
|
|
145
|
+
//# sourceMappingURL=auto-mode-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-mode-handler.d.ts","sourceRoot":"","sources":["../src/auto-mode-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAM7E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,kBAAkB,GAAG,cAAc,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,4CAA4C;IAC5C,eAAe,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,iCAAiC;IACjC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAMlE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,SAAS,GACnB,cAAc,CA4ChB;AAMD;;GAEG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EAAE,GACrB;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQrD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,qBAAqB,GAAG,IAAI,CAiC9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,GAAG,MAAM,EACxB,YAAY,EAAE,OAAO,GACpB,OAAO,CAaT"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto mode handler for Discord DMs and dedicated channels
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for:
|
|
5
|
+
* - Checking if DMs should be processed (auto mode by default)
|
|
6
|
+
* - Filtering users via allowlist/blocklist
|
|
7
|
+
* - Determining channel mode configuration
|
|
8
|
+
*/
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// DM Filtering
|
|
11
|
+
// =============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Check if DMs are enabled based on configuration
|
|
14
|
+
*
|
|
15
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
16
|
+
* @returns true if DMs are enabled, false otherwise
|
|
17
|
+
*/
|
|
18
|
+
export function isDMEnabled(dmConfig) {
|
|
19
|
+
// If no DM config provided, DMs are enabled by default
|
|
20
|
+
if (!dmConfig) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return dmConfig.enabled;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the mode for DM processing
|
|
27
|
+
*
|
|
28
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
29
|
+
* @returns The mode for DM processing (defaults to "auto")
|
|
30
|
+
*/
|
|
31
|
+
export function getDMMode(dmConfig) {
|
|
32
|
+
// DMs default to auto mode (no mention required)
|
|
33
|
+
if (!dmConfig) {
|
|
34
|
+
return "auto";
|
|
35
|
+
}
|
|
36
|
+
return dmConfig.mode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a user is allowed to send DMs to the bot
|
|
40
|
+
*
|
|
41
|
+
* Filtering rules:
|
|
42
|
+
* 1. If DMs are disabled, no users are allowed
|
|
43
|
+
* 2. If a blocklist is defined and user is on it, they are blocked
|
|
44
|
+
* 3. If an allowlist is defined, only users on it are allowed
|
|
45
|
+
* 4. If neither list is defined, all users are allowed
|
|
46
|
+
*
|
|
47
|
+
* @param userId - Discord user ID to check
|
|
48
|
+
* @param dmConfig - DM configuration from agent's Discord config
|
|
49
|
+
* @returns Filter result with allowed status and reason
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const result = checkDMUserFilter("123456789", dmConfig);
|
|
54
|
+
* if (!result.allowed) {
|
|
55
|
+
* console.log(`User blocked: ${result.reason}`);
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function checkDMUserFilter(userId, dmConfig) {
|
|
60
|
+
// If DMs are disabled, no users are allowed
|
|
61
|
+
if (!isDMEnabled(dmConfig)) {
|
|
62
|
+
return {
|
|
63
|
+
allowed: false,
|
|
64
|
+
reason: "dm_disabled",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// If no config, all users are allowed
|
|
68
|
+
if (!dmConfig) {
|
|
69
|
+
return {
|
|
70
|
+
allowed: true,
|
|
71
|
+
reason: "allowed",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const { allowlist, blocklist } = dmConfig;
|
|
75
|
+
// Check blocklist first (takes precedence)
|
|
76
|
+
if (blocklist && blocklist.length > 0) {
|
|
77
|
+
if (blocklist.includes(userId)) {
|
|
78
|
+
return {
|
|
79
|
+
allowed: false,
|
|
80
|
+
reason: "in_blocklist",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Check allowlist (if defined, only users on it are allowed)
|
|
85
|
+
if (allowlist && allowlist.length > 0) {
|
|
86
|
+
if (!allowlist.includes(userId)) {
|
|
87
|
+
return {
|
|
88
|
+
allowed: false,
|
|
89
|
+
reason: "not_in_allowlist",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// User is allowed
|
|
94
|
+
return {
|
|
95
|
+
allowed: true,
|
|
96
|
+
reason: "allowed",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// Channel Configuration
|
|
101
|
+
// =============================================================================
|
|
102
|
+
/**
|
|
103
|
+
* Default number of context messages for DMs
|
|
104
|
+
*/
|
|
105
|
+
export const DEFAULT_DM_CONTEXT_MESSAGES = 10;
|
|
106
|
+
/**
|
|
107
|
+
* Default number of context messages for channels
|
|
108
|
+
*/
|
|
109
|
+
export const DEFAULT_CHANNEL_CONTEXT_MESSAGES = 10;
|
|
110
|
+
/**
|
|
111
|
+
* Find channel configuration from guild config
|
|
112
|
+
*
|
|
113
|
+
* @param channelId - Discord channel ID
|
|
114
|
+
* @param guilds - Array of guild configurations
|
|
115
|
+
* @returns Channel config and guild ID, or null if not found
|
|
116
|
+
*/
|
|
117
|
+
export function findChannelConfig(channelId, guilds) {
|
|
118
|
+
for (const guild of guilds) {
|
|
119
|
+
const channel = guild.channels?.find((c) => c.id === channelId);
|
|
120
|
+
if (channel) {
|
|
121
|
+
return { channel, guildId: guild.id };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve channel configuration for a message
|
|
128
|
+
*
|
|
129
|
+
* Determines the mode and context settings for a given channel,
|
|
130
|
+
* handling both guild channels and DMs appropriately.
|
|
131
|
+
*
|
|
132
|
+
* @param channelId - Discord channel ID
|
|
133
|
+
* @param guildId - Guild ID (null for DMs)
|
|
134
|
+
* @param guilds - Array of guild configurations
|
|
135
|
+
* @param dmConfig - Global DM configuration
|
|
136
|
+
* @returns Resolved channel configuration or null if channel not configured
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const config = resolveChannelConfig(
|
|
141
|
+
* message.channel.id,
|
|
142
|
+
* message.guildId,
|
|
143
|
+
* discordConfig.guilds,
|
|
144
|
+
* discordConfig.dm
|
|
145
|
+
* );
|
|
146
|
+
*
|
|
147
|
+
* if (config) {
|
|
148
|
+
* if (config.mode === 'auto') {
|
|
149
|
+
* // Process all non-bot messages
|
|
150
|
+
* } else {
|
|
151
|
+
* // Only process mentions
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function resolveChannelConfig(channelId, guildId, guilds, dmConfig) {
|
|
157
|
+
// Handle DMs
|
|
158
|
+
if (!guildId) {
|
|
159
|
+
// Check if DMs are enabled
|
|
160
|
+
if (!isDMEnabled(dmConfig)) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
mode: getDMMode(dmConfig),
|
|
165
|
+
contextMessages: DEFAULT_DM_CONTEXT_MESSAGES,
|
|
166
|
+
isDM: true,
|
|
167
|
+
guildId: null,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Handle guild channels
|
|
171
|
+
const guildConfig = guilds.find((g) => g.id === guildId);
|
|
172
|
+
if (!guildConfig) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const channelConfig = guildConfig.channels?.find((c) => c.id === channelId);
|
|
176
|
+
if (!channelConfig) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
mode: channelConfig.mode,
|
|
181
|
+
contextMessages: channelConfig.context_messages,
|
|
182
|
+
isDM: false,
|
|
183
|
+
guildId,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if a message should be processed in auto mode
|
|
188
|
+
*
|
|
189
|
+
* In auto mode:
|
|
190
|
+
* - All non-bot messages are processed
|
|
191
|
+
* - No mention is required
|
|
192
|
+
* - Full conversation context is maintained
|
|
193
|
+
*
|
|
194
|
+
* @param isBot - Whether the message author is a bot
|
|
195
|
+
* @param mode - The channel mode
|
|
196
|
+
* @param wasMentioned - Whether the bot was mentioned
|
|
197
|
+
* @returns true if the message should be processed
|
|
198
|
+
*/
|
|
199
|
+
export function shouldProcessInMode(isBot, mode, wasMentioned) {
|
|
200
|
+
// Never process bot messages
|
|
201
|
+
if (isBot) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
// In auto mode, process all non-bot messages
|
|
205
|
+
if (mode === "auto") {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
// In mention mode, only process if mentioned
|
|
209
|
+
return wasMentioned;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=auto-mode-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-mode-handler.js","sourceRoot":"","sources":["../src/auto-mode-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-manager.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/command-manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Mock discord.js REST and Routes
|
|
4
|
+
// =============================================================================
|
|
5
|
+
const mockRestPut = vi.fn().mockResolvedValue([]);
|
|
6
|
+
vi.mock("discord.js", async () => {
|
|
7
|
+
const actual = await vi.importActual("discord.js");
|
|
8
|
+
// Create a mock REST class
|
|
9
|
+
class MockREST {
|
|
10
|
+
setToken() {
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
put = mockRestPut;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
...actual,
|
|
17
|
+
REST: MockREST,
|
|
18
|
+
Routes: {
|
|
19
|
+
applicationCommands: (clientId) => `/applications/${clientId}/commands`,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
// Import after mock
|
|
24
|
+
import { CommandManager } from "../command-manager.js";
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Test Fixtures
|
|
27
|
+
// =============================================================================
|
|
28
|
+
function createMockClient() {
|
|
29
|
+
return {
|
|
30
|
+
user: {
|
|
31
|
+
id: "bot-123",
|
|
32
|
+
username: "TestBot",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createMockSessionManager() {
|
|
37
|
+
return {
|
|
38
|
+
agentName: "test-agent",
|
|
39
|
+
getOrCreateSession: vi.fn(),
|
|
40
|
+
touchSession: vi.fn(),
|
|
41
|
+
getSession: vi.fn(),
|
|
42
|
+
clearSession: vi.fn(),
|
|
43
|
+
cleanupExpiredSessions: vi.fn(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function createMockConnectorState() {
|
|
47
|
+
return {
|
|
48
|
+
status: "connected",
|
|
49
|
+
connectedAt: new Date().toISOString(),
|
|
50
|
+
disconnectedAt: null,
|
|
51
|
+
reconnectAttempts: 0,
|
|
52
|
+
lastError: null,
|
|
53
|
+
botUser: {
|
|
54
|
+
id: "bot-123",
|
|
55
|
+
username: "TestBot",
|
|
56
|
+
discriminator: "0001",
|
|
57
|
+
},
|
|
58
|
+
rateLimits: {
|
|
59
|
+
totalCount: 0,
|
|
60
|
+
lastRateLimitAt: null,
|
|
61
|
+
isRateLimited: false,
|
|
62
|
+
currentResetTime: 0,
|
|
63
|
+
},
|
|
64
|
+
messageStats: {
|
|
65
|
+
received: 0,
|
|
66
|
+
sent: 0,
|
|
67
|
+
ignored: 0,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function createMockInteraction(commandName, channelId = "channel-123") {
|
|
72
|
+
return {
|
|
73
|
+
commandName,
|
|
74
|
+
channelId,
|
|
75
|
+
reply: vi.fn().mockResolvedValue(undefined),
|
|
76
|
+
replied: false,
|
|
77
|
+
deferred: false,
|
|
78
|
+
user: {
|
|
79
|
+
id: "user-123",
|
|
80
|
+
username: "TestUser",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function createMockLogger() {
|
|
85
|
+
return {
|
|
86
|
+
debug: vi.fn(),
|
|
87
|
+
info: vi.fn(),
|
|
88
|
+
warn: vi.fn(),
|
|
89
|
+
error: vi.fn(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// Tests
|
|
94
|
+
// =============================================================================
|
|
95
|
+
describe("CommandManager", () => {
|
|
96
|
+
let client;
|
|
97
|
+
let sessionManager;
|
|
98
|
+
let connectorState;
|
|
99
|
+
let logger;
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
vi.clearAllMocks();
|
|
102
|
+
client = createMockClient();
|
|
103
|
+
sessionManager = createMockSessionManager();
|
|
104
|
+
connectorState = createMockConnectorState();
|
|
105
|
+
logger = createMockLogger();
|
|
106
|
+
});
|
|
107
|
+
describe("constructor", () => {
|
|
108
|
+
it("creates command manager with valid options", () => {
|
|
109
|
+
const manager = new CommandManager({
|
|
110
|
+
agentName: "test-agent",
|
|
111
|
+
client,
|
|
112
|
+
botToken: "test-token",
|
|
113
|
+
sessionManager,
|
|
114
|
+
getConnectorState: () => connectorState,
|
|
115
|
+
logger,
|
|
116
|
+
});
|
|
117
|
+
expect(manager).toBeDefined();
|
|
118
|
+
});
|
|
119
|
+
it("registers built-in commands", () => {
|
|
120
|
+
const manager = new CommandManager({
|
|
121
|
+
agentName: "test-agent",
|
|
122
|
+
client,
|
|
123
|
+
botToken: "test-token",
|
|
124
|
+
sessionManager,
|
|
125
|
+
getConnectorState: () => connectorState,
|
|
126
|
+
logger,
|
|
127
|
+
});
|
|
128
|
+
const commands = manager.getCommands();
|
|
129
|
+
expect(commands.has("help")).toBe(true);
|
|
130
|
+
expect(commands.has("reset")).toBe(true);
|
|
131
|
+
expect(commands.has("status")).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("registerCommands", () => {
|
|
135
|
+
it("registers commands with Discord API", async () => {
|
|
136
|
+
const manager = new CommandManager({
|
|
137
|
+
agentName: "test-agent",
|
|
138
|
+
client,
|
|
139
|
+
botToken: "test-token",
|
|
140
|
+
sessionManager,
|
|
141
|
+
getConnectorState: () => connectorState,
|
|
142
|
+
logger,
|
|
143
|
+
});
|
|
144
|
+
await manager.registerCommands();
|
|
145
|
+
expect(mockRestPut).toHaveBeenCalledTimes(1);
|
|
146
|
+
expect(mockRestPut).toHaveBeenCalledWith(expect.stringContaining("bot-123"), expect.objectContaining({
|
|
147
|
+
body: expect.any(Array),
|
|
148
|
+
}));
|
|
149
|
+
});
|
|
150
|
+
it("logs successful registration", async () => {
|
|
151
|
+
const manager = new CommandManager({
|
|
152
|
+
agentName: "test-agent",
|
|
153
|
+
client,
|
|
154
|
+
botToken: "test-token",
|
|
155
|
+
sessionManager,
|
|
156
|
+
getConnectorState: () => connectorState,
|
|
157
|
+
logger,
|
|
158
|
+
});
|
|
159
|
+
await manager.registerCommands();
|
|
160
|
+
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Registering"), expect.any(Object));
|
|
161
|
+
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Successfully"));
|
|
162
|
+
});
|
|
163
|
+
it("throws when client user ID not available", async () => {
|
|
164
|
+
const clientWithoutUser = {
|
|
165
|
+
user: null,
|
|
166
|
+
};
|
|
167
|
+
const manager = new CommandManager({
|
|
168
|
+
agentName: "test-agent",
|
|
169
|
+
client: clientWithoutUser,
|
|
170
|
+
botToken: "test-token",
|
|
171
|
+
sessionManager,
|
|
172
|
+
getConnectorState: () => connectorState,
|
|
173
|
+
logger,
|
|
174
|
+
});
|
|
175
|
+
await expect(manager.registerCommands()).rejects.toThrow("Client user ID not available");
|
|
176
|
+
});
|
|
177
|
+
it("logs error on registration failure", async () => {
|
|
178
|
+
mockRestPut.mockRejectedValueOnce(new Error("API Error"));
|
|
179
|
+
const manager = new CommandManager({
|
|
180
|
+
agentName: "test-agent",
|
|
181
|
+
client,
|
|
182
|
+
botToken: "test-token",
|
|
183
|
+
sessionManager,
|
|
184
|
+
getConnectorState: () => connectorState,
|
|
185
|
+
logger,
|
|
186
|
+
});
|
|
187
|
+
await expect(manager.registerCommands()).rejects.toThrow("API Error");
|
|
188
|
+
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed"), expect.any(Object));
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
describe("handleInteraction", () => {
|
|
192
|
+
it("executes known commands", async () => {
|
|
193
|
+
const manager = new CommandManager({
|
|
194
|
+
agentName: "test-agent",
|
|
195
|
+
client,
|
|
196
|
+
botToken: "test-token",
|
|
197
|
+
sessionManager,
|
|
198
|
+
getConnectorState: () => connectorState,
|
|
199
|
+
logger,
|
|
200
|
+
});
|
|
201
|
+
const interaction = createMockInteraction("help");
|
|
202
|
+
await manager.handleInteraction(interaction);
|
|
203
|
+
const reply = interaction.reply;
|
|
204
|
+
expect(reply).toHaveBeenCalledTimes(1);
|
|
205
|
+
});
|
|
206
|
+
it("responds with error for unknown commands", async () => {
|
|
207
|
+
const manager = new CommandManager({
|
|
208
|
+
agentName: "test-agent",
|
|
209
|
+
client,
|
|
210
|
+
botToken: "test-token",
|
|
211
|
+
sessionManager,
|
|
212
|
+
getConnectorState: () => connectorState,
|
|
213
|
+
logger,
|
|
214
|
+
});
|
|
215
|
+
const interaction = createMockInteraction("unknown-command");
|
|
216
|
+
await manager.handleInteraction(interaction);
|
|
217
|
+
const reply = interaction.reply;
|
|
218
|
+
expect(reply).toHaveBeenCalledWith({
|
|
219
|
+
content: "Unknown command.",
|
|
220
|
+
ephemeral: true,
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
it("logs warning for unknown commands", async () => {
|
|
224
|
+
const manager = new CommandManager({
|
|
225
|
+
agentName: "test-agent",
|
|
226
|
+
client,
|
|
227
|
+
botToken: "test-token",
|
|
228
|
+
sessionManager,
|
|
229
|
+
getConnectorState: () => connectorState,
|
|
230
|
+
logger,
|
|
231
|
+
});
|
|
232
|
+
const interaction = createMockInteraction("unknown-command");
|
|
233
|
+
await manager.handleInteraction(interaction);
|
|
234
|
+
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining("Unknown command"), expect.any(Object));
|
|
235
|
+
});
|
|
236
|
+
it("logs command execution", async () => {
|
|
237
|
+
const manager = new CommandManager({
|
|
238
|
+
agentName: "test-agent",
|
|
239
|
+
client,
|
|
240
|
+
botToken: "test-token",
|
|
241
|
+
sessionManager,
|
|
242
|
+
getConnectorState: () => connectorState,
|
|
243
|
+
logger,
|
|
244
|
+
});
|
|
245
|
+
const interaction = createMockInteraction("help");
|
|
246
|
+
await manager.handleInteraction(interaction);
|
|
247
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("Executing"), expect.objectContaining({
|
|
248
|
+
commandName: "help",
|
|
249
|
+
}));
|
|
250
|
+
});
|
|
251
|
+
it("handles command execution errors gracefully", async () => {
|
|
252
|
+
const manager = new CommandManager({
|
|
253
|
+
agentName: "test-agent",
|
|
254
|
+
client,
|
|
255
|
+
botToken: "test-token",
|
|
256
|
+
sessionManager,
|
|
257
|
+
getConnectorState: () => connectorState,
|
|
258
|
+
logger,
|
|
259
|
+
});
|
|
260
|
+
// Create an interaction that will fail when executing
|
|
261
|
+
const interaction = createMockInteraction("reset");
|
|
262
|
+
sessionManager.clearSession.mockRejectedValueOnce(new Error("Session error"));
|
|
263
|
+
await manager.handleInteraction(interaction);
|
|
264
|
+
// ErrorHandler logs with different format
|
|
265
|
+
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Error during"), expect.any(Object));
|
|
266
|
+
const reply = interaction.reply;
|
|
267
|
+
// User-friendly error message (no stack traces)
|
|
268
|
+
expect(reply).toHaveBeenCalledWith({
|
|
269
|
+
content: "Sorry, I encountered an error processing your request. Please try again.",
|
|
270
|
+
ephemeral: true,
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
it("provides correct context to command handler", async () => {
|
|
274
|
+
const manager = new CommandManager({
|
|
275
|
+
agentName: "my-agent",
|
|
276
|
+
client,
|
|
277
|
+
botToken: "test-token",
|
|
278
|
+
sessionManager,
|
|
279
|
+
getConnectorState: () => connectorState,
|
|
280
|
+
logger,
|
|
281
|
+
});
|
|
282
|
+
const interaction = createMockInteraction("help");
|
|
283
|
+
await manager.handleInteraction(interaction);
|
|
284
|
+
const reply = interaction.reply;
|
|
285
|
+
// Help command should include agent name in response
|
|
286
|
+
expect(reply).toHaveBeenCalledWith(expect.objectContaining({
|
|
287
|
+
content: expect.stringContaining("my-agent"),
|
|
288
|
+
}));
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
describe("getCommands", () => {
|
|
292
|
+
it("returns readonly map of commands", () => {
|
|
293
|
+
const manager = new CommandManager({
|
|
294
|
+
agentName: "test-agent",
|
|
295
|
+
client,
|
|
296
|
+
botToken: "test-token",
|
|
297
|
+
sessionManager,
|
|
298
|
+
getConnectorState: () => connectorState,
|
|
299
|
+
logger,
|
|
300
|
+
});
|
|
301
|
+
const commands = manager.getCommands();
|
|
302
|
+
expect(commands).toBeInstanceOf(Map);
|
|
303
|
+
expect(commands.size).toBe(3);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=command-manager.test.js.map
|