@furlow/builtins 1.0.0 → 1.0.2

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/afk/index.js CHANGED
@@ -14,7 +14,7 @@ var afkEventHandlers = [
14
14
  // Remove AFK when user sends message
15
15
  {
16
16
  event: "message",
17
- condition: "${!message.author.bot}",
17
+ condition: "!message.author.bot",
18
18
  actions: [
19
19
  {
20
20
  action: "db_query",
@@ -24,7 +24,7 @@ var afkEventHandlers = [
24
24
  },
25
25
  {
26
26
  action: "flow_if",
27
- condition: "${afkStatus.length > 0}",
27
+ condition: "afkStatus.length > 0",
28
28
  then: [
29
29
  // Remove AFK
30
30
  {
@@ -35,7 +35,7 @@ var afkEventHandlers = [
35
35
  // Remove nickname prefix if set
36
36
  {
37
37
  action: "flow_if",
38
- condition: "${config.afk?.nicknamePrefix && member.nickname?.startsWith(config.afk.nicknamePrefix)}",
38
+ condition: "config.afk?.nicknamePrefix && member.nickname?.startsWith(config.afk.nicknamePrefix)",
39
39
  then: [
40
40
  {
41
41
  action: "set_nickname",
@@ -66,7 +66,7 @@ var afkEventHandlers = [
66
66
  // Notify when AFK user is mentioned
67
67
  {
68
68
  event: "message",
69
- condition: "${message.mentions.users.size > 0 && !message.author.bot}",
69
+ condition: "message.mentions.users.size > 0 && !message.author.bot",
70
70
  actions: [
71
71
  {
72
72
  action: "set",
@@ -86,7 +86,7 @@ var afkEventHandlers = [
86
86
  },
87
87
  {
88
88
  action: "flow_if",
89
- condition: "${afkMentioned.length > 0}",
89
+ condition: "afkMentioned.length > 0",
90
90
  then: [
91
91
  {
92
92
  action: "set",
@@ -135,7 +135,7 @@ var afkCommands = [
135
135
  },
136
136
  {
137
137
  action: "flow_if",
138
- condition: "${existing.length > 0}",
138
+ condition: "existing.length > 0",
139
139
  then: [
140
140
  {
141
141
  action: "db_update",
@@ -160,7 +160,7 @@ var afkCommands = [
160
160
  // Add nickname prefix if configured
161
161
  {
162
162
  action: "flow_if",
163
- condition: "${config.afk?.nicknamePrefix && !member.nickname?.startsWith(config.afk.nicknamePrefix)}",
163
+ condition: "config.afk?.nicknamePrefix && !member.nickname?.startsWith(config.afk.nicknamePrefix)",
164
164
  then: [
165
165
  {
166
166
  action: "set_nickname",
@@ -1 +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":[]}
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":[]}
@@ -38,7 +38,7 @@ var autoResponderTables = {
38
38
  var autoResponderEventHandlers = [
39
39
  {
40
40
  event: "message",
41
- condition: "${!message.author.bot || !config.autoResponder?.ignoreBots}",
41
+ condition: "!message.author.bot || !config.autoResponder?.ignoreBots",
42
42
  actions: [
43
43
  // Get all triggers for this guild
44
44
  {
@@ -53,34 +53,34 @@ var autoResponderEventHandlers = [
53
53
  items: "${triggers}",
54
54
  each: {
55
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; } })()}',
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
57
  then: [
58
58
  // Check channel restrictions
59
59
  {
60
60
  action: "flow_if",
61
- condition: "${item.allowed_channels?.length && !item.allowed_channels.includes(channel.id)}",
61
+ condition: "item.allowed_channels?.length && !item.allowed_channels.includes(channel.id)",
62
62
  then: [{ action: "abort" }]
63
63
  },
64
64
  {
65
65
  action: "flow_if",
66
- condition: "${item.ignored_channels?.includes(channel.id)}",
66
+ condition: "item.ignored_channels?.includes(channel.id)",
67
67
  then: [{ action: "abort" }]
68
68
  },
69
69
  // Check role restrictions
70
70
  {
71
71
  action: "flow_if",
72
- condition: "${item.allowed_roles?.length && !item.allowed_roles.some(r => member.roles.cache.has(r))}",
72
+ condition: "item.allowed_roles?.length && !item.allowed_roles.some(r => member.roles.cache.has(r))",
73
73
  then: [{ action: "abort" }]
74
74
  },
75
75
  {
76
76
  action: "flow_if",
77
- condition: "${item.ignored_roles?.some(r => member.roles.cache.has(r))}",
77
+ condition: "item.ignored_roles?.some(r => member.roles.cache.has(r))",
78
78
  then: [{ action: "abort" }]
79
79
  },
80
80
  // Check cooldown
81
81
  {
82
82
  action: "flow_if",
83
- condition: "${item.cooldown > 0}",
83
+ condition: "item.cooldown > 0",
84
84
  then: [
85
85
  {
86
86
  action: "db_query",
@@ -90,7 +90,7 @@ var autoResponderEventHandlers = [
90
90
  },
91
91
  {
92
92
  action: "flow_if",
93
- condition: "${cooldown[0] && (now() - new Date(cooldown[0].last_triggered)) < item.cooldown * 1000}",
93
+ condition: "cooldown[0] && (now() - new Date(cooldown[0].last_triggered)) < item.cooldown * 1000",
94
94
  then: [{ action: "abort" }]
95
95
  }
96
96
  ]
@@ -98,7 +98,7 @@ var autoResponderEventHandlers = [
98
98
  // Check chance
99
99
  {
100
100
  action: "flow_if",
101
- condition: "${item.chance < 100 && random(1, 100) > item.chance}",
101
+ condition: "item.chance < 100 && random(1, 100) > item.chance",
102
102
  then: [{ action: "abort" }]
103
103
  },
104
104
  // Execute response
@@ -109,7 +109,7 @@ var autoResponderEventHandlers = [
109
109
  message: [
110
110
  {
111
111
  action: "flow_if",
112
- condition: "${item.dm_response}",
112
+ condition: "item.dm_response",
113
113
  then: [
114
114
  { action: "send_dm", user: "${user.id}", content: "${item.response}" }
115
115
  ],
@@ -121,7 +121,7 @@ var autoResponderEventHandlers = [
121
121
  embed: [
122
122
  {
123
123
  action: "flow_if",
124
- condition: "${item.dm_response}",
124
+ condition: "item.dm_response",
125
125
  then: [
126
126
  { action: "send_dm", user: "${user.id}", embed: "${item.embed_data}" }
127
127
  ],
@@ -138,7 +138,7 @@ var autoResponderEventHandlers = [
138
138
  // Delete trigger message if configured
139
139
  {
140
140
  action: "flow_if",
141
- condition: "${item.delete_trigger}",
141
+ condition: "item.delete_trigger",
142
142
  then: [
143
143
  { action: "delete_message", channel: "${channel.id}", message: "${message.id}" }
144
144
  ]
@@ -146,7 +146,7 @@ var autoResponderEventHandlers = [
146
146
  // Update cooldown
147
147
  {
148
148
  action: "flow_if",
149
- condition: "${item.cooldown > 0}",
149
+ condition: "item.cooldown > 0",
150
150
  then: [
151
151
  {
152
152
  action: "db_update",
@@ -193,7 +193,7 @@ var autoResponderCommands = [
193
193
  },
194
194
  {
195
195
  action: "flow_if",
196
- condition: "${existing.length >= (config.autoResponder?.maxTriggers || 50)}",
196
+ condition: "existing.length >= (config.autoResponder?.maxTriggers || 50)",
197
197
  then: [
198
198
  { action: "reply", content: "Maximum triggers reached!", ephemeral: true },
199
199
  { action: "abort" }
@@ -231,7 +231,7 @@ var autoResponderCommands = [
231
231
  },
232
232
  {
233
233
  action: "flow_if",
234
- condition: "${triggers.length === 0}",
234
+ condition: "triggers.length === 0",
235
235
  then: [
236
236
  { action: "reply", content: "No auto-responses configured!", ephemeral: true },
237
237
  { action: "abort" }
@@ -269,7 +269,7 @@ var autoResponderCommands = [
269
269
  },
270
270
  {
271
271
  action: "flow_if",
272
- condition: "${trigger.length === 0}",
272
+ condition: "trigger.length === 0",
273
273
  then: [
274
274
  { action: "reply", content: "Trigger not found!", ephemeral: true },
275
275
  { action: "abort" }
@@ -311,7 +311,7 @@ var autoResponderCommands = [
311
311
  },
312
312
  {
313
313
  action: "flow_if",
314
- condition: "${trigger.length === 0}",
314
+ condition: "trigger.length === 0",
315
315
  then: [
316
316
  { action: "reply", content: "Trigger not found!", ephemeral: true },
317
317
  { action: "abort" }
@@ -1 +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":";AAgBO,IAAM,sBAAuD;AAAA,EAClE,gBAAgB;AAAA,IACd,SAAS;AAAA,MACP,IAAI,EAAE,MAAM,UAAU,SAAS,KAAK;AAAA,MACpC,UAAU,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MACxC,SAAS,EAAE,MAAM,SAAS;AAAA,MAC1B,cAAc,EAAE,MAAM,UAAU,SAAS,WAAW;AAAA;AAAA,MACpD,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,eAAe,EAAE,MAAM,UAAU,SAAS,UAAU;AAAA;AAAA,MACpD,YAAY,EAAE,MAAM,OAAO;AAAA,MAC3B,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,QAAQ,EAAE,MAAM,UAAU,SAAS,IAAI;AAAA;AAAA,MACvC,UAAU,EAAE,MAAM,UAAU,SAAS,EAAE;AAAA;AAAA,MACvC,aAAa,EAAE,MAAM,WAAW,SAAS,KAAK;AAAA,MAC9C,gBAAgB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAClD,aAAa,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC/C,kBAAkB,EAAE,MAAM,OAAO;AAAA,MACjC,kBAAkB,EAAE,MAAM,OAAO;AAAA,MACjC,eAAe,EAAE,MAAM,OAAO;AAAA,MAC9B,eAAe,EAAE,MAAM,OAAO;AAAA,MAC9B,YAAY,EAAE,MAAM,YAAY;AAAA,IAClC;AAAA,EACF;AAAA,EACA,yBAAyB;AAAA,IACvB,SAAS;AAAA,MACP,IAAI,EAAE,MAAM,UAAU,SAAS,KAAK;AAAA,MACpC,aAAa,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MAC3C,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,gBAAgB,EAAE,MAAM,YAAY;AAAA,IACtC;AAAA,EACF;AACF;AAEO,IAAM,6BAA6C;AAAA,EACxD;AAAA,IACE,OAAO;AAAA,IACP,WAAW;AAAA,IACX,SAAS;AAAA;AAAA,MAEP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,UAAU,cAAc;AAAA,QACjC,IAAI;AAAA,MACN;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,MAAM;AAAA;AAAA,YAEJ;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,YAC5B;AAAA,YACA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,YAC5B;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,YAC5B;AAAA,YACA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,YAC5B;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM;AAAA,gBACJ;AAAA,kBACE,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,OAAO,EAAE,aAAa,cAAc,UAAU,cAAc;AAAA,kBAC5D,IAAI;AAAA,gBACN;AAAA,gBACA;AAAA,kBACE,QAAQ;AAAA,kBACR,WAAW;AAAA,kBACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,gBAC5B;AAAA,cACF;AAAA,YACF;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM,CAAC,EAAE,QAAQ,QAAQ,CAAC;AAAA,YAC5B;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,OAAO;AAAA,gBACL,SAAS;AAAA,kBACP;AAAA,oBACE,QAAQ;AAAA,oBACR,WAAW;AAAA,oBACX,MAAM;AAAA,sBACJ,EAAE,QAAQ,WAAW,MAAM,cAAc,SAAS,mBAAmB;AAAA,oBACvE;AAAA,oBACA,MAAM;AAAA,sBACJ,EAAE,QAAQ,gBAAgB,SAAS,iBAAiB,SAAS,mBAAmB;AAAA,oBAClF;AAAA,kBACF;AAAA,gBACF;AAAA,gBACA,OAAO;AAAA,kBACL;AAAA,oBACE,QAAQ;AAAA,oBACR,WAAW;AAAA,oBACX,MAAM;AAAA,sBACJ,EAAE,QAAQ,WAAW,MAAM,cAAc,OAAO,qBAAqB;AAAA,oBACvE;AAAA,oBACA,MAAM;AAAA,sBACJ,EAAE,QAAQ,gBAAgB,SAAS,iBAAiB,OAAO,qBAAqB;AAAA,oBAClF;AAAA,kBACF;AAAA,gBACF;AAAA,gBACA,UAAU;AAAA,kBACR,EAAE,QAAQ,gBAAgB,SAAS,iBAAiB,SAAS,iBAAiB,OAAO,mBAAmB;AAAA,gBAC1G;AAAA,cACF;AAAA,YACF;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM;AAAA,gBACJ,EAAE,QAAQ,kBAAkB,SAAS,iBAAiB,SAAS,gBAAgB;AAAA,cACjF;AAAA,YACF;AAAA;AAAA,YAEA;AAAA,cACE,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,MAAM;AAAA,gBACJ;AAAA,kBACE,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,OAAO,EAAE,aAAa,cAAc,UAAU,cAAc;AAAA,kBAC5D,MAAM,EAAE,gBAAgB,WAAW;AAAA,kBACnC,QAAQ;AAAA,gBACV;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,wBAA6C;AAAA,EACxD;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,SAAS;AAAA,UACP,EAAE,MAAM,WAAW,aAAa,gBAAgB,MAAM,UAAU,UAAU,KAAK;AAAA,UAC/E,EAAE,MAAM,YAAY,aAAa,iBAAiB,MAAM,UAAU,UAAU,KAAK;AAAA,UACjF,EAAE,MAAM,QAAQ,aAAa,cAAc,MAAM,UAAU,UAAU,OAAO,SAAS;AAAA,YACnF,EAAE,MAAM,YAAY,OAAO,WAAW;AAAA,YACtC,EAAE,MAAM,eAAe,OAAO,QAAQ;AAAA,YACtC,EAAE,MAAM,eAAe,OAAO,aAAa;AAAA,YAC3C,EAAE,MAAM,aAAa,OAAO,WAAW;AAAA,YACvC,EAAE,MAAM,SAAS,OAAO,QAAQ;AAAA,UAClC,EAAC;AAAA,UACD,EAAE,MAAM,UAAU,aAAa,2BAA2B,MAAM,WAAW,UAAU,MAAM;AAAA,QAC7F;AAAA,QACA,SAAS;AAAA;AAAA,UAEP;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,UAAU,cAAc;AAAA,YACjC,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA,cACJ,EAAE,QAAQ,SAAS,SAAS,6BAA6B,WAAW,KAAK;AAAA,cACzE,EAAE,QAAQ,QAAQ;AAAA,YACpB;AAAA,UACF;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,UAAU;AAAA,cACV,SAAS;AAAA,cACT,cAAc;AAAA,cACd,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,YAAY;AAAA,YACd;AAAA,YACA,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,SAAS;AAAA,UACP;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,UAAU,cAAc;AAAA,YACjC,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA,cACJ,EAAE,QAAQ,SAAS,SAAS,iCAAiC,WAAW,KAAK;AAAA,cAC7E,EAAE,QAAQ,QAAQ;AAAA,YACpB;AAAA,UACF;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,OAAO;AAAA,cACP,QAAQ,EAAE,MAAM,gCAAgC;AAAA,YAClD;AAAA,YACA,WAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,SAAS;AAAA,UACP,EAAE,MAAM,MAAM,aAAa,cAAc,MAAM,WAAW,UAAU,KAAK;AAAA,QAC3E;AAAA,QACA,SAAS;AAAA,UACP;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,IAAI,cAAc,UAAU,cAAc;AAAA,YACnD,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA,cACJ,EAAE,QAAQ,SAAS,SAAS,sBAAsB,WAAW,KAAK;AAAA,cAClE,EAAE,QAAQ,QAAQ;AAAA,YACpB;AAAA,UACF;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,IAAI,aAAa;AAAA,UAC5B;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,aAAa,aAAa;AAAA,UACrC;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,SAAS;AAAA,UACP,EAAE,MAAM,MAAM,aAAa,cAAc,MAAM,WAAW,UAAU,KAAK;AAAA,UACzE,EAAE,MAAM,WAAW,aAAa,eAAe,MAAM,UAAU,UAAU,MAAM;AAAA,UAC/E,EAAE,MAAM,YAAY,aAAa,gBAAgB,MAAM,UAAU,UAAU,MAAM;AAAA,UACjF,EAAE,MAAM,YAAY,aAAa,uBAAuB,MAAM,WAAW,UAAU,MAAM;AAAA,UACzF,EAAE,MAAM,UAAU,aAAa,2BAA2B,MAAM,WAAW,UAAU,MAAM;AAAA,QAC7F;AAAA,QACA,SAAS;AAAA,UACP;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,IAAI,cAAc,UAAU,cAAc;AAAA,YACnD,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA,cACJ,EAAE,QAAQ,SAAS,SAAS,sBAAsB,WAAW,KAAK;AAAA,cAClE,EAAE,QAAQ,QAAQ;AAAA,YACpB;AAAA,UACF;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO,EAAE,IAAI,aAAa;AAAA,YAC1B,MAAM;AAAA,UACR;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,qBAAqB,SAA8B,CAAC,GAAwB;AAC1F,SAAO;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,QAAQ;AAAA,IACV;AAAA,EACF;AACF;","names":[]}
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":[]}
@@ -28,7 +28,7 @@ var giveawaysEventHandlers = [
28
28
  // Handle giveaway button click
29
29
  {
30
30
  event: "button_click",
31
- condition: '${interaction.customId.startsWith("giveaway_enter_")}',
31
+ condition: 'interaction.customId.startsWith("giveaway_enter_")',
32
32
  actions: [
33
33
  {
34
34
  action: "set",
@@ -43,7 +43,7 @@ var giveawaysEventHandlers = [
43
43
  },
44
44
  {
45
45
  action: "flow_if",
46
- condition: "${!giveaway[0] || giveaway[0].ended}",
46
+ condition: "!giveaway[0] || giveaway[0].ended",
47
47
  then: [
48
48
  { action: "reply", content: "This giveaway has ended!", ephemeral: true },
49
49
  { action: "abort" }
@@ -52,7 +52,7 @@ var giveawaysEventHandlers = [
52
52
  // Check role requirement
53
53
  {
54
54
  action: "flow_if",
55
- condition: "${giveaway[0].require_role && !member.roles.cache.has(giveaway[0].require_role)}",
55
+ condition: "giveaway[0].require_role && !member.roles.cache.has(giveaway[0].require_role)",
56
56
  then: [
57
57
  { action: "reply", content: "You need the <@&${giveaway[0].require_role}> role to enter!", ephemeral: true },
58
58
  { action: "abort" }
@@ -67,7 +67,7 @@ var giveawaysEventHandlers = [
67
67
  },
68
68
  {
69
69
  action: "flow_if",
70
- condition: "${existing.length > 0}",
70
+ condition: "existing.length > 0",
71
71
  then: [
72
72
  // Remove entry
73
73
  {
@@ -165,7 +165,7 @@ var giveawaysEventHandlers = [
165
165
  // Update message
166
166
  {
167
167
  action: "flow_if",
168
- condition: "${winners.length === 0}",
168
+ condition: "winners.length === 0",
169
169
  then: [
170
170
  {
171
171
  action: "edit_message",
@@ -314,7 +314,7 @@ var giveawaysCommands = [
314
314
  },
315
315
  {
316
316
  action: "flow_if",
317
- condition: "${!giveaway[0]}",
317
+ condition: "!giveaway[0]",
318
318
  then: [
319
319
  { action: "reply", content: "Giveaway not found!", ephemeral: true },
320
320
  { action: "abort" }
@@ -348,7 +348,7 @@ var giveawaysCommands = [
348
348
  },
349
349
  {
350
350
  action: "flow_if",
351
- condition: "${!giveaway[0] || !giveaway[0].ended}",
351
+ condition: "!giveaway[0] || !giveaway[0].ended",
352
352
  then: [
353
353
  { action: "reply", content: "Giveaway not found or not ended!", ephemeral: true },
354
354
  { action: "abort" }
@@ -372,7 +372,7 @@ var giveawaysCommands = [
372
372
  },
373
373
  {
374
374
  action: "flow_if",
375
- condition: "${winners.length === 0}",
375
+ condition: "winners.length === 0",
376
376
  then: [
377
377
  { action: "reply", content: "No valid entries to reroll!", ephemeral: true },
378
378
  { action: "abort" }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/giveaways/index.ts"],"sourcesContent":["/**\n * Giveaways builtin module\n * Handles giveaway creation, entries, and winner selection\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';\n\nexport interface GiveawaysConfig {\n /** Default giveaway duration */\n defaultDuration?: string;\n /** Giveaway manager role */\n managerRole?: string;\n /** Require role to enter */\n requireRole?: string;\n /** Embed color */\n embedColor?: string;\n /** End emoji */\n emoji?: string;\n}\n\nexport const giveawaysTables: Record<string, TableDefinition> = {\n giveaways: {\n columns: {\n id: { type: 'number', primary: true },\n guild_id: { type: 'string', index: true },\n channel_id: { type: 'string' },\n message_id: { type: 'string', unique: true },\n host_id: { type: 'string' },\n prize: { type: 'string' },\n winners_count: { type: 'number', default: 1 },\n ends_at: { type: 'timestamp', index: true },\n ended: { type: 'boolean', default: false },\n require_role: { type: 'string' },\n created_at: { type: 'timestamp' },\n },\n },\n giveaway_entries: {\n columns: {\n id: { type: 'number', primary: true },\n giveaway_id: { type: 'number', index: true },\n user_id: { type: 'string' },\n created_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const giveawaysEventHandlers: EventHandler[] = [\n // Handle giveaway button click\n {\n event: 'button_click',\n condition: '${interaction.customId.startsWith(\"giveaway_enter_\")}',\n actions: [\n {\n action: 'set',\n key: 'giveawayId',\n value: '${interaction.customId.replace(\"giveaway_enter_\", \"\")}',\n },\n {\n action: 'db_query',\n table: 'giveaways',\n where: { id: '${giveawayId}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '${!giveaway[0] || giveaway[0].ended}',\n then: [\n { action: 'reply', content: 'This giveaway has ended!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Check role requirement\n {\n action: 'flow_if',\n condition: '${giveaway[0].require_role && !member.roles.cache.has(giveaway[0].require_role)}',\n then: [\n { action: 'reply', content: 'You need the <@&${giveaway[0].require_role}> role to enter!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Check if already entered\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}', user_id: '${user.id}' },\n as: 'existing',\n },\n {\n action: 'flow_if',\n condition: '${existing.length > 0}',\n then: [\n // Remove entry\n {\n action: 'db_delete',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}', user_id: '${user.id}' },\n },\n { action: 'reply', content: 'You have left the giveaway!', ephemeral: true },\n ],\n else: [\n // Add entry\n {\n action: 'db_insert',\n table: 'giveaway_entries',\n data: {\n giveaway_id: '${giveawayId}',\n user_id: '${user.id}',\n created_at: '${now()}',\n },\n },\n { action: 'reply', content: 'You have entered the giveaway!', ephemeral: true },\n ],\n },\n // Update entry count on message\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}' },\n as: 'entries',\n },\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${giveaway[0].prize}\\n**Hosted by:** <@${giveaway[0].host_id}>\\n**Winners:** ${giveaway[0].winners_count}\\n**Entries:** ${entries.length}\\n\\nEnds: ${timestamp(giveaway[0].ends_at, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n footer: { text: 'ID: ${giveaway[0].id}' },\n },\n },\n ],\n },\n // Scheduled giveaway end\n {\n event: 'scheduler_tick',\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { ended: false },\n as: 'activeGiveaways',\n },\n {\n action: 'batch',\n items: '${activeGiveaways.filter(g => new Date(g.ends_at) <= now())}',\n each: { action: 'emit', event: 'giveaway_end', data: { giveawayId: '${item.id}' } },\n },\n ],\n },\n // End giveaway\n {\n event: 'giveaway_end',\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { id: '${event.data.giveawayId}' },\n as: 'giveaway',\n },\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${event.data.giveawayId}' },\n as: 'entries',\n },\n // Select winners\n {\n action: 'set',\n key: 'shuffled',\n value: '${shuffle(entries)}',\n },\n {\n action: 'set',\n key: 'winners',\n value: '${shuffled.slice(0, giveaway[0].winners_count)}',\n },\n // Mark as ended\n {\n action: 'db_update',\n table: 'giveaways',\n where: { id: '${event.data.giveawayId}' },\n data: { ended: true },\n },\n // Update message\n {\n action: 'flow_if',\n condition: '${winners.length === 0}',\n then: [\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: 'GIVEAWAY ENDED',\n description: '**Prize:** ${giveaway[0].prize}\\n\\nNo valid entries.',\n color: '#72767d',\n },\n components: [],\n },\n ],\n else: [\n {\n action: 'set',\n key: 'winnerMentions',\n value: '${winners.map(w => \"<@\" + w.user_id + \">\").join(\", \")}',\n },\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: 'GIVEAWAY ENDED',\n description: '**Prize:** ${giveaway[0].prize}\\n\\n**Winners:** ${winnerMentions}',\n color: '#57f287',\n },\n components: [],\n },\n {\n action: 'send_message',\n channel: '${giveaway[0].channel_id}',\n content: 'Congratulations ${winnerMentions}! You won **${giveaway[0].prize}**!',\n },\n ],\n },\n ],\n },\n];\n\nexport const giveawaysCommands: CommandDefinition[] = [\n {\n name: 'giveaway',\n description: 'Giveaway management',\n subcommands: [\n {\n name: 'start',\n description: 'Start a giveaway',\n options: [\n { name: 'prize', description: 'Prize to give away', type: 'string', required: true },\n { name: 'duration', description: 'Duration (e.g., 1h, 1d, 1w)', type: 'string', required: true },\n { name: 'winners', description: 'Number of winners', type: 'integer', required: false },\n { name: 'require_role', description: 'Required role to enter', type: 'role', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'endsAt',\n value: '${addDuration(now(), args.duration)}',\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${args.prize}\\n**Hosted by:** ${user}\\n**Winners:** ${args.winners || 1}\\n**Entries:** 0\\n\\nEnds: ${timestamp(endsAt, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n },\n components: [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'primary',\n label: 'Enter',\n emoji: '🎉',\n custom_id: 'giveaway_enter_PLACEHOLDER',\n },\n ],\n },\n ],\n as: 'giveawayMessage',\n },\n {\n action: 'db_insert',\n table: 'giveaways',\n data: {\n guild_id: '${guild.id}',\n channel_id: '${channel.id}',\n message_id: '${giveawayMessage.id}',\n host_id: '${user.id}',\n prize: '${args.prize}',\n winners_count: '${args.winners || 1}',\n ends_at: '${endsAt}',\n require_role: '${args.require_role?.id}',\n created_at: '${now()}',\n },\n as: 'giveaway',\n },\n // Update button with correct ID\n {\n action: 'edit_message',\n channel: '${channel.id}',\n message: '${giveawayMessage.id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${args.prize}\\n**Hosted by:** ${user}\\n**Winners:** ${args.winners || 1}\\n**Entries:** 0\\n\\nEnds: ${timestamp(endsAt, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n footer: { text: 'ID: ${giveaway.id}' },\n },\n components: [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'primary',\n label: 'Enter',\n emoji: '🎉',\n custom_id: 'giveaway_enter_${giveaway.id}',\n },\n ],\n },\n ],\n },\n {\n action: 'reply',\n content: 'Giveaway started!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'end',\n description: 'End a giveaway early',\n options: [\n { name: 'message_id', description: 'Giveaway message ID', type: 'string', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { message_id: '${args.message_id}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '${!giveaway[0]}',\n then: [\n { action: 'reply', content: 'Giveaway not found!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'emit',\n event: 'giveaway_end',\n data: { giveawayId: '${giveaway[0].id}' },\n },\n {\n action: 'reply',\n content: 'Giveaway ended!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'reroll',\n description: 'Reroll winners for an ended giveaway',\n options: [\n { name: 'message_id', description: 'Giveaway message ID', type: 'string', required: true },\n { name: 'winners', description: 'Number of new winners', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { message_id: '${args.message_id}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '${!giveaway[0] || !giveaway[0].ended}',\n then: [\n { action: 'reply', content: 'Giveaway not found or not ended!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveaway[0].id}' },\n as: 'entries',\n },\n {\n action: 'set',\n key: 'shuffled',\n value: '${shuffle(entries)}',\n },\n {\n action: 'set',\n key: 'winners',\n value: '${shuffled.slice(0, args.winners || 1)}',\n },\n {\n action: 'flow_if',\n condition: '${winners.length === 0}',\n then: [\n { action: 'reply', content: 'No valid entries to reroll!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set',\n key: 'winnerMentions',\n value: '${winners.map(w => \"<@\" + w.user_id + \">\").join(\", \")}',\n },\n {\n action: 'send_message',\n channel: '${giveaway[0].channel_id}',\n content: 'New winner(s): ${winnerMentions}! Congratulations on winning **${giveaway[0].prize}**!',\n },\n {\n action: 'reply',\n content: 'Rerolled successfully!',\n ephemeral: true,\n },\n ],\n },\n ],\n },\n];\n\nexport function getGiveawaysSpec(config: GiveawaysConfig = {}): Partial<FurlowSpec> {\n return {\n commands: giveawaysCommands,\n events: giveawaysEventHandlers,\n state: {\n tables: giveawaysTables,\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/giveaways/index.ts"],"sourcesContent":["/**\n * Giveaways builtin module\n * Handles giveaway creation, entries, and winner selection\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';\n\nexport interface GiveawaysConfig {\n /** Default giveaway duration */\n defaultDuration?: string;\n /** Giveaway manager role */\n managerRole?: string;\n /** Require role to enter */\n requireRole?: string;\n /** Embed color */\n embedColor?: string;\n /** End emoji */\n emoji?: string;\n}\n\nexport const giveawaysTables: Record<string, TableDefinition> = {\n giveaways: {\n columns: {\n id: { type: 'number', primary: true },\n guild_id: { type: 'string', index: true },\n channel_id: { type: 'string' },\n message_id: { type: 'string', unique: true },\n host_id: { type: 'string' },\n prize: { type: 'string' },\n winners_count: { type: 'number', default: 1 },\n ends_at: { type: 'timestamp', index: true },\n ended: { type: 'boolean', default: false },\n require_role: { type: 'string' },\n created_at: { type: 'timestamp' },\n },\n },\n giveaway_entries: {\n columns: {\n id: { type: 'number', primary: true },\n giveaway_id: { type: 'number', index: true },\n user_id: { type: 'string' },\n created_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const giveawaysEventHandlers: EventHandler[] = [\n // Handle giveaway button click\n {\n event: 'button_click',\n condition: 'interaction.customId.startsWith(\"giveaway_enter_\")',\n actions: [\n {\n action: 'set',\n key: 'giveawayId',\n value: '${interaction.customId.replace(\"giveaway_enter_\", \"\")}',\n },\n {\n action: 'db_query',\n table: 'giveaways',\n where: { id: '${giveawayId}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '!giveaway[0] || giveaway[0].ended',\n then: [\n { action: 'reply', content: 'This giveaway has ended!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Check role requirement\n {\n action: 'flow_if',\n condition: 'giveaway[0].require_role && !member.roles.cache.has(giveaway[0].require_role)',\n then: [\n { action: 'reply', content: 'You need the <@&${giveaway[0].require_role}> role to enter!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Check if already entered\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}', user_id: '${user.id}' },\n as: 'existing',\n },\n {\n action: 'flow_if',\n condition: 'existing.length > 0',\n then: [\n // Remove entry\n {\n action: 'db_delete',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}', user_id: '${user.id}' },\n },\n { action: 'reply', content: 'You have left the giveaway!', ephemeral: true },\n ],\n else: [\n // Add entry\n {\n action: 'db_insert',\n table: 'giveaway_entries',\n data: {\n giveaway_id: '${giveawayId}',\n user_id: '${user.id}',\n created_at: '${now()}',\n },\n },\n { action: 'reply', content: 'You have entered the giveaway!', ephemeral: true },\n ],\n },\n // Update entry count on message\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveawayId}' },\n as: 'entries',\n },\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${giveaway[0].prize}\\n**Hosted by:** <@${giveaway[0].host_id}>\\n**Winners:** ${giveaway[0].winners_count}\\n**Entries:** ${entries.length}\\n\\nEnds: ${timestamp(giveaway[0].ends_at, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n footer: { text: 'ID: ${giveaway[0].id}' },\n },\n },\n ],\n },\n // Scheduled giveaway end\n {\n event: 'scheduler_tick',\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { ended: false },\n as: 'activeGiveaways',\n },\n {\n action: 'batch',\n items: '${activeGiveaways.filter(g => new Date(g.ends_at) <= now())}',\n each: { action: 'emit', event: 'giveaway_end', data: { giveawayId: '${item.id}' } },\n },\n ],\n },\n // End giveaway\n {\n event: 'giveaway_end',\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { id: '${event.data.giveawayId}' },\n as: 'giveaway',\n },\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${event.data.giveawayId}' },\n as: 'entries',\n },\n // Select winners\n {\n action: 'set',\n key: 'shuffled',\n value: '${shuffle(entries)}',\n },\n {\n action: 'set',\n key: 'winners',\n value: '${shuffled.slice(0, giveaway[0].winners_count)}',\n },\n // Mark as ended\n {\n action: 'db_update',\n table: 'giveaways',\n where: { id: '${event.data.giveawayId}' },\n data: { ended: true },\n },\n // Update message\n {\n action: 'flow_if',\n condition: 'winners.length === 0',\n then: [\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: 'GIVEAWAY ENDED',\n description: '**Prize:** ${giveaway[0].prize}\\n\\nNo valid entries.',\n color: '#72767d',\n },\n components: [],\n },\n ],\n else: [\n {\n action: 'set',\n key: 'winnerMentions',\n value: '${winners.map(w => \"<@\" + w.user_id + \">\").join(\", \")}',\n },\n {\n action: 'edit_message',\n channel: '${giveaway[0].channel_id}',\n message: '${giveaway[0].message_id}',\n embed: {\n title: 'GIVEAWAY ENDED',\n description: '**Prize:** ${giveaway[0].prize}\\n\\n**Winners:** ${winnerMentions}',\n color: '#57f287',\n },\n components: [],\n },\n {\n action: 'send_message',\n channel: '${giveaway[0].channel_id}',\n content: 'Congratulations ${winnerMentions}! You won **${giveaway[0].prize}**!',\n },\n ],\n },\n ],\n },\n];\n\nexport const giveawaysCommands: CommandDefinition[] = [\n {\n name: 'giveaway',\n description: 'Giveaway management',\n subcommands: [\n {\n name: 'start',\n description: 'Start a giveaway',\n options: [\n { name: 'prize', description: 'Prize to give away', type: 'string', required: true },\n { name: 'duration', description: 'Duration (e.g., 1h, 1d, 1w)', type: 'string', required: true },\n { name: 'winners', description: 'Number of winners', type: 'integer', required: false },\n { name: 'require_role', description: 'Required role to enter', type: 'role', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'endsAt',\n value: '${addDuration(now(), args.duration)}',\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${args.prize}\\n**Hosted by:** ${user}\\n**Winners:** ${args.winners || 1}\\n**Entries:** 0\\n\\nEnds: ${timestamp(endsAt, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n },\n components: [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'primary',\n label: 'Enter',\n emoji: '🎉',\n custom_id: 'giveaway_enter_PLACEHOLDER',\n },\n ],\n },\n ],\n as: 'giveawayMessage',\n },\n {\n action: 'db_insert',\n table: 'giveaways',\n data: {\n guild_id: '${guild.id}',\n channel_id: '${channel.id}',\n message_id: '${giveawayMessage.id}',\n host_id: '${user.id}',\n prize: '${args.prize}',\n winners_count: '${args.winners || 1}',\n ends_at: '${endsAt}',\n require_role: '${args.require_role?.id}',\n created_at: '${now()}',\n },\n as: 'giveaway',\n },\n // Update button with correct ID\n {\n action: 'edit_message',\n channel: '${channel.id}',\n message: '${giveawayMessage.id}',\n embed: {\n title: '${config.giveaways?.emoji || \"🎉\"} GIVEAWAY ${config.giveaways?.emoji || \"🎉\"}',\n description: '**Prize:** ${args.prize}\\n**Hosted by:** ${user}\\n**Winners:** ${args.winners || 1}\\n**Entries:** 0\\n\\nEnds: ${timestamp(endsAt, \"R\")}',\n color: '${config.giveaways?.embedColor || \"#ff73fa\"}',\n footer: { text: 'ID: ${giveaway.id}' },\n },\n components: [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'primary',\n label: 'Enter',\n emoji: '🎉',\n custom_id: 'giveaway_enter_${giveaway.id}',\n },\n ],\n },\n ],\n },\n {\n action: 'reply',\n content: 'Giveaway started!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'end',\n description: 'End a giveaway early',\n options: [\n { name: 'message_id', description: 'Giveaway message ID', type: 'string', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { message_id: '${args.message_id}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '!giveaway[0]',\n then: [\n { action: 'reply', content: 'Giveaway not found!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'emit',\n event: 'giveaway_end',\n data: { giveawayId: '${giveaway[0].id}' },\n },\n {\n action: 'reply',\n content: 'Giveaway ended!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'reroll',\n description: 'Reroll winners for an ended giveaway',\n options: [\n { name: 'message_id', description: 'Giveaway message ID', type: 'string', required: true },\n { name: 'winners', description: 'Number of new winners', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'giveaways',\n where: { message_id: '${args.message_id}' },\n as: 'giveaway',\n },\n {\n action: 'flow_if',\n condition: '!giveaway[0] || !giveaway[0].ended',\n then: [\n { action: 'reply', content: 'Giveaway not found or not ended!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_query',\n table: 'giveaway_entries',\n where: { giveaway_id: '${giveaway[0].id}' },\n as: 'entries',\n },\n {\n action: 'set',\n key: 'shuffled',\n value: '${shuffle(entries)}',\n },\n {\n action: 'set',\n key: 'winners',\n value: '${shuffled.slice(0, args.winners || 1)}',\n },\n {\n action: 'flow_if',\n condition: 'winners.length === 0',\n then: [\n { action: 'reply', content: 'No valid entries to reroll!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set',\n key: 'winnerMentions',\n value: '${winners.map(w => \"<@\" + w.user_id + \">\").join(\", \")}',\n },\n {\n action: 'send_message',\n channel: '${giveaway[0].channel_id}',\n content: 'New winner(s): ${winnerMentions}! Congratulations on winning **${giveaway[0].prize}**!',\n },\n {\n action: 'reply',\n content: 'Rerolled successfully!',\n ephemeral: true,\n },\n ],\n },\n ],\n },\n];\n\nexport function getGiveawaysSpec(config: GiveawaysConfig = {}): Partial<FurlowSpec> {\n return {\n commands: giveawaysCommands,\n events: giveawaysEventHandlers,\n state: {\n tables: giveawaysTables,\n },\n };\n}\n"],"mappings":"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","names":[]}