@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,224 @@
1
+ // src/reminders/index.ts
2
+ var remindersTables = {
3
+ reminders: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ guild_id: { type: "string", index: true },
7
+ channel_id: { type: "string" },
8
+ user_id: { type: "string", index: true },
9
+ message: { type: "string" },
10
+ remind_at: { type: "timestamp", index: true },
11
+ dm: { type: "boolean", default: false },
12
+ created_at: { type: "timestamp" }
13
+ }
14
+ }
15
+ };
16
+ var remindersEventHandlers = [
17
+ // Check reminders on scheduler tick
18
+ {
19
+ event: "scheduler_tick",
20
+ actions: [
21
+ {
22
+ action: "db_query",
23
+ table: "reminders",
24
+ where: {},
25
+ as: "allReminders"
26
+ },
27
+ {
28
+ action: "set",
29
+ key: "dueReminders",
30
+ value: "${allReminders.filter(r => new Date(r.remind_at) <= now())}"
31
+ },
32
+ {
33
+ action: "batch",
34
+ items: "${dueReminders}",
35
+ each: { action: "emit", event: "reminder_due", data: { reminder: "${item}" } }
36
+ }
37
+ ]
38
+ },
39
+ // Handle due reminder
40
+ {
41
+ event: "reminder_due",
42
+ actions: [
43
+ {
44
+ action: "set",
45
+ key: "reminder",
46
+ value: "${event.data.reminder}"
47
+ },
48
+ // Delete the reminder
49
+ {
50
+ action: "db_delete",
51
+ table: "reminders",
52
+ where: { id: "${reminder.id}" }
53
+ },
54
+ // Send reminder
55
+ {
56
+ action: "flow_if",
57
+ condition: "${reminder.dm}",
58
+ then: [
59
+ {
60
+ action: "send_dm",
61
+ user: "${reminder.user_id}",
62
+ embed: {
63
+ title: "Reminder",
64
+ description: "${reminder.message}",
65
+ color: "#5865f2",
66
+ footer: { text: 'Set ${timestamp(reminder.created_at, "R")}' }
67
+ }
68
+ }
69
+ ],
70
+ else: [
71
+ {
72
+ action: "send_message",
73
+ channel: "${reminder.channel_id}",
74
+ content: "<@${reminder.user_id}> Reminder: ${reminder.message}"
75
+ }
76
+ ]
77
+ }
78
+ ]
79
+ }
80
+ ];
81
+ var remindersCommands = [
82
+ {
83
+ name: "remind",
84
+ description: "Set a reminder",
85
+ options: [
86
+ { name: "time", description: "When to remind (e.g., 10m, 2h, 1d)", type: "string", required: true },
87
+ { name: "message", description: "Reminder message", type: "string", required: true },
88
+ { name: "dm", description: "Send reminder via DM", type: "boolean", required: false }
89
+ ],
90
+ actions: [
91
+ // Check max reminders
92
+ {
93
+ action: "db_query",
94
+ table: "reminders",
95
+ where: { user_id: "${user.id}" },
96
+ as: "userReminders"
97
+ },
98
+ {
99
+ action: "flow_if",
100
+ condition: "${userReminders.length >= (config.reminders?.maxRemindersPerUser || 25)}",
101
+ then: [
102
+ { action: "reply", content: "You have too many reminders! Delete some first.", ephemeral: true },
103
+ { action: "abort" }
104
+ ]
105
+ },
106
+ // Calculate remind time
107
+ {
108
+ action: "set",
109
+ key: "remindAt",
110
+ value: "${addDuration(now(), args.time)}"
111
+ },
112
+ // Create reminder
113
+ {
114
+ action: "db_insert",
115
+ table: "reminders",
116
+ data: {
117
+ guild_id: "${guild.id}",
118
+ channel_id: "${channel.id}",
119
+ user_id: "${user.id}",
120
+ message: "${args.message}",
121
+ remind_at: "${remindAt}",
122
+ dm: "${args.dm || false}",
123
+ created_at: "${now()}"
124
+ },
125
+ as: "reminder"
126
+ },
127
+ {
128
+ action: "reply",
129
+ embed: {
130
+ title: "Reminder Set",
131
+ description: 'I\'ll remind you ${timestamp(remindAt, "R")}: ${args.message}',
132
+ color: "#57f287",
133
+ footer: { text: "ID: ${reminder.id}" }
134
+ },
135
+ ephemeral: true
136
+ }
137
+ ]
138
+ },
139
+ {
140
+ name: "reminders",
141
+ description: "View your reminders",
142
+ actions: [
143
+ {
144
+ action: "db_query",
145
+ table: "reminders",
146
+ where: { user_id: "${user.id}" },
147
+ order_by: "remind_at ASC",
148
+ as: "reminders"
149
+ },
150
+ {
151
+ action: "flow_if",
152
+ condition: "${reminders.length === 0}",
153
+ then: [
154
+ { action: "reply", content: "You have no reminders!", ephemeral: true },
155
+ { action: "abort" }
156
+ ]
157
+ },
158
+ {
159
+ action: "set",
160
+ key: "reminderList",
161
+ value: '${reminders.map((r, i) => "**" + (i + 1) + ".** " + truncate(r.message, 50) + " - " + timestamp(r.remind_at, "R") + " (ID: " + r.id + ")").join("\\n")}'
162
+ },
163
+ {
164
+ action: "reply",
165
+ embed: {
166
+ title: "Your Reminders",
167
+ description: "${reminderList}",
168
+ color: "#5865f2",
169
+ footer: { text: "${reminders.length} reminder(s)" }
170
+ },
171
+ ephemeral: true
172
+ }
173
+ ]
174
+ },
175
+ {
176
+ name: "delreminder",
177
+ description: "Delete a reminder",
178
+ options: [
179
+ { name: "id", description: "Reminder ID", type: "integer", required: true }
180
+ ],
181
+ actions: [
182
+ {
183
+ action: "db_query",
184
+ table: "reminders",
185
+ where: { id: "${args.id}", user_id: "${user.id}" },
186
+ as: "reminder"
187
+ },
188
+ {
189
+ action: "flow_if",
190
+ condition: "${reminder.length === 0}",
191
+ then: [
192
+ { action: "reply", content: "Reminder not found or not yours!", ephemeral: true },
193
+ { action: "abort" }
194
+ ]
195
+ },
196
+ {
197
+ action: "db_delete",
198
+ table: "reminders",
199
+ where: { id: "${args.id}" }
200
+ },
201
+ {
202
+ action: "reply",
203
+ content: "Reminder deleted!",
204
+ ephemeral: true
205
+ }
206
+ ]
207
+ }
208
+ ];
209
+ function getRemindersSpec(config = {}) {
210
+ return {
211
+ commands: remindersCommands,
212
+ events: remindersEventHandlers,
213
+ state: {
214
+ tables: remindersTables
215
+ }
216
+ };
217
+ }
218
+ export {
219
+ getRemindersSpec,
220
+ remindersCommands,
221
+ remindersEventHandlers,
222
+ remindersTables
223
+ };
224
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/reminders/index.ts"],"sourcesContent":["/**\n * Reminders builtin module\n * Handles scheduled reminders with DM delivery\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';\n\nexport interface RemindersConfig {\n /** Maximum reminders per user */\n maxRemindersPerUser?: number;\n /** Minimum reminder duration */\n minDuration?: string;\n /** Maximum reminder duration */\n maxDuration?: string;\n /** Allow DM reminders */\n allowDM?: boolean;\n}\n\nexport const remindersTables: Record<string, TableDefinition> = {\n reminders: {\n columns: {\n id: { type: 'number', primary: true },\n guild_id: { type: 'string', index: true },\n channel_id: { type: 'string' },\n user_id: { type: 'string', index: true },\n message: { type: 'string' },\n remind_at: { type: 'timestamp', index: true },\n dm: { type: 'boolean', default: false },\n created_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const remindersEventHandlers: EventHandler[] = [\n // Check reminders on scheduler tick\n {\n event: 'scheduler_tick',\n actions: [\n {\n action: 'db_query',\n table: 'reminders',\n where: {},\n as: 'allReminders',\n },\n {\n action: 'set',\n key: 'dueReminders',\n value: '${allReminders.filter(r => new Date(r.remind_at) <= now())}',\n },\n {\n action: 'batch',\n items: '${dueReminders}',\n each: { action: 'emit', event: 'reminder_due', data: { reminder: '${item}' } },\n },\n ],\n },\n // Handle due reminder\n {\n event: 'reminder_due',\n actions: [\n {\n action: 'set',\n key: 'reminder',\n value: '${event.data.reminder}',\n },\n // Delete the reminder\n {\n action: 'db_delete',\n table: 'reminders',\n where: { id: '${reminder.id}' },\n },\n // Send reminder\n {\n action: 'flow_if',\n condition: '${reminder.dm}',\n then: [\n {\n action: 'send_dm',\n user: '${reminder.user_id}',\n embed: {\n title: 'Reminder',\n description: '${reminder.message}',\n color: '#5865f2',\n footer: { text: 'Set ${timestamp(reminder.created_at, \"R\")}' },\n },\n },\n ],\n else: [\n {\n action: 'send_message',\n channel: '${reminder.channel_id}',\n content: '<@${reminder.user_id}> Reminder: ${reminder.message}',\n },\n ],\n },\n ],\n },\n];\n\nexport const remindersCommands: CommandDefinition[] = [\n {\n name: 'remind',\n description: 'Set a reminder',\n options: [\n { name: 'time', description: 'When to remind (e.g., 10m, 2h, 1d)', type: 'string', required: true },\n { name: 'message', description: 'Reminder message', type: 'string', required: true },\n { name: 'dm', description: 'Send reminder via DM', type: 'boolean', required: false },\n ],\n actions: [\n // Check max reminders\n {\n action: 'db_query',\n table: 'reminders',\n where: { user_id: '${user.id}' },\n as: 'userReminders',\n },\n {\n action: 'flow_if',\n condition: '${userReminders.length >= (config.reminders?.maxRemindersPerUser || 25)}',\n then: [\n { action: 'reply', content: 'You have too many reminders! Delete some first.', ephemeral: true },\n { action: 'abort' },\n ],\n },\n // Calculate remind time\n {\n action: 'set',\n key: 'remindAt',\n value: '${addDuration(now(), args.time)}',\n },\n // Create reminder\n {\n action: 'db_insert',\n table: 'reminders',\n data: {\n guild_id: '${guild.id}',\n channel_id: '${channel.id}',\n user_id: '${user.id}',\n message: '${args.message}',\n remind_at: '${remindAt}',\n dm: '${args.dm || false}',\n created_at: '${now()}',\n },\n as: 'reminder',\n },\n {\n action: 'reply',\n embed: {\n title: 'Reminder Set',\n description: 'I\\'ll remind you ${timestamp(remindAt, \"R\")}: ${args.message}',\n color: '#57f287',\n footer: { text: 'ID: ${reminder.id}' },\n },\n ephemeral: true,\n },\n ],\n },\n {\n name: 'reminders',\n description: 'View your reminders',\n actions: [\n {\n action: 'db_query',\n table: 'reminders',\n where: { user_id: '${user.id}' },\n order_by: 'remind_at ASC',\n as: 'reminders',\n },\n {\n action: 'flow_if',\n condition: '${reminders.length === 0}',\n then: [\n { action: 'reply', content: 'You have no reminders!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'set',\n key: 'reminderList',\n value: '${reminders.map((r, i) => \"**\" + (i + 1) + \".** \" + truncate(r.message, 50) + \" - \" + timestamp(r.remind_at, \"R\") + \" (ID: \" + r.id + \")\").join(\"\\\\n\")}',\n },\n {\n action: 'reply',\n embed: {\n title: 'Your Reminders',\n description: '${reminderList}',\n color: '#5865f2',\n footer: { text: '${reminders.length} reminder(s)' },\n },\n ephemeral: true,\n },\n ],\n },\n {\n name: 'delreminder',\n description: 'Delete a reminder',\n options: [\n { name: 'id', description: 'Reminder ID', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'reminders',\n where: { id: '${args.id}', user_id: '${user.id}' },\n as: 'reminder',\n },\n {\n action: 'flow_if',\n condition: '${reminder.length === 0}',\n then: [\n { action: 'reply', content: 'Reminder not found or not yours!', ephemeral: true },\n { action: 'abort' },\n ],\n },\n {\n action: 'db_delete',\n table: 'reminders',\n where: { id: '${args.id}' },\n },\n {\n action: 'reply',\n content: 'Reminder deleted!',\n ephemeral: true,\n },\n ],\n },\n];\n\nexport function getRemindersSpec(config: RemindersConfig = {}): Partial<FurlowSpec> {\n return {\n commands: remindersCommands,\n events: remindersEventHandlers,\n state: {\n tables: remindersTables,\n },\n };\n}\n"],"mappings":";AAkBO,IAAM,kBAAmD;AAAA,EAC9D,WAAW;AAAA,IACT,SAAS;AAAA,MACP,IAAI,EAAE,MAAM,UAAU,SAAS,KAAK;AAAA,MACpC,UAAU,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MACxC,YAAY,EAAE,MAAM,SAAS;AAAA,MAC7B,SAAS,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MACvC,SAAS,EAAE,MAAM,SAAS;AAAA,MAC1B,WAAW,EAAE,MAAM,aAAa,OAAO,KAAK;AAAA,MAC5C,IAAI,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACtC,YAAY,EAAE,MAAM,YAAY;AAAA,IAClC;AAAA,EACF;AACF;AAEO,IAAM,yBAAyC;AAAA;AAAA,EAEpD;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,CAAC;AAAA,QACR,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM,EAAE,QAAQ,QAAQ,OAAO,gBAAgB,MAAM,EAAE,UAAU,UAAU,EAAE;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,IAAI,iBAAiB;AAAA,MAChC;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ;AAAA,YACE,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,OAAO;AAAA,cACP,QAAQ,EAAE,MAAM,6CAA6C;AAAA,YAC/D;AAAA,UACF;AAAA,QACF;AAAA,QACA,MAAM;AAAA,UACJ;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,oBAAyC;AAAA,EACpD;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,QAAQ,aAAa,sCAAsC,MAAM,UAAU,UAAU,KAAK;AAAA,MAClG,EAAE,MAAM,WAAW,aAAa,oBAAoB,MAAM,UAAU,UAAU,KAAK;AAAA,MACnF,EAAE,MAAM,MAAM,aAAa,wBAAwB,MAAM,WAAW,UAAU,MAAM;AAAA,IACtF;AAAA,IACA,SAAS;AAAA;AAAA,MAEP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,SAAS,aAAa;AAAA,QAC/B,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ,EAAE,QAAQ,SAAS,SAAS,mDAAmD,WAAW,KAAK;AAAA,UAC/F,EAAE,QAAQ,QAAQ;AAAA,QACpB;AAAA,MACF;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,SAAS;AAAA,UACT,SAAS;AAAA,UACT,WAAW;AAAA,UACX,IAAI;AAAA,UACJ,YAAY;AAAA,QACd;AAAA,QACA,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,UACP,QAAQ,EAAE,MAAM,qBAAqB;AAAA,QACvC;AAAA,QACA,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,SAAS,aAAa;AAAA,QAC/B,UAAU;AAAA,QACV,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ,EAAE,QAAQ,SAAS,SAAS,0BAA0B,WAAW,KAAK;AAAA,UACtE,EAAE,QAAQ,QAAQ;AAAA,QACpB;AAAA,MACF;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,UACP,QAAQ,EAAE,MAAM,kCAAkC;AAAA,QACpD;AAAA,QACA,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,MAAM,aAAa,eAAe,MAAM,WAAW,UAAU,KAAK;AAAA,IAC5E;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,IAAI,cAAc,SAAS,aAAa;AAAA,QACjD,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ,EAAE,QAAQ,SAAS,SAAS,oCAAoC,WAAW,KAAK;AAAA,UAChF,EAAE,QAAQ,QAAQ;AAAA,QACpB;AAAA,MACF;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO,EAAE,IAAI,aAAa;AAAA,MAC5B;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,iBAAiB,SAA0B,CAAC,GAAwB;AAClF,SAAO;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,QAAQ;AAAA,IACV;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,35 @@
1
+ import { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';
2
+
3
+ /**
4
+ * Starboard builtin module
5
+ * Handles star reactions and hall of fame
6
+ */
7
+
8
+ interface StarboardConfig {
9
+ /** Starboard channel */
10
+ channel?: string;
11
+ /** Minimum stars required */
12
+ threshold?: number;
13
+ /** Emoji to use (default: star) */
14
+ emoji?: string;
15
+ /** Self-starring allowed */
16
+ selfStar?: boolean;
17
+ /** Bot messages can be starred */
18
+ botMessages?: boolean;
19
+ /** Ignored channels */
20
+ ignoredChannels?: string[];
21
+ /** NSFW channel handling */
22
+ nsfwAllowed?: boolean;
23
+ /** Star tiers with different thresholds */
24
+ tiers?: Array<{
25
+ threshold: number;
26
+ emoji: string;
27
+ color?: string;
28
+ }>;
29
+ }
30
+ declare const starboardTables: Record<string, TableDefinition>;
31
+ declare const starboardEventHandlers: EventHandler[];
32
+ declare const starboardCommands: CommandDefinition[];
33
+ declare function getStarboardSpec(config?: StarboardConfig): Partial<FurlowSpec>;
34
+
35
+ export { type StarboardConfig, getStarboardSpec, starboardCommands, starboardEventHandlers, starboardTables };
@@ -0,0 +1,401 @@
1
+ // src/starboard/index.ts
2
+ var starboardTables = {
3
+ starboard_messages: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ guild_id: { type: "string", index: true },
7
+ source_channel_id: { type: "string" },
8
+ source_message_id: { type: "string", unique: true },
9
+ starboard_message_id: { type: "string" },
10
+ author_id: { type: "string", index: true },
11
+ star_count: { type: "number", default: 0 },
12
+ created_at: { type: "timestamp" }
13
+ }
14
+ },
15
+ starboard_stars: {
16
+ columns: {
17
+ id: { type: "number", primary: true },
18
+ message_id: { type: "string", index: true },
19
+ user_id: { type: "string" },
20
+ created_at: { type: "timestamp" }
21
+ }
22
+ }
23
+ };
24
+ var starboardEventHandlers = [
25
+ // Handle star reactions
26
+ {
27
+ event: "reaction_add",
28
+ condition: '${(reaction.emoji.name === (config.starboard?.emoji || "\u2B50") || reaction.emoji.name === "\u{1F31F}") && config.starboard?.channel}',
29
+ actions: [
30
+ // Check if channel is ignored
31
+ {
32
+ action: "flow_if",
33
+ condition: "${config.starboard.ignoredChannels?.includes(channel.id)}",
34
+ then: [{ action: "abort" }]
35
+ },
36
+ // Check NSFW
37
+ {
38
+ action: "flow_if",
39
+ condition: "${channel.nsfw && !config.starboard.nsfwAllowed}",
40
+ then: [{ action: "abort" }]
41
+ },
42
+ // Check self-star
43
+ {
44
+ action: "flow_if",
45
+ condition: "${!config.starboard.selfStar && user.id === message.author.id}",
46
+ then: [{ action: "abort" }]
47
+ },
48
+ // Check bot messages
49
+ {
50
+ action: "flow_if",
51
+ condition: "${!config.starboard.botMessages && message.author.bot}",
52
+ then: [{ action: "abort" }]
53
+ },
54
+ // Check if already starred by this user
55
+ {
56
+ action: "db_query",
57
+ table: "starboard_stars",
58
+ where: { message_id: "${message.id}", user_id: "${user.id}" },
59
+ as: "existingStar"
60
+ },
61
+ {
62
+ action: "flow_if",
63
+ condition: "${existingStar.length > 0}",
64
+ then: [{ action: "abort" }]
65
+ },
66
+ // Add star
67
+ {
68
+ action: "db_insert",
69
+ table: "starboard_stars",
70
+ data: {
71
+ message_id: "${message.id}",
72
+ user_id: "${user.id}",
73
+ created_at: "${now()}"
74
+ }
75
+ },
76
+ // Count stars
77
+ {
78
+ action: "db_query",
79
+ table: "starboard_stars",
80
+ where: { message_id: "${message.id}" },
81
+ as: "allStars"
82
+ },
83
+ {
84
+ action: "set",
85
+ key: "starCount",
86
+ value: "${allStars.length}"
87
+ },
88
+ // Get threshold
89
+ {
90
+ action: "set",
91
+ key: "threshold",
92
+ value: "${config.starboard.threshold || 3}"
93
+ },
94
+ // Get appropriate tier
95
+ {
96
+ action: "set",
97
+ key: "tier",
98
+ value: "${config.starboard.tiers ? config.starboard.tiers.filter(t => starCount >= t.threshold).pop() : null}"
99
+ },
100
+ {
101
+ action: "set",
102
+ key: "displayEmoji",
103
+ value: '${tier?.emoji || config.starboard.emoji || "\u2B50"}'
104
+ },
105
+ {
106
+ action: "set",
107
+ key: "embedColor",
108
+ value: '${tier?.color || "#ffd700"}'
109
+ },
110
+ // Check if already on starboard
111
+ {
112
+ action: "db_query",
113
+ table: "starboard_messages",
114
+ where: { source_message_id: "${message.id}" },
115
+ as: "existing"
116
+ },
117
+ {
118
+ action: "flow_if",
119
+ condition: "${existing.length > 0}",
120
+ then: [
121
+ // Update existing starboard message
122
+ {
123
+ action: "db_update",
124
+ table: "starboard_messages",
125
+ where: { source_message_id: "${message.id}" },
126
+ data: { star_count: "${starCount}" }
127
+ },
128
+ {
129
+ action: "edit_message",
130
+ channel: "${config.starboard.channel}",
131
+ message: "${existing[0].starboard_message_id}",
132
+ content: "${displayEmoji} **${starCount}** | <#${channel.id}>"
133
+ }
134
+ ],
135
+ else: [
136
+ // Create new starboard entry if threshold met
137
+ {
138
+ action: "flow_if",
139
+ condition: "${starCount >= threshold}",
140
+ then: [
141
+ // Build embed
142
+ {
143
+ action: "set",
144
+ key: "imageUrl",
145
+ value: "${message.attachments.first()?.url || message.embeds[0]?.image?.url || message.embeds[0]?.thumbnail?.url}"
146
+ },
147
+ {
148
+ action: "send_message",
149
+ channel: "${config.starboard.channel}",
150
+ content: "${displayEmoji} **${starCount}** | <#${channel.id}>",
151
+ embed: {
152
+ author: {
153
+ name: "${message.author.tag}",
154
+ icon_url: "${message.author.avatarURL}"
155
+ },
156
+ description: "${message.content}\n\n[Jump to message](${message.url})",
157
+ color: "${embedColor}",
158
+ image: "${imageUrl ? { url: imageUrl } : null}",
159
+ timestamp: "${message.createdAt}",
160
+ footer: {
161
+ text: "ID: ${message.id}"
162
+ }
163
+ },
164
+ as: "starboardMessage"
165
+ },
166
+ {
167
+ action: "db_insert",
168
+ table: "starboard_messages",
169
+ data: {
170
+ guild_id: "${guild.id}",
171
+ source_channel_id: "${channel.id}",
172
+ source_message_id: "${message.id}",
173
+ starboard_message_id: "${starboardMessage.id}",
174
+ author_id: "${message.author.id}",
175
+ star_count: "${starCount}",
176
+ created_at: "${now()}"
177
+ }
178
+ }
179
+ ]
180
+ }
181
+ ]
182
+ }
183
+ ]
184
+ },
185
+ // Handle star removal
186
+ {
187
+ event: "reaction_remove",
188
+ condition: '${(reaction.emoji.name === (config.starboard?.emoji || "\u2B50") || reaction.emoji.name === "\u{1F31F}") && config.starboard?.channel}',
189
+ actions: [
190
+ // Remove star record
191
+ {
192
+ action: "db_delete",
193
+ table: "starboard_stars",
194
+ where: { message_id: "${message.id}", user_id: "${user.id}" }
195
+ },
196
+ // Count remaining stars
197
+ {
198
+ action: "db_query",
199
+ table: "starboard_stars",
200
+ where: { message_id: "${message.id}" },
201
+ as: "allStars"
202
+ },
203
+ {
204
+ action: "set",
205
+ key: "starCount",
206
+ value: "${allStars.length}"
207
+ },
208
+ // Check if on starboard
209
+ {
210
+ action: "db_query",
211
+ table: "starboard_messages",
212
+ where: { source_message_id: "${message.id}" },
213
+ as: "existing"
214
+ },
215
+ {
216
+ action: "flow_if",
217
+ condition: "${existing.length > 0}",
218
+ then: [
219
+ {
220
+ action: "flow_if",
221
+ condition: "${starCount < (config.starboard.threshold || 3)}",
222
+ then: [
223
+ // Remove from starboard
224
+ {
225
+ action: "delete_message",
226
+ channel: "${config.starboard.channel}",
227
+ message: "${existing[0].starboard_message_id}"
228
+ },
229
+ {
230
+ action: "db_delete",
231
+ table: "starboard_messages",
232
+ where: { source_message_id: "${message.id}" }
233
+ }
234
+ ],
235
+ else: [
236
+ // Update count
237
+ {
238
+ action: "set",
239
+ key: "tier",
240
+ value: "${config.starboard.tiers ? config.starboard.tiers.filter(t => starCount >= t.threshold).pop() : null}"
241
+ },
242
+ {
243
+ action: "set",
244
+ key: "displayEmoji",
245
+ value: '${tier?.emoji || config.starboard.emoji || "\u2B50"}'
246
+ },
247
+ {
248
+ action: "db_update",
249
+ table: "starboard_messages",
250
+ where: { source_message_id: "${message.id}" },
251
+ data: { star_count: "${starCount}" }
252
+ },
253
+ {
254
+ action: "edit_message",
255
+ channel: "${config.starboard.channel}",
256
+ message: "${existing[0].starboard_message_id}",
257
+ content: "${displayEmoji} **${starCount}** | <#${channel.id}>"
258
+ }
259
+ ]
260
+ }
261
+ ]
262
+ }
263
+ ]
264
+ }
265
+ ];
266
+ var starboardCommands = [
267
+ {
268
+ name: "starboard",
269
+ description: "Starboard commands",
270
+ subcommands: [
271
+ {
272
+ name: "setup",
273
+ description: "Set up the starboard",
274
+ options: [
275
+ { name: "channel", description: "Starboard channel", type: "channel", required: true },
276
+ { name: "threshold", description: "Minimum stars required", type: "integer", required: false },
277
+ { name: "emoji", description: "Star emoji", type: "string", required: false }
278
+ ],
279
+ actions: [
280
+ {
281
+ action: "set",
282
+ key: "config.starboard.channel",
283
+ value: "${args.channel.id}",
284
+ scope: "guild"
285
+ },
286
+ {
287
+ action: "flow_if",
288
+ condition: "${args.threshold}",
289
+ then: [
290
+ {
291
+ action: "set",
292
+ key: "config.starboard.threshold",
293
+ value: "${args.threshold}",
294
+ scope: "guild"
295
+ }
296
+ ]
297
+ },
298
+ {
299
+ action: "flow_if",
300
+ condition: "${args.emoji}",
301
+ then: [
302
+ {
303
+ action: "set",
304
+ key: "config.starboard.emoji",
305
+ value: "${args.emoji}",
306
+ scope: "guild"
307
+ }
308
+ ]
309
+ },
310
+ {
311
+ action: "reply",
312
+ content: "Starboard set up in ${args.channel}!",
313
+ ephemeral: true
314
+ }
315
+ ]
316
+ },
317
+ {
318
+ name: "stats",
319
+ description: "View starboard statistics",
320
+ actions: [
321
+ {
322
+ action: "db_query",
323
+ table: "starboard_messages",
324
+ where: { guild_id: "${guild.id}" },
325
+ order_by: "star_count DESC",
326
+ limit: 5,
327
+ as: "topMessages"
328
+ },
329
+ {
330
+ action: "db_query",
331
+ table: "starboard_messages",
332
+ where: { guild_id: "${guild.id}" },
333
+ as: "allMessages"
334
+ },
335
+ {
336
+ action: "set",
337
+ key: "totalStars",
338
+ value: "${allMessages.reduce((sum, m) => sum + m.star_count, 0)}"
339
+ },
340
+ {
341
+ action: "reply",
342
+ embed: {
343
+ title: "Starboard Statistics",
344
+ color: "#ffd700",
345
+ fields: [
346
+ { name: "Total Starred Messages", value: "${allMessages.length}", inline: true },
347
+ { name: "Total Stars", value: "${totalStars}", inline: true },
348
+ { name: "Top Starred Messages", value: '${topMessages.map((m, i) => (i + 1) + ". " + m.star_count + " stars - <@" + m.author_id + ">").join("\\n") || "None yet!"}' }
349
+ ]
350
+ }
351
+ }
352
+ ]
353
+ },
354
+ {
355
+ name: "random",
356
+ description: "Get a random starred message",
357
+ actions: [
358
+ {
359
+ action: "db_query",
360
+ table: "starboard_messages",
361
+ where: { guild_id: "${guild.id}" },
362
+ as: "allMessages"
363
+ },
364
+ {
365
+ action: "flow_if",
366
+ condition: "${allMessages.length === 0}",
367
+ then: [
368
+ { action: "reply", content: "No starred messages yet!", ephemeral: true },
369
+ { action: "abort" }
370
+ ]
371
+ },
372
+ {
373
+ action: "set",
374
+ key: "randomMessage",
375
+ value: "${allMessages[floor(random(0, allMessages.length))]}"
376
+ },
377
+ {
378
+ action: "reply",
379
+ content: "\u2B50 **${randomMessage.star_count}** - https://discord.com/channels/${guild.id}/${randomMessage.source_channel_id}/${randomMessage.source_message_id}"
380
+ }
381
+ ]
382
+ }
383
+ ]
384
+ }
385
+ ];
386
+ function getStarboardSpec(config = {}) {
387
+ return {
388
+ events: starboardEventHandlers,
389
+ commands: starboardCommands,
390
+ state: {
391
+ tables: starboardTables
392
+ }
393
+ };
394
+ }
395
+ export {
396
+ getStarboardSpec,
397
+ starboardCommands,
398
+ starboardEventHandlers,
399
+ starboardTables
400
+ };
401
+ //# sourceMappingURL=index.js.map