@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,221 @@
1
+ // src/moderation/index.ts
2
+ var moderationCommands = [
3
+ {
4
+ name: "warn",
5
+ description: "Warn a user",
6
+ options: [
7
+ { name: "user", description: "User to warn", type: "user", required: true },
8
+ { name: "reason", description: "Reason for warning", type: "string", required: true }
9
+ ],
10
+ actions: [
11
+ {
12
+ action: "db_insert",
13
+ table: "warnings",
14
+ data: {
15
+ user_id: "${args.user.id}",
16
+ guild_id: "${guild.id}",
17
+ moderator_id: "${user.id}",
18
+ reason: "${args.reason}",
19
+ created_at: "${now()}"
20
+ }
21
+ },
22
+ {
23
+ action: "reply",
24
+ content: "Warned ${args.user.username} for: ${args.reason}",
25
+ ephemeral: true
26
+ },
27
+ {
28
+ action: "send_dm",
29
+ user: "${args.user.id}",
30
+ embed: {
31
+ title: "Warning Received",
32
+ description: "You have been warned in ${guild.name}",
33
+ fields: [
34
+ { name: "Reason", value: "${args.reason}" },
35
+ { name: "Moderator", value: "${user.username}" }
36
+ ],
37
+ color: "#ffa500"
38
+ }
39
+ }
40
+ ]
41
+ },
42
+ {
43
+ name: "kick",
44
+ description: "Kick a user from the server",
45
+ options: [
46
+ { name: "user", description: "User to kick", type: "user", required: true },
47
+ { name: "reason", description: "Reason for kick", type: "string", required: false }
48
+ ],
49
+ actions: [
50
+ {
51
+ action: "kick",
52
+ user: "${args.user.id}",
53
+ reason: '${args.reason || "No reason provided"}',
54
+ dm_user: true,
55
+ dm_message: 'You have been kicked from ${guild.name}. Reason: ${args.reason || "No reason provided"}'
56
+ },
57
+ {
58
+ action: "reply",
59
+ content: "Kicked ${args.user.username}",
60
+ ephemeral: true
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ name: "ban",
66
+ description: "Ban a user from the server",
67
+ options: [
68
+ { name: "user", description: "User to ban", type: "user", required: true },
69
+ { name: "reason", description: "Reason for ban", type: "string", required: false },
70
+ { name: "delete_days", description: "Days of messages to delete (0-7)", type: "integer", required: false }
71
+ ],
72
+ actions: [
73
+ {
74
+ action: "ban",
75
+ user: "${args.user.id}",
76
+ reason: '${args.reason || "No reason provided"}',
77
+ delete_message_days: "${args.delete_days || 0}",
78
+ dm_user: true,
79
+ dm_message: 'You have been banned from ${guild.name}. Reason: ${args.reason || "No reason provided"}'
80
+ },
81
+ {
82
+ action: "reply",
83
+ content: "Banned ${args.user.username}",
84
+ ephemeral: true
85
+ }
86
+ ]
87
+ },
88
+ {
89
+ name: "unban",
90
+ description: "Unban a user",
91
+ options: [
92
+ { name: "user_id", description: "User ID to unban", type: "string", required: true },
93
+ { name: "reason", description: "Reason for unban", type: "string", required: false }
94
+ ],
95
+ actions: [
96
+ {
97
+ action: "unban",
98
+ user: "${args.user_id}",
99
+ reason: '${args.reason || "No reason provided"}'
100
+ },
101
+ {
102
+ action: "reply",
103
+ content: "Unbanned user ${args.user_id}",
104
+ ephemeral: true
105
+ }
106
+ ]
107
+ },
108
+ {
109
+ name: "timeout",
110
+ description: "Timeout a user",
111
+ options: [
112
+ { name: "user", description: "User to timeout", type: "user", required: true },
113
+ { name: "duration", description: "Duration (e.g., 10m, 1h, 1d)", type: "string", required: true },
114
+ { name: "reason", description: "Reason for timeout", type: "string", required: false }
115
+ ],
116
+ actions: [
117
+ {
118
+ action: "timeout",
119
+ user: "${args.user.id}",
120
+ duration: "${args.duration}",
121
+ reason: '${args.reason || "No reason provided"}',
122
+ dm_user: true,
123
+ dm_message: 'You have been timed out in ${guild.name} for ${args.duration}. Reason: ${args.reason || "No reason provided"}'
124
+ },
125
+ {
126
+ action: "reply",
127
+ content: "Timed out ${args.user.username} for ${args.duration}",
128
+ ephemeral: true
129
+ }
130
+ ]
131
+ },
132
+ {
133
+ name: "warnings",
134
+ description: "View warnings for a user",
135
+ options: [
136
+ { name: "user", description: "User to check", type: "user", required: true }
137
+ ],
138
+ actions: [
139
+ {
140
+ action: "db_query",
141
+ table: "warnings",
142
+ where: {
143
+ user_id: "${args.user.id}",
144
+ guild_id: "${guild.id}"
145
+ },
146
+ order_by: "created_at DESC",
147
+ limit: 10,
148
+ as: "warnings"
149
+ },
150
+ {
151
+ action: "reply",
152
+ embed: {
153
+ title: "Warnings for ${args.user.username}",
154
+ description: '${warnings.length == 0 ? "No warnings found" : warnings | map("- " + .reason) | join("\\n")}',
155
+ color: '${warnings.length > 0 ? "#ffa500" : "#00ff00"}',
156
+ footer: {
157
+ text: "Total: ${warnings.length} warnings"
158
+ }
159
+ }
160
+ }
161
+ ]
162
+ },
163
+ {
164
+ name: "purge",
165
+ description: "Delete multiple messages",
166
+ options: [
167
+ { name: "count", description: "Number of messages to delete (1-100)", type: "integer", required: true },
168
+ { name: "user", description: "Only delete messages from this user", type: "user", required: false }
169
+ ],
170
+ actions: [
171
+ {
172
+ action: "bulk_delete",
173
+ channel: "${channel.id}",
174
+ count: "${args.count}",
175
+ filter: '${args.user ? "message.author.id == \\"" + args.user.id + "\\"" : null}'
176
+ },
177
+ {
178
+ action: "reply",
179
+ content: "Deleted messages",
180
+ ephemeral: true
181
+ }
182
+ ]
183
+ }
184
+ ];
185
+ var moderationTables = {
186
+ warnings: {
187
+ columns: {
188
+ id: { type: "number", primary: true },
189
+ user_id: { type: "string", index: true },
190
+ guild_id: { type: "string", index: true },
191
+ moderator_id: { type: "string" },
192
+ reason: { type: "string" },
193
+ created_at: { type: "timestamp" }
194
+ }
195
+ },
196
+ mod_cases: {
197
+ columns: {
198
+ id: { type: "number", primary: true },
199
+ guild_id: { type: "string", index: true },
200
+ user_id: { type: "string", index: true },
201
+ moderator_id: { type: "string" },
202
+ action: { type: "string" },
203
+ reason: { type: "string" },
204
+ created_at: { type: "timestamp" }
205
+ }
206
+ }
207
+ };
208
+ function getModerationSpec(config = {}) {
209
+ return {
210
+ commands: moderationCommands,
211
+ state: {
212
+ tables: moderationTables
213
+ }
214
+ };
215
+ }
216
+ export {
217
+ getModerationSpec,
218
+ moderationCommands,
219
+ moderationTables
220
+ };
221
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/moderation/index.ts"],"sourcesContent":["/**\n * Moderation builtin module\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler } from '@furlow/schema';\n\nexport interface ModerationConfig {\n /** Log channel for moderation actions */\n logChannel?: string;\n /** Whether to DM users on moderation actions */\n dmOnAction?: boolean;\n /** Roles exempt from moderation */\n exemptRoles?: string[];\n /** Enable warning system */\n warnings?: {\n enabled?: boolean;\n maxWarnings?: number;\n escalation?: Array<{\n count: number;\n action: 'kick' | 'ban' | 'timeout';\n duration?: string;\n }>;\n };\n}\n\nexport const moderationCommands: CommandDefinition[] = [\n {\n name: 'warn',\n description: 'Warn a user',\n options: [\n { name: 'user', description: 'User to warn', type: 'user', required: true },\n { name: 'reason', description: 'Reason for warning', type: 'string', required: true },\n ],\n actions: [\n {\n action: 'db_insert',\n table: 'warnings',\n data: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n moderator_id: '${user.id}',\n reason: '${args.reason}',\n created_at: '${now()}',\n },\n },\n {\n action: 'reply',\n content: 'Warned ${args.user.username} for: ${args.reason}',\n ephemeral: true,\n },\n {\n action: 'send_dm',\n user: '${args.user.id}',\n embed: {\n title: 'Warning Received',\n description: 'You have been warned in ${guild.name}',\n fields: [\n { name: 'Reason', value: '${args.reason}' },\n { name: 'Moderator', value: '${user.username}' },\n ],\n color: '#ffa500',\n },\n },\n ],\n },\n {\n name: 'kick',\n description: 'Kick a user from the server',\n options: [\n { name: 'user', description: 'User to kick', type: 'user', required: true },\n { name: 'reason', description: 'Reason for kick', type: 'string', required: false },\n ],\n actions: [\n {\n action: 'kick',\n user: '${args.user.id}',\n reason: '${args.reason || \"No reason provided\"}',\n dm_user: true,\n dm_message: 'You have been kicked from ${guild.name}. Reason: ${args.reason || \"No reason provided\"}',\n },\n {\n action: 'reply',\n content: 'Kicked ${args.user.username}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'ban',\n description: 'Ban a user from the server',\n options: [\n { name: 'user', description: 'User to ban', type: 'user', required: true },\n { name: 'reason', description: 'Reason for ban', type: 'string', required: false },\n { name: 'delete_days', description: 'Days of messages to delete (0-7)', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'ban',\n user: '${args.user.id}',\n reason: '${args.reason || \"No reason provided\"}',\n delete_message_days: '${args.delete_days || 0}' as unknown as number,\n dm_user: true,\n dm_message: 'You have been banned from ${guild.name}. Reason: ${args.reason || \"No reason provided\"}',\n },\n {\n action: 'reply',\n content: 'Banned ${args.user.username}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'unban',\n description: 'Unban a user',\n options: [\n { name: 'user_id', description: 'User ID to unban', type: 'string', required: true },\n { name: 'reason', description: 'Reason for unban', type: 'string', required: false },\n ],\n actions: [\n {\n action: 'unban',\n user: '${args.user_id}',\n reason: '${args.reason || \"No reason provided\"}',\n },\n {\n action: 'reply',\n content: 'Unbanned user ${args.user_id}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'timeout',\n description: 'Timeout a user',\n options: [\n { name: 'user', description: 'User to timeout', type: 'user', required: true },\n { name: 'duration', description: 'Duration (e.g., 10m, 1h, 1d)', type: 'string', required: true },\n { name: 'reason', description: 'Reason for timeout', type: 'string', required: false },\n ],\n actions: [\n {\n action: 'timeout',\n user: '${args.user.id}',\n duration: '${args.duration}',\n reason: '${args.reason || \"No reason provided\"}',\n dm_user: true,\n dm_message: 'You have been timed out in ${guild.name} for ${args.duration}. Reason: ${args.reason || \"No reason provided\"}',\n },\n {\n action: 'reply',\n content: 'Timed out ${args.user.username} for ${args.duration}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'warnings',\n description: 'View warnings for a user',\n options: [\n { name: 'user', description: 'User to check', type: 'user', required: true },\n ],\n actions: [\n {\n action: 'db_query',\n table: 'warnings',\n where: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n },\n order_by: 'created_at DESC',\n limit: 10,\n as: 'warnings',\n },\n {\n action: 'reply',\n embed: {\n title: 'Warnings for ${args.user.username}',\n description: '${warnings.length == 0 ? \"No warnings found\" : warnings | map(\"- \" + .reason) | join(\"\\\\n\")}',\n color: '${warnings.length > 0 ? \"#ffa500\" : \"#00ff00\"}',\n footer: {\n text: 'Total: ${warnings.length} warnings',\n },\n },\n },\n ],\n },\n {\n name: 'purge',\n description: 'Delete multiple messages',\n options: [\n { name: 'count', description: 'Number of messages to delete (1-100)', type: 'integer', required: true },\n { name: 'user', description: 'Only delete messages from this user', type: 'user', required: false },\n ],\n actions: [\n {\n action: 'bulk_delete',\n channel: '${channel.id}',\n count: '${args.count}' as unknown as number,\n filter: '${args.user ? \"message.author.id == \\\\\"\" + args.user.id + \"\\\\\"\" : null}',\n },\n {\n action: 'reply',\n content: 'Deleted messages',\n ephemeral: true,\n },\n ],\n },\n];\n\nexport const moderationTables = {\n warnings: {\n columns: {\n id: { type: 'number' as const, primary: true },\n user_id: { type: 'string' as const, index: true },\n guild_id: { type: 'string' as const, index: true },\n moderator_id: { type: 'string' as const },\n reason: { type: 'string' as const },\n created_at: { type: 'timestamp' as const },\n },\n },\n mod_cases: {\n columns: {\n id: { type: 'number' as const, primary: true },\n guild_id: { type: 'string' as const, index: true },\n user_id: { type: 'string' as const, index: true },\n moderator_id: { type: 'string' as const },\n action: { type: 'string' as const },\n reason: { type: 'string' as const },\n created_at: { type: 'timestamp' as const },\n },\n },\n};\n\nexport function getModerationSpec(config: ModerationConfig = {}): Partial<FurlowSpec> {\n return {\n commands: moderationCommands,\n state: {\n tables: moderationTables,\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,33 @@
1
+ import { FurlowSpec, CommandDefinition, EventHandler, TableDefinition } from '@furlow/schema';
2
+
3
+ /**
4
+ * Music builtin module
5
+ * Handles audio playback, queue management, and filters
6
+ */
7
+
8
+ interface MusicConfig {
9
+ /** Default volume (0-100) */
10
+ defaultVolume?: number;
11
+ /** Maximum queue size */
12
+ maxQueueSize?: number;
13
+ /** DJ role (required for some commands) */
14
+ djRole?: string;
15
+ /** Stay in channel when queue empty */
16
+ stayInChannel?: boolean;
17
+ /** Leave after idle (seconds) */
18
+ leaveAfterIdle?: number;
19
+ /** Allow seeking */
20
+ allowSeeking?: boolean;
21
+ /** Allow filters */
22
+ allowFilters?: boolean;
23
+ /** Show now playing messages */
24
+ announceNowPlaying?: boolean;
25
+ /** Announce channel */
26
+ announceChannel?: string;
27
+ }
28
+ declare const musicTables: Record<string, TableDefinition>;
29
+ declare const musicCommands: CommandDefinition[];
30
+ declare const musicEventHandlers: EventHandler[];
31
+ declare function getMusicSpec(config?: MusicConfig): Partial<FurlowSpec>;
32
+
33
+ export { type MusicConfig, getMusicSpec, musicCommands, musicEventHandlers, musicTables };
@@ -0,0 +1,414 @@
1
+ // src/music/index.ts
2
+ var musicTables = {
3
+ music_playlists: {
4
+ columns: {
5
+ id: { type: "number", primary: true },
6
+ guild_id: { type: "string", index: true },
7
+ user_id: { type: "string", index: true },
8
+ name: { type: "string" },
9
+ tracks: { type: "json" },
10
+ created_at: { type: "timestamp" }
11
+ }
12
+ }
13
+ };
14
+ var musicCommands = [
15
+ {
16
+ name: "play",
17
+ description: "Play a song or add to queue",
18
+ options: [
19
+ { name: "query", description: "Song URL or search query", type: "string", required: true }
20
+ ],
21
+ actions: [
22
+ // Check if user is in voice channel
23
+ {
24
+ action: "flow_if",
25
+ condition: "${!member.voice.channel}",
26
+ then: [
27
+ { action: "reply", content: "You must be in a voice channel!", ephemeral: true },
28
+ { action: "abort" }
29
+ ]
30
+ },
31
+ // Join voice channel if not already
32
+ {
33
+ action: "voice_join",
34
+ channel: "${member.voice.channel.id}"
35
+ },
36
+ // Search/resolve track
37
+ {
38
+ action: "voice_search",
39
+ query: "${args.query}",
40
+ as: "track"
41
+ },
42
+ {
43
+ action: "flow_if",
44
+ condition: "${!track}",
45
+ then: [
46
+ { action: "reply", content: "No results found for: ${args.query}", ephemeral: true },
47
+ { action: "abort" }
48
+ ]
49
+ },
50
+ // Add to queue
51
+ {
52
+ action: "queue_add",
53
+ track: "${track}",
54
+ requester: "${user.id}"
55
+ },
56
+ {
57
+ action: "reply",
58
+ embed: {
59
+ title: "Added to Queue",
60
+ description: "[${track.title}](${track.url})",
61
+ thumbnail: "${track.thumbnail}",
62
+ color: "#57f287",
63
+ fields: [
64
+ { name: "Duration", value: "${formatDuration(track.duration)}", inline: true },
65
+ { name: "Requested by", value: "${user}", inline: true }
66
+ ]
67
+ }
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ name: "skip",
73
+ description: "Skip the current song",
74
+ actions: [
75
+ {
76
+ action: "voice_skip"
77
+ },
78
+ {
79
+ action: "reply",
80
+ content: "Skipped the current song.",
81
+ ephemeral: true
82
+ }
83
+ ]
84
+ },
85
+ {
86
+ name: "stop",
87
+ description: "Stop playback and clear the queue",
88
+ actions: [
89
+ {
90
+ action: "voice_stop"
91
+ },
92
+ {
93
+ action: "queue_clear"
94
+ },
95
+ {
96
+ action: "reply",
97
+ content: "Stopped playback and cleared the queue.",
98
+ ephemeral: true
99
+ }
100
+ ]
101
+ },
102
+ {
103
+ name: "pause",
104
+ description: "Pause playback",
105
+ actions: [
106
+ {
107
+ action: "voice_pause"
108
+ },
109
+ {
110
+ action: "reply",
111
+ content: "Paused playback.",
112
+ ephemeral: true
113
+ }
114
+ ]
115
+ },
116
+ {
117
+ name: "resume",
118
+ description: "Resume playback",
119
+ actions: [
120
+ {
121
+ action: "voice_resume"
122
+ },
123
+ {
124
+ action: "reply",
125
+ content: "Resumed playback.",
126
+ ephemeral: true
127
+ }
128
+ ]
129
+ },
130
+ {
131
+ name: "queue",
132
+ description: "View the current queue",
133
+ options: [
134
+ { name: "page", description: "Page number", type: "integer", required: false }
135
+ ],
136
+ actions: [
137
+ {
138
+ action: "queue_get",
139
+ as: "queue"
140
+ },
141
+ {
142
+ action: "flow_if",
143
+ condition: "${queue.tracks.length === 0}",
144
+ then: [
145
+ { action: "reply", content: "The queue is empty!", ephemeral: true },
146
+ { action: "abort" }
147
+ ]
148
+ },
149
+ {
150
+ action: "set",
151
+ key: "page",
152
+ value: "${args.page || 1}"
153
+ },
154
+ {
155
+ action: "set",
156
+ key: "perPage",
157
+ value: 10
158
+ },
159
+ {
160
+ action: "set",
161
+ key: "start",
162
+ value: "${(page - 1) * perPage}"
163
+ },
164
+ {
165
+ action: "set",
166
+ key: "queueList",
167
+ value: '${queue.tracks.slice(start, start + perPage).map((t, i) => (start + i + 1) + ". [" + truncate(t.title, 40) + "](" + t.url + ") - " + formatDuration(t.duration)).join("\\n")}'
168
+ },
169
+ {
170
+ action: "reply",
171
+ embed: {
172
+ title: "Queue",
173
+ description: "**Now Playing:**\n[${queue.current.title}](${queue.current.url})\n\n**Up Next:**\n${queueList}",
174
+ color: "#5865f2",
175
+ footer: {
176
+ text: "Page ${page} | ${queue.tracks.length} tracks | Total: ${formatDuration(queue.totalDuration)}"
177
+ }
178
+ }
179
+ }
180
+ ]
181
+ },
182
+ {
183
+ name: "nowplaying",
184
+ description: "Show the currently playing song",
185
+ actions: [
186
+ {
187
+ action: "queue_get",
188
+ as: "queue"
189
+ },
190
+ {
191
+ action: "flow_if",
192
+ condition: "${!queue.current}",
193
+ then: [
194
+ { action: "reply", content: "Nothing is playing!", ephemeral: true },
195
+ { action: "abort" }
196
+ ]
197
+ },
198
+ {
199
+ action: "set",
200
+ key: "progress",
201
+ value: "${queue.position / queue.current.duration}"
202
+ },
203
+ {
204
+ action: "set",
205
+ key: "progressBar",
206
+ value: '${repeat("\u2593", floor(progress * 20)) + repeat("\u2591", 20 - floor(progress * 20))}'
207
+ },
208
+ {
209
+ action: "reply",
210
+ embed: {
211
+ title: "Now Playing",
212
+ description: "[${queue.current.title}](${queue.current.url})",
213
+ thumbnail: "${queue.current.thumbnail}",
214
+ color: "#5865f2",
215
+ fields: [
216
+ { name: "Progress", value: "${progressBar}\n${formatDuration(queue.position)} / ${formatDuration(queue.current.duration)}" },
217
+ { name: "Requested by", value: "<@${queue.current.requester}>", inline: true },
218
+ { name: "Volume", value: "${queue.volume}%", inline: true }
219
+ ]
220
+ }
221
+ }
222
+ ]
223
+ },
224
+ {
225
+ name: "volume",
226
+ description: "Set the volume",
227
+ options: [
228
+ { name: "level", description: "Volume level (0-100)", type: "integer", required: true }
229
+ ],
230
+ actions: [
231
+ {
232
+ action: "flow_if",
233
+ condition: "${args.level < 0 || args.level > 100}",
234
+ then: [
235
+ { action: "reply", content: "Volume must be between 0 and 100!", ephemeral: true },
236
+ { action: "abort" }
237
+ ]
238
+ },
239
+ {
240
+ action: "voice_volume",
241
+ level: "${args.level}"
242
+ },
243
+ {
244
+ action: "reply",
245
+ content: "Volume set to ${args.level}%",
246
+ ephemeral: true
247
+ }
248
+ ]
249
+ },
250
+ {
251
+ name: "shuffle",
252
+ description: "Shuffle the queue",
253
+ actions: [
254
+ {
255
+ action: "queue_shuffle"
256
+ },
257
+ {
258
+ action: "reply",
259
+ content: "Shuffled the queue!",
260
+ ephemeral: true
261
+ }
262
+ ]
263
+ },
264
+ {
265
+ name: "loop",
266
+ description: "Set loop mode",
267
+ options: [
268
+ { name: "mode", description: "Loop mode", type: "string", required: true, choices: [
269
+ { name: "Off", value: "off" },
270
+ { name: "Track", value: "track" },
271
+ { name: "Queue", value: "queue" }
272
+ ] }
273
+ ],
274
+ actions: [
275
+ {
276
+ action: "queue_loop",
277
+ mode: "${args.mode}"
278
+ },
279
+ {
280
+ action: "reply",
281
+ content: "Loop mode set to: ${args.mode}",
282
+ ephemeral: true
283
+ }
284
+ ]
285
+ },
286
+ {
287
+ name: "seek",
288
+ description: "Seek to a position in the song",
289
+ options: [
290
+ { name: "position", description: "Position (e.g., 1:30, 90)", type: "string", required: true }
291
+ ],
292
+ actions: [
293
+ {
294
+ action: "flow_if",
295
+ condition: "${!config.music?.allowSeeking}",
296
+ then: [
297
+ { action: "reply", content: "Seeking is disabled!", ephemeral: true },
298
+ { action: "abort" }
299
+ ]
300
+ },
301
+ {
302
+ action: "voice_seek",
303
+ position: "${parseDuration(args.position)}"
304
+ },
305
+ {
306
+ action: "reply",
307
+ content: "Seeked to ${args.position}",
308
+ ephemeral: true
309
+ }
310
+ ]
311
+ },
312
+ {
313
+ name: "filter",
314
+ description: "Apply an audio filter",
315
+ options: [
316
+ { name: "filter", description: "Filter to apply", type: "string", required: true, choices: [
317
+ { name: "None", value: "none" },
318
+ { name: "Bass Boost", value: "bassboost" },
319
+ { name: "Nightcore", value: "nightcore" },
320
+ { name: "Vaporwave", value: "vaporwave" },
321
+ { name: "8D", value: "8d" },
322
+ { name: "Tremolo", value: "tremolo" },
323
+ { name: "Vibrato", value: "vibrato" }
324
+ ] }
325
+ ],
326
+ actions: [
327
+ {
328
+ action: "flow_if",
329
+ condition: "${!config.music?.allowFilters}",
330
+ then: [
331
+ { action: "reply", content: "Filters are disabled!", ephemeral: true },
332
+ { action: "abort" }
333
+ ]
334
+ },
335
+ {
336
+ action: "voice_set_filter",
337
+ filter: "${args.filter}"
338
+ },
339
+ {
340
+ action: "reply",
341
+ content: "Applied filter: ${args.filter}",
342
+ ephemeral: true
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ name: "leave",
348
+ description: "Leave the voice channel",
349
+ actions: [
350
+ {
351
+ action: "voice_leave"
352
+ },
353
+ {
354
+ action: "reply",
355
+ content: "Left the voice channel.",
356
+ ephemeral: true
357
+ }
358
+ ]
359
+ }
360
+ ];
361
+ var musicEventHandlers = [
362
+ // Announce now playing
363
+ {
364
+ event: "voice_track_start",
365
+ condition: "${config.music?.announceNowPlaying}",
366
+ actions: [
367
+ {
368
+ action: "send_message",
369
+ channel: "${config.music.announceChannel || channel.id}",
370
+ embed: {
371
+ title: "Now Playing",
372
+ description: "[${track.title}](${track.url})",
373
+ thumbnail: "${track.thumbnail}",
374
+ color: "#5865f2",
375
+ fields: [
376
+ { name: "Duration", value: "${formatDuration(track.duration)}", inline: true },
377
+ { name: "Requested by", value: "<@${track.requester}>", inline: true }
378
+ ]
379
+ }
380
+ }
381
+ ]
382
+ },
383
+ // Auto-leave when alone
384
+ {
385
+ event: "voice_leave",
386
+ condition: "${voiceChannel.members.filter(m => !m.user.bot).size === 0 && !config.music?.stayInChannel}",
387
+ actions: [
388
+ { action: "wait", duration: 3e4 },
389
+ {
390
+ action: "flow_if",
391
+ condition: "${voiceChannel.members.filter(m => !m.user.bot).size === 0}",
392
+ then: [
393
+ { action: "voice_leave" }
394
+ ]
395
+ }
396
+ ]
397
+ }
398
+ ];
399
+ function getMusicSpec(config = {}) {
400
+ return {
401
+ commands: musicCommands,
402
+ events: musicEventHandlers,
403
+ state: {
404
+ tables: musicTables
405
+ }
406
+ };
407
+ }
408
+ export {
409
+ getMusicSpec,
410
+ musicCommands,
411
+ musicEventHandlers,
412
+ musicTables
413
+ };
414
+ //# sourceMappingURL=index.js.map