@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.
- package/dist/afk/index.d.ts +21 -0
- package/dist/afk/index.js +195 -0
- package/dist/afk/index.js.map +1 -0
- package/dist/auto-responder/index.d.ts +21 -0
- package/dist/auto-responder/index.js +356 -0
- package/dist/auto-responder/index.js.map +1 -0
- package/dist/giveaways/index.d.ts +25 -0
- package/dist/giveaways/index.js +416 -0
- package/dist/giveaways/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +5402 -0
- package/dist/index.js.map +1 -0
- package/dist/leveling/index.d.ts +46 -0
- package/dist/leveling/index.js +521 -0
- package/dist/leveling/index.js.map +1 -0
- package/dist/logging/index.d.ts +52 -0
- package/dist/logging/index.js +519 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/moderation/index.d.ts +83 -0
- package/dist/moderation/index.js +221 -0
- package/dist/moderation/index.js.map +1 -0
- package/dist/music/index.d.ts +33 -0
- package/dist/music/index.js +414 -0
- package/dist/music/index.js.map +1 -0
- package/dist/polls/index.d.ts +21 -0
- package/dist/polls/index.js +395 -0
- package/dist/polls/index.js.map +1 -0
- package/dist/reaction-roles/index.d.ts +19 -0
- package/dist/reaction-roles/index.js +551 -0
- package/dist/reaction-roles/index.js.map +1 -0
- package/dist/reminders/index.d.ts +23 -0
- package/dist/reminders/index.js +224 -0
- package/dist/reminders/index.js.map +1 -0
- package/dist/starboard/index.d.ts +35 -0
- package/dist/starboard/index.js +401 -0
- package/dist/starboard/index.js.map +1 -0
- package/dist/tickets/index.d.ts +43 -0
- package/dist/tickets/index.js +614 -0
- package/dist/tickets/index.js.map +1 -0
- package/dist/utilities/index.d.ts +15 -0
- package/dist/utilities/index.js +299 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/welcome/index.d.ts +48 -0
- package/dist/welcome/index.js +302 -0
- package/dist/welcome/index.js.map +1 -0
- 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
|