@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,614 @@
1
+ // src/tickets/index.ts
2
+ var ticketsTables = {
3
+ tickets: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ channel_id: { type: "string", unique: true },
7
+ guild_id: { type: "string", index: true },
8
+ user_id: { type: "string", index: true },
9
+ category: { type: "string" },
10
+ claimed_by: { type: "string" },
11
+ status: { type: "string", default: "open" },
12
+ created_at: { type: "timestamp" },
13
+ closed_at: { type: "timestamp" }
14
+ }
15
+ },
16
+ ticket_messages: {
17
+ columns: {
18
+ id: { type: "number", primary: true },
19
+ ticket_id: { type: "number", index: true },
20
+ author_id: { type: "string" },
21
+ author_name: { type: "string" },
22
+ content: { type: "string" },
23
+ attachments: { type: "json" },
24
+ created_at: { type: "timestamp" }
25
+ }
26
+ }
27
+ };
28
+ var ticketPanelComponents = [
29
+ {
30
+ type: "action_row",
31
+ components: [
32
+ {
33
+ type: "button",
34
+ style: "primary",
35
+ label: "Create Ticket",
36
+ emoji: "\u{1F4E9}",
37
+ custom_id: "ticket_create"
38
+ }
39
+ ]
40
+ }
41
+ ];
42
+ var ticketControlComponents = [
43
+ {
44
+ type: "action_row",
45
+ components: [
46
+ {
47
+ type: "button",
48
+ style: "secondary",
49
+ label: "Claim",
50
+ emoji: "\u{1F64B}",
51
+ custom_id: "ticket_claim"
52
+ },
53
+ {
54
+ type: "button",
55
+ style: "danger",
56
+ label: "Close",
57
+ emoji: "\u{1F512}",
58
+ custom_id: "ticket_close"
59
+ },
60
+ {
61
+ type: "button",
62
+ style: "secondary",
63
+ label: "Transcript",
64
+ emoji: "\u{1F4DC}",
65
+ custom_id: "ticket_transcript"
66
+ }
67
+ ]
68
+ }
69
+ ];
70
+ var ticketsEventHandlers = [
71
+ // Handle ticket creation button
72
+ {
73
+ event: "button_click",
74
+ condition: '${interaction.customId === "ticket_create"}',
75
+ actions: [
76
+ // Check max tickets
77
+ {
78
+ action: "db_query",
79
+ table: "tickets",
80
+ where: {
81
+ guild_id: "${guild.id}",
82
+ user_id: "${user.id}",
83
+ status: "open"
84
+ },
85
+ as: "userTickets"
86
+ },
87
+ {
88
+ action: "flow_if",
89
+ condition: "${userTickets.length >= (config.tickets.maxTicketsPerUser || 1)}",
90
+ then: [
91
+ {
92
+ action: "reply",
93
+ content: "You already have the maximum number of open tickets!",
94
+ ephemeral: true
95
+ },
96
+ { action: "abort" }
97
+ ]
98
+ },
99
+ // Show category selector if configured
100
+ {
101
+ action: "flow_if",
102
+ condition: "${config.tickets.categories && config.tickets.categories.length > 0}",
103
+ then: [
104
+ {
105
+ action: "reply",
106
+ content: "Select a ticket category:",
107
+ ephemeral: true,
108
+ components: [
109
+ {
110
+ type: "action_row",
111
+ components: [
112
+ {
113
+ type: "select_menu",
114
+ custom_id: "ticket_category_select",
115
+ placeholder: "Select category...",
116
+ options: "${config.tickets.categories | map(c => ({ label: c.name, value: c.id, description: c.description, emoji: c.emoji }))}"
117
+ }
118
+ ]
119
+ }
120
+ ]
121
+ }
122
+ ],
123
+ else: [
124
+ { action: "emit", event: "ticket_create_confirmed", data: { category: "general" } }
125
+ ]
126
+ }
127
+ ]
128
+ },
129
+ // Handle category selection
130
+ {
131
+ event: "select_menu",
132
+ condition: '${interaction.customId === "ticket_category_select"}',
133
+ actions: [
134
+ {
135
+ action: "emit",
136
+ event: "ticket_create_confirmed",
137
+ data: { category: "${interaction.values[0]}" }
138
+ }
139
+ ]
140
+ },
141
+ // Create the ticket channel
142
+ {
143
+ event: "ticket_create_confirmed",
144
+ actions: [
145
+ {
146
+ action: "db_query",
147
+ table: "tickets",
148
+ where: { guild_id: "${guild.id}" },
149
+ order_by: "id DESC",
150
+ limit: 1,
151
+ as: "lastTicket"
152
+ },
153
+ {
154
+ action: "set",
155
+ key: "ticketNumber",
156
+ value: "${(lastTicket[0]?.id || 0) + 1}"
157
+ },
158
+ {
159
+ action: "set",
160
+ key: "channelName",
161
+ value: '${(config.tickets.channelPattern || "ticket-{number}") | replace("{number}", ticketNumber) | replace("{user}", user.username) | replace("{category}", event.data.category)}'
162
+ },
163
+ // Get support roles for this category
164
+ {
165
+ action: "set",
166
+ key: "categoryConfig",
167
+ value: "${config.tickets.categories?.find(c => c.id === event.data.category)}"
168
+ },
169
+ {
170
+ action: "set",
171
+ key: "supportRoles",
172
+ value: "${categoryConfig?.supportRoles || config.tickets.supportRoles || []}"
173
+ },
174
+ // Create channel with permissions
175
+ {
176
+ action: "create_channel",
177
+ name: "${channelName}",
178
+ type: "text",
179
+ parent: "${config.tickets.category}",
180
+ permission_overwrites: [
181
+ {
182
+ id: "${guild.id}",
183
+ type: "role",
184
+ deny: ["VIEW_CHANNEL"]
185
+ },
186
+ {
187
+ id: "${user.id}",
188
+ type: "member",
189
+ allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "ATTACH_FILES"]
190
+ },
191
+ '${supportRoles | map(r => ({ id: r, type: "role", allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "ATTACH_FILES", "MANAGE_MESSAGES"] }))}'
192
+ ],
193
+ as: "ticketChannel"
194
+ },
195
+ // Store in database
196
+ {
197
+ action: "db_insert",
198
+ table: "tickets",
199
+ data: {
200
+ channel_id: "${ticketChannel.id}",
201
+ guild_id: "${guild.id}",
202
+ user_id: "${user.id}",
203
+ category: "${event.data.category}",
204
+ status: "open",
205
+ created_at: "${now()}"
206
+ }
207
+ },
208
+ // Send opening message
209
+ {
210
+ action: "send_message",
211
+ channel: "${ticketChannel.id}",
212
+ content: "Welcome ${user}! Support will be with you shortly.",
213
+ embed: {
214
+ title: "Ticket #${ticketNumber}",
215
+ description: "Please describe your issue and a staff member will assist you.",
216
+ color: "#5865f2",
217
+ fields: [
218
+ { name: "Category", value: '${categoryConfig?.name || "General"}', inline: true },
219
+ { name: "Created by", value: "${user}", inline: true }
220
+ ],
221
+ timestamp: "${now()}"
222
+ },
223
+ components: "${ticketControlComponents}"
224
+ },
225
+ // Reply to original interaction
226
+ {
227
+ action: "update_message",
228
+ content: "Your ticket has been created: ${ticketChannel}",
229
+ components: []
230
+ }
231
+ ]
232
+ },
233
+ // Handle claim button
234
+ {
235
+ event: "button_click",
236
+ condition: '${interaction.customId === "ticket_claim"}',
237
+ actions: [
238
+ {
239
+ action: "db_query",
240
+ table: "tickets",
241
+ where: { channel_id: "${channel.id}" },
242
+ as: "ticket"
243
+ },
244
+ {
245
+ action: "flow_if",
246
+ condition: "${!ticket[0]}",
247
+ then: [
248
+ { action: "reply", content: "This is not a ticket channel!", ephemeral: true },
249
+ { action: "abort" }
250
+ ]
251
+ },
252
+ {
253
+ action: "flow_if",
254
+ condition: "${ticket[0].claimed_by}",
255
+ then: [
256
+ { action: "reply", content: "This ticket is already claimed by <@${ticket[0].claimed_by}>!", ephemeral: true },
257
+ { action: "abort" }
258
+ ]
259
+ },
260
+ {
261
+ action: "db_update",
262
+ table: "tickets",
263
+ where: { channel_id: "${channel.id}" },
264
+ data: { claimed_by: "${user.id}" }
265
+ },
266
+ {
267
+ action: "send_message",
268
+ channel: "${channel.id}",
269
+ embed: {
270
+ description: "${user} has claimed this ticket.",
271
+ color: "#57f287"
272
+ }
273
+ },
274
+ { action: "reply", content: "You have claimed this ticket!", ephemeral: true }
275
+ ]
276
+ },
277
+ // Handle close button
278
+ {
279
+ event: "button_click",
280
+ condition: '${interaction.customId === "ticket_close"}',
281
+ actions: [
282
+ {
283
+ action: "db_query",
284
+ table: "tickets",
285
+ where: { channel_id: "${channel.id}" },
286
+ as: "ticket"
287
+ },
288
+ {
289
+ action: "flow_if",
290
+ condition: "${!ticket[0]}",
291
+ then: [
292
+ { action: "reply", content: "This is not a ticket channel!", ephemeral: true },
293
+ { action: "abort" }
294
+ ]
295
+ },
296
+ // Confirm close
297
+ {
298
+ action: "reply",
299
+ content: "Are you sure you want to close this ticket?",
300
+ ephemeral: true,
301
+ components: [
302
+ {
303
+ type: "action_row",
304
+ components: [
305
+ { type: "button", style: "danger", label: "Close Ticket", custom_id: "ticket_close_confirm" },
306
+ { type: "button", style: "secondary", label: "Cancel", custom_id: "ticket_close_cancel" }
307
+ ]
308
+ }
309
+ ]
310
+ }
311
+ ]
312
+ },
313
+ // Handle close confirmation
314
+ {
315
+ event: "button_click",
316
+ condition: '${interaction.customId === "ticket_close_confirm"}',
317
+ actions: [
318
+ {
319
+ action: "db_query",
320
+ table: "tickets",
321
+ where: { channel_id: "${channel.id}" },
322
+ as: "ticket"
323
+ },
324
+ // Generate transcript if configured
325
+ {
326
+ action: "flow_if",
327
+ condition: "${config.tickets.includeTranscript && config.tickets.logChannel}",
328
+ then: [
329
+ {
330
+ action: "db_query",
331
+ table: "ticket_messages",
332
+ where: { ticket_id: "${ticket[0].id}" },
333
+ order_by: "created_at ASC",
334
+ as: "messages"
335
+ },
336
+ {
337
+ action: "set",
338
+ key: "transcriptText",
339
+ value: '${messages | map(m => "[" + formatDate(m.created_at, "YYYY-MM-DD HH:mm:ss") + "] " + m.author_name + ": " + m.content) | join("\\n")}'
340
+ },
341
+ {
342
+ action: "send_message",
343
+ channel: "${config.tickets.logChannel}",
344
+ embed: {
345
+ title: "Ticket Closed",
346
+ fields: [
347
+ { name: "Ticket ID", value: "${ticket[0].id}", inline: true },
348
+ { name: "Opened by", value: "<@${ticket[0].user_id}>", inline: true },
349
+ { name: "Closed by", value: "${user}", inline: true },
350
+ { name: "Category", value: "${ticket[0].category}", inline: true }
351
+ ],
352
+ color: "#ed4245",
353
+ timestamp: "${now()}"
354
+ },
355
+ files: [
356
+ {
357
+ attachment: "${Buffer.from(transcriptText)}",
358
+ name: "transcript-${ticket[0].id}.txt"
359
+ }
360
+ ]
361
+ }
362
+ ]
363
+ },
364
+ // Update database
365
+ {
366
+ action: "db_update",
367
+ table: "tickets",
368
+ where: { channel_id: "${channel.id}" },
369
+ data: { status: "closed", closed_at: "${now()}" }
370
+ },
371
+ // DM user if configured
372
+ {
373
+ action: "flow_if",
374
+ condition: "${config.tickets.dmOnClose}",
375
+ then: [
376
+ {
377
+ action: "send_dm",
378
+ user: "${ticket[0].user_id}",
379
+ embed: {
380
+ title: "Ticket Closed",
381
+ description: "Your ticket in ${guild.name} has been closed.",
382
+ color: "#ed4245"
383
+ }
384
+ }
385
+ ]
386
+ },
387
+ // Delete channel after delay
388
+ {
389
+ action: "send_message",
390
+ channel: "${channel.id}",
391
+ content: "This ticket will be deleted in 5 seconds..."
392
+ },
393
+ { action: "wait", duration: 5e3 },
394
+ { action: "delete_channel", channel: "${channel.id}" }
395
+ ]
396
+ },
397
+ // Handle transcript button
398
+ {
399
+ event: "button_click",
400
+ condition: '${interaction.customId === "ticket_transcript"}',
401
+ actions: [
402
+ {
403
+ action: "db_query",
404
+ table: "tickets",
405
+ where: { channel_id: "${channel.id}" },
406
+ as: "ticket"
407
+ },
408
+ {
409
+ action: "db_query",
410
+ table: "ticket_messages",
411
+ where: { ticket_id: "${ticket[0].id}" },
412
+ order_by: "created_at ASC",
413
+ as: "messages"
414
+ },
415
+ {
416
+ action: "set",
417
+ key: "transcriptText",
418
+ value: '${messages | map(m => "[" + formatDate(m.created_at, "YYYY-MM-DD HH:mm:ss") + "] " + m.author_name + ": " + m.content) | join("\\n")}'
419
+ },
420
+ {
421
+ action: "reply",
422
+ content: "Here is the transcript:",
423
+ ephemeral: true,
424
+ files: [
425
+ {
426
+ attachment: "${Buffer.from(transcriptText)}",
427
+ name: "transcript-${ticket[0].id}.txt"
428
+ }
429
+ ]
430
+ }
431
+ ]
432
+ },
433
+ // Log ticket messages
434
+ {
435
+ event: "message",
436
+ condition: "${channel.parentId === config.tickets.category}",
437
+ actions: [
438
+ {
439
+ action: "db_query",
440
+ table: "tickets",
441
+ where: { channel_id: "${channel.id}" },
442
+ as: "ticket"
443
+ },
444
+ {
445
+ action: "flow_if",
446
+ condition: "${ticket[0]}",
447
+ then: [
448
+ {
449
+ action: "db_insert",
450
+ table: "ticket_messages",
451
+ data: {
452
+ ticket_id: "${ticket[0].id}",
453
+ author_id: "${user.id}",
454
+ author_name: "${user.tag}",
455
+ content: "${message.content}",
456
+ attachments: "${message.attachments | map(a => a.url)}",
457
+ created_at: "${now()}"
458
+ }
459
+ }
460
+ ]
461
+ }
462
+ ]
463
+ }
464
+ ];
465
+ var ticketsCommands = [
466
+ {
467
+ name: "ticket",
468
+ description: "Ticket management commands",
469
+ subcommands: [
470
+ {
471
+ name: "panel",
472
+ description: "Create a ticket panel",
473
+ options: [
474
+ { name: "channel", description: "Channel to send panel", type: "channel", required: false },
475
+ { name: "message", description: "Custom panel message", type: "string", required: false }
476
+ ],
477
+ actions: [
478
+ {
479
+ action: "send_message",
480
+ channel: "${args.channel?.id || channel.id}",
481
+ embed: {
482
+ title: "Support Tickets",
483
+ description: '${args.message || "Need help? Click the button below to create a support ticket."}',
484
+ color: "#5865f2"
485
+ },
486
+ components: "${ticketPanelComponents}"
487
+ },
488
+ {
489
+ action: "reply",
490
+ content: "Ticket panel created!",
491
+ ephemeral: true
492
+ }
493
+ ]
494
+ },
495
+ {
496
+ name: "add",
497
+ description: "Add a user to the ticket",
498
+ options: [
499
+ { name: "user", description: "User to add", type: "user", required: true }
500
+ ],
501
+ actions: [
502
+ {
503
+ action: "db_query",
504
+ table: "tickets",
505
+ where: { channel_id: "${channel.id}" },
506
+ as: "ticket"
507
+ },
508
+ {
509
+ action: "flow_if",
510
+ condition: "${!ticket[0]}",
511
+ then: [
512
+ { action: "reply", content: "This is not a ticket channel!", ephemeral: true },
513
+ { action: "abort" }
514
+ ]
515
+ },
516
+ {
517
+ action: "set_channel_permissions",
518
+ channel: "${channel.id}",
519
+ user: "${args.user.id}",
520
+ allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "READ_MESSAGE_HISTORY"]
521
+ },
522
+ {
523
+ action: "send_message",
524
+ channel: "${channel.id}",
525
+ content: "${args.user} has been added to the ticket."
526
+ },
527
+ { action: "reply", content: "User added!", ephemeral: true }
528
+ ]
529
+ },
530
+ {
531
+ name: "remove",
532
+ description: "Remove a user from the ticket",
533
+ options: [
534
+ { name: "user", description: "User to remove", type: "user", required: true }
535
+ ],
536
+ actions: [
537
+ {
538
+ action: "db_query",
539
+ table: "tickets",
540
+ where: { channel_id: "${channel.id}" },
541
+ as: "ticket"
542
+ },
543
+ {
544
+ action: "flow_if",
545
+ condition: "${!ticket[0]}",
546
+ then: [
547
+ { action: "reply", content: "This is not a ticket channel!", ephemeral: true },
548
+ { action: "abort" }
549
+ ]
550
+ },
551
+ {
552
+ action: "set_channel_permissions",
553
+ channel: "${channel.id}",
554
+ user: "${args.user.id}",
555
+ deny: ["VIEW_CHANNEL"]
556
+ },
557
+ {
558
+ action: "send_message",
559
+ channel: "${channel.id}",
560
+ content: "${args.user.username} has been removed from the ticket."
561
+ },
562
+ { action: "reply", content: "User removed!", ephemeral: true }
563
+ ]
564
+ },
565
+ {
566
+ name: "rename",
567
+ description: "Rename the ticket channel",
568
+ options: [
569
+ { name: "name", description: "New channel name", type: "string", required: true }
570
+ ],
571
+ actions: [
572
+ {
573
+ action: "db_query",
574
+ table: "tickets",
575
+ where: { channel_id: "${channel.id}" },
576
+ as: "ticket"
577
+ },
578
+ {
579
+ action: "flow_if",
580
+ condition: "${!ticket[0]}",
581
+ then: [
582
+ { action: "reply", content: "This is not a ticket channel!", ephemeral: true },
583
+ { action: "abort" }
584
+ ]
585
+ },
586
+ {
587
+ action: "edit_channel",
588
+ channel: "${channel.id}",
589
+ name: "${args.name}"
590
+ },
591
+ { action: "reply", content: "Ticket renamed!", ephemeral: true }
592
+ ]
593
+ }
594
+ ]
595
+ }
596
+ ];
597
+ function getTicketsSpec(config = {}) {
598
+ return {
599
+ events: ticketsEventHandlers,
600
+ commands: ticketsCommands,
601
+ state: {
602
+ tables: ticketsTables
603
+ }
604
+ };
605
+ }
606
+ export {
607
+ getTicketsSpec,
608
+ ticketControlComponents,
609
+ ticketPanelComponents,
610
+ ticketsCommands,
611
+ ticketsEventHandlers,
612
+ ticketsTables
613
+ };
614
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tickets/index.ts"],"sourcesContent":["/**\n * Tickets builtin module\n * Handles ticket creation, claiming, closing, and transcripts\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition, ComponentDefinition } from '@furlow/schema';\n\nexport interface TicketsConfig {\n /** Category to create tickets in */\n category?: string;\n /** Support roles that can see tickets */\n supportRoles?: string[];\n /** Ticket channel naming pattern */\n channelPattern?: string;\n /** Maximum open tickets per user */\n maxTicketsPerUser?: number;\n /** Ticket panel channel */\n panelChannel?: string;\n /** Log channel for ticket transcripts */\n logChannel?: string;\n /** Auto-close inactive tickets (hours) */\n autoCloseAfter?: number;\n /** Ticket categories/types */\n categories?: Array<{\n id: string;\n name: string;\n description?: string;\n emoji?: string;\n supportRoles?: string[];\n }>;\n /** DM user on ticket close */\n dmOnClose?: boolean;\n /** Include transcript on close */\n includeTranscript?: boolean;\n}\n\nexport const ticketsTables: Record<string, TableDefinition> = {\n tickets: {\n columns: {\n id: { type: 'number', primary: true },\n channel_id: { type: 'string', unique: true },\n guild_id: { type: 'string', index: true },\n user_id: { type: 'string', index: true },\n category: { type: 'string' },\n claimed_by: { type: 'string' },\n status: { type: 'string', default: 'open' },\n created_at: { type: 'timestamp' },\n closed_at: { type: 'timestamp' },\n },\n },\n ticket_messages: {\n columns: {\n id: { type: 'number', primary: true },\n ticket_id: { type: 'number', index: true },\n author_id: { type: 'string' },\n author_name: { type: 'string' },\n content: { type: 'string' },\n attachments: { type: 'json' },\n created_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const ticketPanelComponents: ComponentDefinition[] = [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'primary',\n label: 'Create Ticket',\n emoji: '📩',\n custom_id: 'ticket_create',\n },\n ],\n },\n];\n\nexport const ticketControlComponents: ComponentDefinition[] = [\n {\n type: 'action_row',\n components: [\n {\n type: 'button',\n style: 'secondary',\n label: 'Claim',\n emoji: '🙋',\n custom_id: 'ticket_claim',\n },\n {\n type: 'button',\n style: 'danger',\n label: 'Close',\n emoji: '🔒',\n custom_id: 'ticket_close',\n },\n {\n type: 'button',\n style: 'secondary',\n label: 'Transcript',\n emoji: '📜',\n custom_id: 'ticket_transcript',\n },\n ],\n },\n];\n\nexport const ticketsEventHandlers: EventHandler[] = [\n // Handle ticket creation button\n {\n event: 'button_click',\n condition: '${interaction.customId === \"ticket_create\"}',\n actions: [\n // Check max tickets\n {\n action: 'db_query',\n table: 'tickets',\n where: {\n guild_id: '${guild.id}',\n user_id: '${user.id}',\n status: 'open',\n },\n as: 'userTickets',\n },\n {\n action: 'flow_if',\n condition: '${userTickets.length >= (config.tickets.maxTicketsPerUser || 1)}',\n then: [\n {\n action: 'reply',\n content: 'You already have the maximum number of open tickets!',\n ephemeral: true,\n },\n { action: 'abort' },\n ],\n },\n // Show category selector if configured\n {\n action: 'flow_if',\n condition: '${config.tickets.categories && config.tickets.categories.length > 0}',\n then: [\n {\n action: 'reply',\n content: 'Select a ticket category:',\n ephemeral: true,\n components: [\n {\n type: 'action_row',\n components: [\n {\n type: 'select_menu',\n custom_id: 'ticket_category_select',\n placeholder: 'Select category...',\n options: '${config.tickets.categories | map(c => ({ label: c.name, value: c.id, description: c.description, emoji: c.emoji }))}',\n },\n ],\n },\n ],\n },\n ],\n else: [\n { action: 'emit', event: 'ticket_create_confirmed', data: { category: 'general' } },\n ],\n },\n ],\n },\n // Handle category selection\n {\n event: 'select_menu',\n condition: '${interaction.customId === \"ticket_category_select\"}',\n actions: [\n {\n action: 'emit',\n event: 'ticket_create_confirmed',\n data: { category: '${interaction.values[0]}' },\n },\n ],\n },\n // Create the ticket channel\n {\n event: 'ticket_create_confirmed',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { guild_id: '${guild.id}' },\n order_by: 'id DESC',\n limit: 1,\n as: 'lastTicket',\n },\n {\n action: 'set',\n key: 'ticketNumber',\n value: '${(lastTicket[0]?.id || 0) + 1}',\n },\n {\n action: 'set',\n key: 'channelName',\n value: '${(config.tickets.channelPattern || \"ticket-{number}\") | replace(\"{number}\", ticketNumber) | replace(\"{user}\", user.username) | replace(\"{category}\", event.data.category)}',\n },\n // Get support roles for this category\n {\n action: 'set',\n key: 'categoryConfig',\n value: '${config.tickets.categories?.find(c => c.id === event.data.category)}',\n },\n {\n action: 'set',\n key: 'supportRoles',\n value: '${categoryConfig?.supportRoles || config.tickets.supportRoles || []}',\n },\n // Create channel with permissions\n {\n action: 'create_channel',\n name: '${channelName}',\n type: 'text',\n parent: '${config.tickets.category}',\n permission_overwrites: [\n {\n id: '${guild.id}',\n type: 'role',\n deny: ['VIEW_CHANNEL'],\n },\n {\n id: '${user.id}',\n type: 'member',\n allow: ['VIEW_CHANNEL', 'SEND_MESSAGES', 'READ_MESSAGE_HISTORY', 'ATTACH_FILES'],\n },\n '${supportRoles | map(r => ({ id: r, type: \"role\", allow: [\"VIEW_CHANNEL\", \"SEND_MESSAGES\", \"READ_MESSAGE_HISTORY\", \"ATTACH_FILES\", \"MANAGE_MESSAGES\"] }))}',\n ],\n as: 'ticketChannel',\n },\n // Store in database\n {\n action: 'db_insert',\n table: 'tickets',\n data: {\n channel_id: '${ticketChannel.id}',\n guild_id: '${guild.id}',\n user_id: '${user.id}',\n category: '${event.data.category}',\n status: 'open',\n created_at: '${now()}',\n },\n },\n // Send opening message\n {\n action: 'send_message',\n channel: '${ticketChannel.id}',\n content: 'Welcome ${user}! Support will be with you shortly.',\n embed: {\n title: 'Ticket #${ticketNumber}',\n description: 'Please describe your issue and a staff member will assist you.',\n color: '#5865f2',\n fields: [\n { name: 'Category', value: '${categoryConfig?.name || \"General\"}', inline: true },\n { name: 'Created by', value: '${user}', inline: true },\n ],\n timestamp: '${now()}',\n },\n components: '${ticketControlComponents}',\n },\n // Reply to original interaction\n {\n action: 'update_message',\n content: 'Your ticket has been created: ${ticketChannel}',\n components: [],\n },\n ],\n },\n // Handle claim button\n {\n event: 'button_click',\n condition: '${interaction.customId === \"ticket_claim\"}',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${!ticket[0]}',\n then: [\n { action: 'reply', content: 'This is not a ticket channel!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'flow_if',\n condition: '${ticket[0].claimed_by}',\n then: [\n { action: 'reply', content: 'This ticket is already claimed by <@${ticket[0].claimed_by}>!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_update',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n data: { claimed_by: '${user.id}' },\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n embed: {\n description: '${user} has claimed this ticket.',\n color: '#57f287',\n },\n },\n { action: 'reply', content: 'You have claimed this ticket!', ephemeral: true },\n ],\n },\n // Handle close button\n {\n event: 'button_click',\n condition: '${interaction.customId === \"ticket_close\"}',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${!ticket[0]}',\n then: [\n { action: 'reply', content: 'This is not a ticket channel!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Confirm close\n {\n action: 'reply',\n content: 'Are you sure you want to close this ticket?',\n ephemeral: true,\n components: [\n {\n type: 'action_row',\n components: [\n { type: 'button', style: 'danger', label: 'Close Ticket', custom_id: 'ticket_close_confirm' },\n { type: 'button', style: 'secondary', label: 'Cancel', custom_id: 'ticket_close_cancel' },\n ],\n },\n ],\n },\n ],\n },\n // Handle close confirmation\n {\n event: 'button_click',\n condition: '${interaction.customId === \"ticket_close_confirm\"}',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n // Generate transcript if configured\n {\n action: 'flow_if',\n condition: '${config.tickets.includeTranscript && config.tickets.logChannel}',\n then: [\n {\n action: 'db_query',\n table: 'ticket_messages',\n where: { ticket_id: '${ticket[0].id}' },\n order_by: 'created_at ASC',\n as: 'messages',\n },\n {\n action: 'set',\n key: 'transcriptText',\n value: '${messages | map(m => \"[\" + formatDate(m.created_at, \"YYYY-MM-DD HH:mm:ss\") + \"] \" + m.author_name + \": \" + m.content) | join(\"\\\\n\")}',\n },\n {\n action: 'send_message',\n channel: '${config.tickets.logChannel}',\n embed: {\n title: 'Ticket Closed',\n fields: [\n { name: 'Ticket ID', value: '${ticket[0].id}', inline: true },\n { name: 'Opened by', value: '<@${ticket[0].user_id}>', inline: true },\n { name: 'Closed by', value: '${user}', inline: true },\n { name: 'Category', value: '${ticket[0].category}', inline: true },\n ],\n color: '#ed4245',\n timestamp: '${now()}',\n },\n files: [\n {\n attachment: '${Buffer.from(transcriptText)}',\n name: 'transcript-${ticket[0].id}.txt',\n },\n ],\n },\n ],\n },\n // Update database\n {\n action: 'db_update',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n data: { status: 'closed', closed_at: '${now()}' },\n },\n // DM user if configured\n {\n action: 'flow_if',\n condition: '${config.tickets.dmOnClose}',\n then: [\n {\n action: 'send_dm',\n user: '${ticket[0].user_id}',\n embed: {\n title: 'Ticket Closed',\n description: 'Your ticket in ${guild.name} has been closed.',\n color: '#ed4245',\n },\n },\n ],\n },\n // Delete channel after delay\n {\n action: 'send_message',\n channel: '${channel.id}',\n content: 'This ticket will be deleted in 5 seconds...',\n },\n { action: 'wait', duration: 5000 },\n { action: 'delete_channel', channel: '${channel.id}' },\n ],\n },\n // Handle transcript button\n {\n event: 'button_click',\n condition: '${interaction.customId === \"ticket_transcript\"}',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'db_query',\n table: 'ticket_messages',\n where: { ticket_id: '${ticket[0].id}' },\n order_by: 'created_at ASC',\n as: 'messages',\n },\n {\n action: 'set',\n key: 'transcriptText',\n value: '${messages | map(m => \"[\" + formatDate(m.created_at, \"YYYY-MM-DD HH:mm:ss\") + \"] \" + m.author_name + \": \" + m.content) | join(\"\\\\n\")}',\n },\n {\n action: 'reply',\n content: 'Here is the transcript:',\n ephemeral: true,\n files: [\n {\n attachment: '${Buffer.from(transcriptText)}',\n name: 'transcript-${ticket[0].id}.txt',\n },\n ],\n },\n ],\n },\n // Log ticket messages\n {\n event: 'message',\n condition: '${channel.parentId === config.tickets.category}',\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${ticket[0]}',\n then: [\n {\n action: 'db_insert',\n table: 'ticket_messages',\n data: {\n ticket_id: '${ticket[0].id}',\n author_id: '${user.id}',\n author_name: '${user.tag}',\n content: '${message.content}',\n attachments: '${message.attachments | map(a => a.url)}',\n created_at: '${now()}',\n },\n },\n ],\n },\n ],\n },\n];\n\nexport const ticketsCommands: CommandDefinition[] = [\n {\n name: 'ticket',\n description: 'Ticket management commands',\n subcommands: [\n {\n name: 'panel',\n description: 'Create a ticket panel',\n options: [\n { name: 'channel', description: 'Channel to send panel', type: 'channel', required: false },\n { name: 'message', description: 'Custom panel message', type: 'string', required: false },\n ],\n actions: [\n {\n action: 'send_message',\n channel: '${args.channel?.id || channel.id}',\n embed: {\n title: 'Support Tickets',\n description: '${args.message || \"Need help? Click the button below to create a support ticket.\"}',\n color: '#5865f2',\n },\n components: '${ticketPanelComponents}',\n },\n {\n action: 'reply',\n content: 'Ticket panel created!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'add',\n description: 'Add a user to the ticket',\n options: [\n { name: 'user', description: 'User to add', type: 'user', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${!ticket[0]}',\n then: [\n { action: 'reply', content: 'This is not a ticket channel!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set_channel_permissions',\n channel: '${channel.id}',\n user: '${args.user.id}',\n allow: ['VIEW_CHANNEL', 'SEND_MESSAGES', 'READ_MESSAGE_HISTORY'],\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n content: '${args.user} has been added to the ticket.',\n },\n { action: 'reply', content: 'User added!', ephemeral: true },\n ],\n },\n {\n name: 'remove',\n description: 'Remove a user from the ticket',\n options: [\n { name: 'user', description: 'User to remove', type: 'user', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${!ticket[0]}',\n then: [\n { action: 'reply', content: 'This is not a ticket channel!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set_channel_permissions',\n channel: '${channel.id}',\n user: '${args.user.id}',\n deny: ['VIEW_CHANNEL'],\n },\n {\n action: 'send_message',\n channel: '${channel.id}',\n content: '${args.user.username} has been removed from the ticket.',\n },\n { action: 'reply', content: 'User removed!', ephemeral: true },\n ],\n },\n {\n name: 'rename',\n description: 'Rename the ticket channel',\n options: [\n { name: 'name', description: 'New channel name', type: 'string', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'tickets',\n where: { channel_id: '${channel.id}' },\n as: 'ticket',\n },\n {\n action: 'flow_if',\n condition: '${!ticket[0]}',\n then: [\n { action: 'reply', content: 'This is not a ticket channel!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'edit_channel',\n channel: '${channel.id}',\n name: '${args.name}',\n },\n { action: 'reply', content: 'Ticket renamed!', ephemeral: true },\n ],\n },\n ],\n },\n];\n\nexport function getTicketsSpec(config: TicketsConfig = {}): Partial<FurlowSpec> {\n return {\n events: ticketsEventHandlers,\n commands: ticketsCommands,\n state: {\n tables: ticketsTables,\n },\n };\n}\n"],"mappings":"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","names":[]}