@furlow/builtins 0.1.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.
Files changed (46) hide show
  1. package/dist/afk/index.d.ts +21 -0
  2. package/dist/afk/index.js +195 -0
  3. package/dist/afk/index.js.map +1 -0
  4. package/dist/auto-responder/index.d.ts +21 -0
  5. package/dist/auto-responder/index.js +356 -0
  6. package/dist/auto-responder/index.js.map +1 -0
  7. package/dist/giveaways/index.d.ts +25 -0
  8. package/dist/giveaways/index.js +416 -0
  9. package/dist/giveaways/index.js.map +1 -0
  10. package/dist/index.d.ts +15 -0
  11. package/dist/index.js +5402 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/leveling/index.d.ts +46 -0
  14. package/dist/leveling/index.js +521 -0
  15. package/dist/leveling/index.js.map +1 -0
  16. package/dist/logging/index.d.ts +52 -0
  17. package/dist/logging/index.js +519 -0
  18. package/dist/logging/index.js.map +1 -0
  19. package/dist/moderation/index.d.ts +83 -0
  20. package/dist/moderation/index.js +221 -0
  21. package/dist/moderation/index.js.map +1 -0
  22. package/dist/music/index.d.ts +33 -0
  23. package/dist/music/index.js +414 -0
  24. package/dist/music/index.js.map +1 -0
  25. package/dist/polls/index.d.ts +21 -0
  26. package/dist/polls/index.js +395 -0
  27. package/dist/polls/index.js.map +1 -0
  28. package/dist/reaction-roles/index.d.ts +19 -0
  29. package/dist/reaction-roles/index.js +551 -0
  30. package/dist/reaction-roles/index.js.map +1 -0
  31. package/dist/reminders/index.d.ts +23 -0
  32. package/dist/reminders/index.js +224 -0
  33. package/dist/reminders/index.js.map +1 -0
  34. package/dist/starboard/index.d.ts +35 -0
  35. package/dist/starboard/index.js +401 -0
  36. package/dist/starboard/index.js.map +1 -0
  37. package/dist/tickets/index.d.ts +43 -0
  38. package/dist/tickets/index.js +614 -0
  39. package/dist/tickets/index.js.map +1 -0
  40. package/dist/utilities/index.d.ts +15 -0
  41. package/dist/utilities/index.js +299 -0
  42. package/dist/utilities/index.js.map +1 -0
  43. package/dist/welcome/index.d.ts +48 -0
  44. package/dist/welcome/index.js +302 -0
  45. package/dist/welcome/index.js.map +1 -0
  46. package/package.json +109 -0
@@ -0,0 +1,21 @@
1
+ import { CommandDefinition, EventHandler, TableDefinition, FurlowSpec } from '@furlow/schema';
2
+
3
+ /**
4
+ * AFK builtin module
5
+ * Handles AFK status and mention notifications
6
+ */
7
+
8
+ interface AfkConfig {
9
+ /** Maximum AFK reason length */
10
+ maxReasonLength?: number;
11
+ /** Add prefix to nickname */
12
+ nicknamePrefix?: string;
13
+ /** Ignore bot mentions */
14
+ ignoreBots?: boolean;
15
+ }
16
+ declare const afkTables: Record<string, TableDefinition>;
17
+ declare const afkEventHandlers: EventHandler[];
18
+ declare const afkCommands: CommandDefinition[];
19
+ declare function getAfkSpec(config?: AfkConfig): Partial<FurlowSpec>;
20
+
21
+ export { type AfkConfig, afkCommands, afkEventHandlers, afkTables, getAfkSpec };
@@ -0,0 +1,195 @@
1
+ // src/afk/index.ts
2
+ var afkTables = {
3
+ afk_users: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ guild_id: { type: "string", index: true },
7
+ user_id: { type: "string", index: true },
8
+ reason: { type: "string" },
9
+ set_at: { type: "timestamp" }
10
+ }
11
+ }
12
+ };
13
+ var afkEventHandlers = [
14
+ // Remove AFK when user sends message
15
+ {
16
+ event: "message",
17
+ condition: "${!message.author.bot}",
18
+ actions: [
19
+ {
20
+ action: "db_query",
21
+ table: "afk_users",
22
+ where: { guild_id: "${guild.id}", user_id: "${user.id}" },
23
+ as: "afkStatus"
24
+ },
25
+ {
26
+ action: "flow_if",
27
+ condition: "${afkStatus.length > 0}",
28
+ then: [
29
+ // Remove AFK
30
+ {
31
+ action: "db_delete",
32
+ table: "afk_users",
33
+ where: { guild_id: "${guild.id}", user_id: "${user.id}" }
34
+ },
35
+ // Remove nickname prefix if set
36
+ {
37
+ action: "flow_if",
38
+ condition: "${config.afk?.nicknamePrefix && member.nickname?.startsWith(config.afk.nicknamePrefix)}",
39
+ then: [
40
+ {
41
+ action: "set_nickname",
42
+ user: "${user.id}",
43
+ nickname: '${member.nickname.replace(config.afk.nicknamePrefix, "")}'
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ action: "send_message",
49
+ channel: "${channel.id}",
50
+ content: "Welcome back ${user}! I removed your AFK status.",
51
+ as: "welcomeBack"
52
+ },
53
+ {
54
+ action: "wait",
55
+ duration: 5e3
56
+ },
57
+ {
58
+ action: "delete_message",
59
+ channel: "${channel.id}",
60
+ message: "${welcomeBack.id}"
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ },
66
+ // Notify when AFK user is mentioned
67
+ {
68
+ event: "message",
69
+ condition: "${message.mentions.users.size > 0 && !message.author.bot}",
70
+ actions: [
71
+ {
72
+ action: "set",
73
+ key: "mentionedIds",
74
+ value: "${[...message.mentions.users.keys()]}"
75
+ },
76
+ {
77
+ action: "db_query",
78
+ table: "afk_users",
79
+ where: { guild_id: "${guild.id}" },
80
+ as: "allAfk"
81
+ },
82
+ {
83
+ action: "set",
84
+ key: "afkMentioned",
85
+ value: "${allAfk.filter(a => mentionedIds.includes(a.user_id))}"
86
+ },
87
+ {
88
+ action: "flow_if",
89
+ condition: "${afkMentioned.length > 0}",
90
+ then: [
91
+ {
92
+ action: "set",
93
+ key: "afkMessages",
94
+ value: '${afkMentioned.map(a => "<@" + a.user_id + "> is AFK: " + a.reason + " (since " + timestamp(a.set_at, "R") + ")").join("\\n")}'
95
+ },
96
+ {
97
+ action: "send_message",
98
+ channel: "${channel.id}",
99
+ content: "${afkMessages}",
100
+ as: "afkNotice"
101
+ },
102
+ {
103
+ action: "wait",
104
+ duration: 1e4
105
+ },
106
+ {
107
+ action: "delete_message",
108
+ channel: "${channel.id}",
109
+ message: "${afkNotice.id}"
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ }
115
+ ];
116
+ var afkCommands = [
117
+ {
118
+ name: "afk",
119
+ description: "Set your AFK status",
120
+ options: [
121
+ { name: "reason", description: "AFK reason", type: "string", required: false }
122
+ ],
123
+ actions: [
124
+ {
125
+ action: "set",
126
+ key: "reason",
127
+ value: '${truncate(args.reason || "AFK", config.afk?.maxReasonLength || 100)}'
128
+ },
129
+ // Check if already AFK
130
+ {
131
+ action: "db_query",
132
+ table: "afk_users",
133
+ where: { guild_id: "${guild.id}", user_id: "${user.id}" },
134
+ as: "existing"
135
+ },
136
+ {
137
+ action: "flow_if",
138
+ condition: "${existing.length > 0}",
139
+ then: [
140
+ {
141
+ action: "db_update",
142
+ table: "afk_users",
143
+ where: { guild_id: "${guild.id}", user_id: "${user.id}" },
144
+ data: { reason: "${reason}", set_at: "${now()}" }
145
+ }
146
+ ],
147
+ else: [
148
+ {
149
+ action: "db_insert",
150
+ table: "afk_users",
151
+ data: {
152
+ guild_id: "${guild.id}",
153
+ user_id: "${user.id}",
154
+ reason: "${reason}",
155
+ set_at: "${now()}"
156
+ }
157
+ }
158
+ ]
159
+ },
160
+ // Add nickname prefix if configured
161
+ {
162
+ action: "flow_if",
163
+ condition: "${config.afk?.nicknamePrefix && !member.nickname?.startsWith(config.afk.nicknamePrefix)}",
164
+ then: [
165
+ {
166
+ action: "set_nickname",
167
+ user: "${user.id}",
168
+ nickname: "${config.afk.nicknamePrefix + (member.nickname || user.username)}"
169
+ }
170
+ ]
171
+ },
172
+ {
173
+ action: "reply",
174
+ content: "I set your AFK: ${reason}",
175
+ ephemeral: false
176
+ }
177
+ ]
178
+ }
179
+ ];
180
+ function getAfkSpec(config = {}) {
181
+ return {
182
+ commands: afkCommands,
183
+ events: afkEventHandlers,
184
+ state: {
185
+ tables: afkTables
186
+ }
187
+ };
188
+ }
189
+ export {
190
+ afkCommands,
191
+ afkEventHandlers,
192
+ afkTables,
193
+ getAfkSpec
194
+ };
195
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/afk/index.ts"],"sourcesContent":["/**\n * AFK builtin module\n * Handles AFK status and mention notifications\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';\n\nexport interface AfkConfig {\n /** Maximum AFK reason length */\n maxReasonLength?: number;\n /** Add prefix to nickname */\n nicknamePrefix?: string;\n /** Ignore bot mentions */\n ignoreBots?: boolean;\n}\n\nexport const afkTables: Record<string, TableDefinition> = {\n afk_users: {\n columns: {\n id: { type: 'number', primary: true },\n guild_id: { type: 'string', index: true },\n user_id: { type: 'string', index: true },\n reason: { type: 'string' },\n set_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const afkEventHandlers: EventHandler[] = [\n // Remove AFK when user sends message\n {\n event: 'message',\n condition: '${!message.author.bot}',\n actions: [\n {\n action: 'db_query',\n table: 'afk_users',\n where: { guild_id: '${guild.id}', user_id: '${user.id}' },\n as: 'afkStatus',\n },\n {\n action: 'flow_if',\n condition: '${afkStatus.length > 0}',\n then: [\n // Remove AFK\n {\n action: 'db_delete',\n table: 'afk_users',\n where: { guild_id: '${guild.id}', user_id: '${user.id}' },\n },\n // Remove nickname prefix if set\n {\n action: 'flow_if',\n condition: '${config.afk?.nicknamePrefix && member.nickname?.startsWith(config.afk.nicknamePrefix)}',\n then: [\n {\n action: 'set_nickname',\n user: '${user.id}',\n nickname: '${member.nickname.replace(config.afk.nicknamePrefix, \"\")}',\n },\n ],\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n content: 'Welcome back ${user}! I removed your AFK status.',\n as: 'welcomeBack',\n },\n {\n action: 'wait',\n duration: 5000,\n },\n {\n action: 'delete_message',\n channel: '${channel.id}',\n message: '${welcomeBack.id}',\n },\n ],\n },\n ],\n },\n // Notify when AFK user is mentioned\n {\n event: 'message',\n condition: '${message.mentions.users.size > 0 && !message.author.bot}',\n actions: [\n {\n action: 'set',\n key: 'mentionedIds',\n value: '${[...message.mentions.users.keys()]}',\n },\n {\n action: 'db_query',\n table: 'afk_users',\n where: { guild_id: '${guild.id}' },\n as: 'allAfk',\n },\n {\n action: 'set',\n key: 'afkMentioned',\n value: '${allAfk.filter(a => mentionedIds.includes(a.user_id))}',\n },\n {\n action: 'flow_if',\n condition: '${afkMentioned.length > 0}',\n then: [\n {\n action: 'set',\n key: 'afkMessages',\n value: '${afkMentioned.map(a => \"<@\" + a.user_id + \"> is AFK: \" + a.reason + \" (since \" + timestamp(a.set_at, \"R\") + \")\").join(\"\\\\n\")}',\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n content: '${afkMessages}',\n as: 'afkNotice',\n },\n {\n action: 'wait',\n duration: 10000,\n },\n {\n action: 'delete_message',\n channel: '${channel.id}',\n message: '${afkNotice.id}',\n },\n ],\n },\n ],\n },\n];\n\nexport const afkCommands: CommandDefinition[] = [\n {\n name: 'afk',\n description: 'Set your AFK status',\n options: [\n { name: 'reason', description: 'AFK reason', type: 'string', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'reason',\n value: '${truncate(args.reason || \"AFK\", config.afk?.maxReasonLength || 100)}',\n },\n // Check if already AFK\n {\n action: 'db_query',\n table: 'afk_users',\n where: { guild_id: '${guild.id}', user_id: '${user.id}' },\n as: 'existing',\n },\n {\n action: 'flow_if',\n condition: '${existing.length > 0}',\n then: [\n {\n action: 'db_update',\n table: 'afk_users',\n where: { guild_id: '${guild.id}', user_id: '${user.id}' },\n data: { reason: '${reason}', set_at: '${now()}' },\n },\n ],\n else: [\n {\n action: 'db_insert',\n table: 'afk_users',\n data: {\n guild_id: '${guild.id}',\n user_id: '${user.id}',\n reason: '${reason}',\n set_at: '${now()}',\n },\n },\n ],\n },\n // Add nickname prefix if configured\n {\n action: 'flow_if',\n condition: '${config.afk?.nicknamePrefix && !member.nickname?.startsWith(config.afk.nicknamePrefix)}',\n then: [\n {\n action: 'set_nickname',\n user: '${user.id}',\n nickname: '${config.afk.nicknamePrefix + (member.nickname || user.username)}',\n },\n ],\n },\n {\n action: 'reply',\n content: 'I set your AFK: ${reason}',\n ephemeral: false,\n },\n ],\n },\n];\n\nexport function getAfkSpec(config: AfkConfig = {}): Partial<FurlowSpec> {\n return {\n commands: afkCommands,\n events: afkEventHandlers,\n state: {\n tables: afkTables,\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,21 @@
1
+ import { CommandDefinition, EventHandler, TableDefinition, FurlowSpec } from '@furlow/schema';
2
+
3
+ /**
4
+ * Auto-Responder builtin module
5
+ * Handles trigger-based automatic responses
6
+ */
7
+
8
+ interface AutoResponderConfig {
9
+ /** Maximum triggers per guild */
10
+ maxTriggers?: number;
11
+ /** Ignore bots */
12
+ ignoreBots?: boolean;
13
+ /** Global cooldown (seconds) */
14
+ globalCooldown?: number;
15
+ }
16
+ declare const autoResponderTables: Record<string, TableDefinition>;
17
+ declare const autoResponderEventHandlers: EventHandler[];
18
+ declare const autoResponderCommands: CommandDefinition[];
19
+ declare function getAutoResponderSpec(config?: AutoResponderConfig): Partial<FurlowSpec>;
20
+
21
+ export { type AutoResponderConfig, autoResponderCommands, autoResponderEventHandlers, autoResponderTables, getAutoResponderSpec };
@@ -0,0 +1,356 @@
1
+ // src/auto-responder/index.ts
2
+ var autoResponderTables = {
3
+ auto_responses: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ guild_id: { type: "string", index: true },
7
+ trigger: { type: "string" },
8
+ trigger_type: { type: "string", default: "contains" },
9
+ // exact, contains, startswith, endswith, regex
10
+ response: { type: "string" },
11
+ response_type: { type: "string", default: "message" },
12
+ // message, embed, reaction
13
+ embed_data: { type: "json" },
14
+ reaction: { type: "string" },
15
+ chance: { type: "number", default: 100 },
16
+ // 0-100 percentage
17
+ cooldown: { type: "number", default: 0 },
18
+ // seconds
19
+ ignore_case: { type: "boolean", default: true },
20
+ delete_trigger: { type: "boolean", default: false },
21
+ dm_response: { type: "boolean", default: false },
22
+ allowed_channels: { type: "json" },
23
+ ignored_channels: { type: "json" },
24
+ allowed_roles: { type: "json" },
25
+ ignored_roles: { type: "json" },
26
+ created_at: { type: "timestamp" }
27
+ }
28
+ },
29
+ auto_response_cooldowns: {
30
+ columns: {
31
+ id: { type: "number", primary: true },
32
+ response_id: { type: "number", index: true },
33
+ guild_id: { type: "string" },
34
+ last_triggered: { type: "timestamp" }
35
+ }
36
+ }
37
+ };
38
+ var autoResponderEventHandlers = [
39
+ {
40
+ event: "message",
41
+ condition: "${!message.author.bot || !config.autoResponder?.ignoreBots}",
42
+ actions: [
43
+ // Get all triggers for this guild
44
+ {
45
+ action: "db_query",
46
+ table: "auto_responses",
47
+ where: { guild_id: "${guild.id}" },
48
+ as: "triggers"
49
+ },
50
+ // Check each trigger
51
+ {
52
+ action: "batch",
53
+ items: "${triggers}",
54
+ each: {
55
+ action: "flow_if",
56
+ condition: '${(() => { const t = item; const msg = t.ignore_case ? message.content.toLowerCase() : message.content; const trig = t.ignore_case ? t.trigger.toLowerCase() : t.trigger; switch(t.trigger_type) { case "exact": return msg === trig; case "contains": return msg.includes(trig); case "startswith": return msg.startsWith(trig); case "endswith": return msg.endsWith(trig); case "regex": return new RegExp(t.trigger, t.ignore_case ? "i" : "").test(message.content); default: return false; } })()}',
57
+ then: [
58
+ // Check channel restrictions
59
+ {
60
+ action: "flow_if",
61
+ condition: "${item.allowed_channels?.length && !item.allowed_channels.includes(channel.id)}",
62
+ then: [{ action: "abort" }]
63
+ },
64
+ {
65
+ action: "flow_if",
66
+ condition: "${item.ignored_channels?.includes(channel.id)}",
67
+ then: [{ action: "abort" }]
68
+ },
69
+ // Check role restrictions
70
+ {
71
+ action: "flow_if",
72
+ condition: "${item.allowed_roles?.length && !item.allowed_roles.some(r => member.roles.cache.has(r))}",
73
+ then: [{ action: "abort" }]
74
+ },
75
+ {
76
+ action: "flow_if",
77
+ condition: "${item.ignored_roles?.some(r => member.roles.cache.has(r))}",
78
+ then: [{ action: "abort" }]
79
+ },
80
+ // Check cooldown
81
+ {
82
+ action: "flow_if",
83
+ condition: "${item.cooldown > 0}",
84
+ then: [
85
+ {
86
+ action: "db_query",
87
+ table: "auto_response_cooldowns",
88
+ where: { response_id: "${item.id}", guild_id: "${guild.id}" },
89
+ as: "cooldown"
90
+ },
91
+ {
92
+ action: "flow_if",
93
+ condition: "${cooldown[0] && (now() - new Date(cooldown[0].last_triggered)) < item.cooldown * 1000}",
94
+ then: [{ action: "abort" }]
95
+ }
96
+ ]
97
+ },
98
+ // Check chance
99
+ {
100
+ action: "flow_if",
101
+ condition: "${item.chance < 100 && random(1, 100) > item.chance}",
102
+ then: [{ action: "abort" }]
103
+ },
104
+ // Execute response
105
+ {
106
+ action: "flow_switch",
107
+ value: "${item.response_type}",
108
+ cases: {
109
+ message: [
110
+ {
111
+ action: "flow_if",
112
+ condition: "${item.dm_response}",
113
+ then: [
114
+ { action: "send_dm", user: "${user.id}", content: "${item.response}" }
115
+ ],
116
+ else: [
117
+ { action: "send_message", channel: "${channel.id}", content: "${item.response}" }
118
+ ]
119
+ }
120
+ ],
121
+ embed: [
122
+ {
123
+ action: "flow_if",
124
+ condition: "${item.dm_response}",
125
+ then: [
126
+ { action: "send_dm", user: "${user.id}", embed: "${item.embed_data}" }
127
+ ],
128
+ else: [
129
+ { action: "send_message", channel: "${channel.id}", embed: "${item.embed_data}" }
130
+ ]
131
+ }
132
+ ],
133
+ reaction: [
134
+ { action: "add_reaction", message: "${message.id}", channel: "${channel.id}", emoji: "${item.reaction}" }
135
+ ]
136
+ }
137
+ },
138
+ // Delete trigger message if configured
139
+ {
140
+ action: "flow_if",
141
+ condition: "${item.delete_trigger}",
142
+ then: [
143
+ { action: "delete_message", channel: "${channel.id}", message: "${message.id}" }
144
+ ]
145
+ },
146
+ // Update cooldown
147
+ {
148
+ action: "flow_if",
149
+ condition: "${item.cooldown > 0}",
150
+ then: [
151
+ {
152
+ action: "db_update",
153
+ table: "auto_response_cooldowns",
154
+ where: { response_id: "${item.id}", guild_id: "${guild.id}" },
155
+ data: { last_triggered: "${now()}" },
156
+ upsert: true
157
+ }
158
+ ]
159
+ }
160
+ ]
161
+ }
162
+ }
163
+ ]
164
+ }
165
+ ];
166
+ var autoResponderCommands = [
167
+ {
168
+ name: "autoresponder",
169
+ description: "Auto-responder management",
170
+ subcommands: [
171
+ {
172
+ name: "add",
173
+ description: "Add an auto-response",
174
+ options: [
175
+ { name: "trigger", description: "Trigger text", type: "string", required: true },
176
+ { name: "response", description: "Response text", type: "string", required: true },
177
+ { name: "type", description: "Match type", type: "string", required: false, choices: [
178
+ { name: "Contains", value: "contains" },
179
+ { name: "Exact match", value: "exact" },
180
+ { name: "Starts with", value: "startswith" },
181
+ { name: "Ends with", value: "endswith" },
182
+ { name: "Regex", value: "regex" }
183
+ ] },
184
+ { name: "chance", description: "Response chance (1-100)", type: "integer", required: false }
185
+ ],
186
+ actions: [
187
+ // Check max triggers
188
+ {
189
+ action: "db_query",
190
+ table: "auto_responses",
191
+ where: { guild_id: "${guild.id}" },
192
+ as: "existing"
193
+ },
194
+ {
195
+ action: "flow_if",
196
+ condition: "${existing.length >= (config.autoResponder?.maxTriggers || 50)}",
197
+ then: [
198
+ { action: "reply", content: "Maximum triggers reached!", ephemeral: true },
199
+ { action: "abort" }
200
+ ]
201
+ },
202
+ {
203
+ action: "db_insert",
204
+ table: "auto_responses",
205
+ data: {
206
+ guild_id: "${guild.id}",
207
+ trigger: "${args.trigger}",
208
+ trigger_type: '${args.type || "contains"}',
209
+ response: "${args.response}",
210
+ chance: "${args.chance || 100}",
211
+ created_at: "${now()}"
212
+ },
213
+ as: "newTrigger"
214
+ },
215
+ {
216
+ action: "reply",
217
+ content: "Auto-response added! ID: ${newTrigger.id}",
218
+ ephemeral: true
219
+ }
220
+ ]
221
+ },
222
+ {
223
+ name: "list",
224
+ description: "List auto-responses",
225
+ actions: [
226
+ {
227
+ action: "db_query",
228
+ table: "auto_responses",
229
+ where: { guild_id: "${guild.id}" },
230
+ as: "triggers"
231
+ },
232
+ {
233
+ action: "flow_if",
234
+ condition: "${triggers.length === 0}",
235
+ then: [
236
+ { action: "reply", content: "No auto-responses configured!", ephemeral: true },
237
+ { action: "abort" }
238
+ ]
239
+ },
240
+ {
241
+ action: "set",
242
+ key: "triggerList",
243
+ value: '${triggers.map(t => "**" + t.id + ".** `" + truncate(t.trigger, 30) + "` \u2192 `" + truncate(t.response, 30) + "` (" + t.trigger_type + ")").join("\\n")}'
244
+ },
245
+ {
246
+ action: "reply",
247
+ embed: {
248
+ title: "Auto-Responses",
249
+ description: "${triggerList}",
250
+ color: "#5865f2",
251
+ footer: { text: "${triggers.length} trigger(s)" }
252
+ },
253
+ ephemeral: true
254
+ }
255
+ ]
256
+ },
257
+ {
258
+ name: "delete",
259
+ description: "Delete an auto-response",
260
+ options: [
261
+ { name: "id", description: "Trigger ID", type: "integer", required: true }
262
+ ],
263
+ actions: [
264
+ {
265
+ action: "db_query",
266
+ table: "auto_responses",
267
+ where: { id: "${args.id}", guild_id: "${guild.id}" },
268
+ as: "trigger"
269
+ },
270
+ {
271
+ action: "flow_if",
272
+ condition: "${trigger.length === 0}",
273
+ then: [
274
+ { action: "reply", content: "Trigger not found!", ephemeral: true },
275
+ { action: "abort" }
276
+ ]
277
+ },
278
+ {
279
+ action: "db_delete",
280
+ table: "auto_responses",
281
+ where: { id: "${args.id}" }
282
+ },
283
+ {
284
+ action: "db_delete",
285
+ table: "auto_response_cooldowns",
286
+ where: { response_id: "${args.id}" }
287
+ },
288
+ {
289
+ action: "reply",
290
+ content: "Auto-response deleted!",
291
+ ephemeral: true
292
+ }
293
+ ]
294
+ },
295
+ {
296
+ name: "edit",
297
+ description: "Edit an auto-response",
298
+ options: [
299
+ { name: "id", description: "Trigger ID", type: "integer", required: true },
300
+ { name: "trigger", description: "New trigger", type: "string", required: false },
301
+ { name: "response", description: "New response", type: "string", required: false },
302
+ { name: "cooldown", description: "Cooldown in seconds", type: "integer", required: false },
303
+ { name: "chance", description: "Response chance (1-100)", type: "integer", required: false }
304
+ ],
305
+ actions: [
306
+ {
307
+ action: "db_query",
308
+ table: "auto_responses",
309
+ where: { id: "${args.id}", guild_id: "${guild.id}" },
310
+ as: "trigger"
311
+ },
312
+ {
313
+ action: "flow_if",
314
+ condition: "${trigger.length === 0}",
315
+ then: [
316
+ { action: "reply", content: "Trigger not found!", ephemeral: true },
317
+ { action: "abort" }
318
+ ]
319
+ },
320
+ {
321
+ action: "set",
322
+ key: "updates",
323
+ value: "${Object.fromEntries(Object.entries({ trigger: args.trigger, response: args.response, cooldown: args.cooldown, chance: args.chance }).filter(([_, v]) => v !== undefined))}"
324
+ },
325
+ {
326
+ action: "db_update",
327
+ table: "auto_responses",
328
+ where: { id: "${args.id}" },
329
+ data: "${updates}"
330
+ },
331
+ {
332
+ action: "reply",
333
+ content: "Auto-response updated!",
334
+ ephemeral: true
335
+ }
336
+ ]
337
+ }
338
+ ]
339
+ }
340
+ ];
341
+ function getAutoResponderSpec(config = {}) {
342
+ return {
343
+ commands: autoResponderCommands,
344
+ events: autoResponderEventHandlers,
345
+ state: {
346
+ tables: autoResponderTables
347
+ }
348
+ };
349
+ }
350
+ export {
351
+ autoResponderCommands,
352
+ autoResponderEventHandlers,
353
+ autoResponderTables,
354
+ getAutoResponderSpec
355
+ };
356
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auto-responder/index.ts"],"sourcesContent":["/**\n * Auto-Responder builtin module\n * Handles trigger-based automatic responses\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';\n\nexport interface AutoResponderConfig {\n /** Maximum triggers per guild */\n maxTriggers?: number;\n /** Ignore bots */\n ignoreBots?: boolean;\n /** Global cooldown (seconds) */\n globalCooldown?: number;\n}\n\nexport const autoResponderTables: Record<string, TableDefinition> = {\n auto_responses: {\n columns: {\n id: { type: 'number', primary: true },\n guild_id: { type: 'string', index: true },\n trigger: { type: 'string' },\n trigger_type: { type: 'string', default: 'contains' }, // exact, contains, startswith, endswith, regex\n response: { type: 'string' },\n response_type: { type: 'string', default: 'message' }, // message, embed, reaction\n embed_data: { type: 'json' },\n reaction: { type: 'string' },\n chance: { type: 'number', default: 100 }, // 0-100 percentage\n cooldown: { type: 'number', default: 0 }, // seconds\n ignore_case: { type: 'boolean', default: true },\n delete_trigger: { type: 'boolean', default: false },\n dm_response: { type: 'boolean', default: false },\n allowed_channels: { type: 'json' },\n ignored_channels: { type: 'json' },\n allowed_roles: { type: 'json' },\n ignored_roles: { type: 'json' },\n created_at: { type: 'timestamp' },\n },\n },\n auto_response_cooldowns: {\n columns: {\n id: { type: 'number', primary: true },\n response_id: { type: 'number', index: true },\n guild_id: { type: 'string' },\n last_triggered: { type: 'timestamp' },\n },\n },\n};\n\nexport const autoResponderEventHandlers: EventHandler[] = [\n {\n event: 'message',\n condition: '${!message.author.bot || !config.autoResponder?.ignoreBots}',\n actions: [\n // Get all triggers for this guild\n {\n action: 'db_query',\n table: 'auto_responses',\n where: { guild_id: '${guild.id}' },\n as: 'triggers',\n },\n // Check each trigger\n {\n action: 'batch',\n items: '${triggers}',\n each: {\n action: 'flow_if',\n condition: '${(() => { const t = item; const msg = t.ignore_case ? message.content.toLowerCase() : message.content; const trig = t.ignore_case ? t.trigger.toLowerCase() : t.trigger; switch(t.trigger_type) { case \"exact\": return msg === trig; case \"contains\": return msg.includes(trig); case \"startswith\": return msg.startsWith(trig); case \"endswith\": return msg.endsWith(trig); case \"regex\": return new RegExp(t.trigger, t.ignore_case ? \"i\" : \"\").test(message.content); default: return false; } })()}',\n then: [\n // Check channel restrictions\n {\n action: 'flow_if',\n condition: '${item.allowed_channels?.length && !item.allowed_channels.includes(channel.id)}',\n then: [{ action: 'abort' }],\n },\n {\n action: 'flow_if',\n condition: '${item.ignored_channels?.includes(channel.id)}',\n then: [{ action: 'abort' }],\n },\n // Check role restrictions\n {\n action: 'flow_if',\n condition: '${item.allowed_roles?.length && !item.allowed_roles.some(r => member.roles.cache.has(r))}',\n then: [{ action: 'abort' }],\n },\n {\n action: 'flow_if',\n condition: '${item.ignored_roles?.some(r => member.roles.cache.has(r))}',\n then: [{ action: 'abort' }],\n },\n // Check cooldown\n {\n action: 'flow_if',\n condition: '${item.cooldown > 0}',\n then: [\n {\n action: 'db_query',\n table: 'auto_response_cooldowns',\n where: { response_id: '${item.id}', guild_id: '${guild.id}' },\n as: 'cooldown',\n },\n {\n action: 'flow_if',\n condition: '${cooldown[0] && (now() - new Date(cooldown[0].last_triggered)) < item.cooldown * 1000}',\n then: [{ action: 'abort' }],\n },\n ],\n },\n // Check chance\n {\n action: 'flow_if',\n condition: '${item.chance < 100 && random(1, 100) > item.chance}',\n then: [{ action: 'abort' }],\n },\n // Execute response\n {\n action: 'flow_switch',\n value: '${item.response_type}',\n cases: {\n message: [\n {\n action: 'flow_if',\n condition: '${item.dm_response}',\n then: [\n { action: 'send_dm', user: '${user.id}', content: '${item.response}' },\n ],\n else: [\n { action: 'send_message', channel: '${channel.id}', content: '${item.response}' },\n ],\n },\n ],\n embed: [\n {\n action: 'flow_if',\n condition: '${item.dm_response}',\n then: [\n { action: 'send_dm', user: '${user.id}', embed: '${item.embed_data}' },\n ],\n else: [\n { action: 'send_message', channel: '${channel.id}', embed: '${item.embed_data}' },\n ],\n },\n ],\n reaction: [\n { action: 'add_reaction', message: '${message.id}', channel: '${channel.id}', emoji: '${item.reaction}' },\n ],\n },\n },\n // Delete trigger message if configured\n {\n action: 'flow_if',\n condition: '${item.delete_trigger}',\n then: [\n { action: 'delete_message', channel: '${channel.id}', message: '${message.id}' },\n ],\n },\n // Update cooldown\n {\n action: 'flow_if',\n condition: '${item.cooldown > 0}',\n then: [\n {\n action: 'db_update',\n table: 'auto_response_cooldowns',\n where: { response_id: '${item.id}', guild_id: '${guild.id}' },\n data: { last_triggered: '${now()}' },\n upsert: true,\n },\n ],\n },\n ],\n },\n },\n ],\n },\n];\n\nexport const autoResponderCommands: CommandDefinition[] = [\n {\n name: 'autoresponder',\n description: 'Auto-responder management',\n subcommands: [\n {\n name: 'add',\n description: 'Add an auto-response',\n options: [\n { name: 'trigger', description: 'Trigger text', type: 'string', required: true },\n { name: 'response', description: 'Response text', type: 'string', required: true },\n { name: 'type', description: 'Match type', type: 'string', required: false, choices: [\n { name: 'Contains', value: 'contains' },\n { name: 'Exact match', value: 'exact' },\n { name: 'Starts with', value: 'startswith' },\n { name: 'Ends with', value: 'endswith' },\n { name: 'Regex', value: 'regex' },\n ]},\n { name: 'chance', description: 'Response chance (1-100)', type: 'integer', required: false },\n ],\n actions: [\n // Check max triggers\n {\n action: 'db_query',\n table: 'auto_responses',\n where: { guild_id: '${guild.id}' },\n as: 'existing',\n },\n {\n action: 'flow_if',\n condition: '${existing.length >= (config.autoResponder?.maxTriggers || 50)}',\n then: [\n { action: 'reply', content: 'Maximum triggers reached!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_insert',\n table: 'auto_responses',\n data: {\n guild_id: '${guild.id}',\n trigger: '${args.trigger}',\n trigger_type: '${args.type || \"contains\"}',\n response: '${args.response}',\n chance: '${args.chance || 100}',\n created_at: '${now()}',\n },\n as: 'newTrigger',\n },\n {\n action: 'reply',\n content: 'Auto-response added! ID: ${newTrigger.id}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'list',\n description: 'List auto-responses',\n actions: [\n {\n action: 'db_query',\n table: 'auto_responses',\n where: { guild_id: '${guild.id}' },\n as: 'triggers',\n },\n {\n action: 'flow_if',\n condition: '${triggers.length === 0}',\n then: [\n { action: 'reply', content: 'No auto-responses configured!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set',\n key: 'triggerList',\n value: '${triggers.map(t => \"**\" + t.id + \".** `\" + truncate(t.trigger, 30) + \"` → `\" + truncate(t.response, 30) + \"` (\" + t.trigger_type + \")\").join(\"\\\\n\")}',\n },\n {\n action: 'reply',\n embed: {\n title: 'Auto-Responses',\n description: '${triggerList}',\n color: '#5865f2',\n footer: { text: '${triggers.length} trigger(s)' },\n },\n ephemeral: true,\n },\n ],\n },\n {\n name: 'delete',\n description: 'Delete an auto-response',\n options: [\n { name: 'id', description: 'Trigger ID', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'auto_responses',\n where: { id: '${args.id}', guild_id: '${guild.id}' },\n as: 'trigger',\n },\n {\n action: 'flow_if',\n condition: '${trigger.length === 0}',\n then: [\n { action: 'reply', content: 'Trigger not found!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_delete',\n table: 'auto_responses',\n where: { id: '${args.id}' },\n },\n {\n action: 'db_delete',\n table: 'auto_response_cooldowns',\n where: { response_id: '${args.id}' },\n },\n {\n action: 'reply',\n content: 'Auto-response deleted!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'edit',\n description: 'Edit an auto-response',\n options: [\n { name: 'id', description: 'Trigger ID', type: 'integer', required: true },\n { name: 'trigger', description: 'New trigger', type: 'string', required: false },\n { name: 'response', description: 'New response', type: 'string', required: false },\n { name: 'cooldown', description: 'Cooldown in seconds', type: 'integer', required: false },\n { name: 'chance', description: 'Response chance (1-100)', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'auto_responses',\n where: { id: '${args.id}', guild_id: '${guild.id}' },\n as: 'trigger',\n },\n {\n action: 'flow_if',\n condition: '${trigger.length === 0}',\n then: [\n { action: 'reply', content: 'Trigger not found!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set',\n key: 'updates',\n value: '${Object.fromEntries(Object.entries({ trigger: args.trigger, response: args.response, cooldown: args.cooldown, chance: args.chance }).filter(([_, v]) => v !== undefined))}',\n },\n {\n action: 'db_update',\n table: 'auto_responses',\n where: { id: '${args.id}' },\n data: '${updates}',\n },\n {\n action: 'reply',\n content: 'Auto-response updated!',\n ephemeral: true,\n },\n ],\n },\n ],\n },\n];\n\nexport function getAutoResponderSpec(config: AutoResponderConfig = {}): Partial<FurlowSpec> {\n return {\n commands: autoResponderCommands,\n events: autoResponderEventHandlers,\n state: {\n tables: autoResponderTables,\n },\n };\n}\n"],"mappings":"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","names":[]}