@furlow/builtins 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ var levelingTables = {
15
15
  var levelingEventHandlers = [
16
16
  {
17
17
  event: "message",
18
- condition: "${!message.author.bot && !config.leveling.ignoredChannels?.includes(channel.id)}",
18
+ condition: "!message.author.bot && !config.leveling.ignoredChannels?.includes(channel.id)",
19
19
  actions: [
20
20
  // Check cooldown
21
21
  {
@@ -30,7 +30,7 @@ var levelingEventHandlers = [
30
30
  // Initialize if new user
31
31
  {
32
32
  action: "flow_if",
33
- condition: "${!userData || userData.length === 0}",
33
+ condition: "!userData || userData.length === 0",
34
34
  then: [
35
35
  {
36
36
  action: "db_insert",
@@ -59,7 +59,7 @@ var levelingEventHandlers = [
59
59
  },
60
60
  {
61
61
  action: "flow_if",
62
- condition: "${cooldownPassed}",
62
+ condition: "cooldownPassed",
63
63
  then: [
64
64
  // Calculate XP gain
65
65
  {
@@ -129,25 +129,25 @@ var levelingEventHandlers = [
129
129
  // Handle level up
130
130
  {
131
131
  action: "flow_if",
132
- condition: "${leveledUp}",
132
+ condition: "leveledUp",
133
133
  then: [
134
134
  // Announce level up
135
135
  {
136
136
  action: "flow_if",
137
- condition: "${config.leveling.announceChannel}",
137
+ condition: "config.leveling.announceChannel",
138
138
  then: [
139
139
  {
140
140
  action: "flow_if",
141
- condition: "${config.leveling.levelUpEmbed}",
141
+ condition: "config.leveling.levelUpEmbed",
142
142
  then: [
143
143
  {
144
144
  action: "send_message",
145
145
  channel: "${config.leveling.announceChannel}",
146
146
  embed: {
147
147
  title: "Level Up!",
148
- description: '${config.leveling.levelUpMessage || member.displayName + " has reached level " + newLevel + "!"}',
148
+ description: '${config.leveling.levelUpMessage || member.display_name + " has reached level " + newLevel + "!"}',
149
149
  color: "#ffd700",
150
- thumbnail: "${member.avatarURL}"
150
+ thumbnail: "${member.avatar}"
151
151
  }
152
152
  }
153
153
  ],
@@ -155,7 +155,7 @@ var levelingEventHandlers = [
155
155
  {
156
156
  action: "send_message",
157
157
  channel: "${config.leveling.announceChannel}",
158
- content: '${config.leveling.levelUpMessage || "Congratulations " + member.displayName + "! You reached level " + newLevel + "!"}'
158
+ content: '${config.leveling.levelUpMessage || "Congratulations " + member.display_name + "! You reached level " + newLevel + "!"}'
159
159
  }
160
160
  ]
161
161
  }
@@ -164,12 +164,12 @@ var levelingEventHandlers = [
164
164
  // Award role rewards
165
165
  {
166
166
  action: "flow_if",
167
- condition: "${config.leveling.rewards && config.leveling.rewards[newLevel]}",
167
+ condition: "config.leveling.rewards && config.leveling.rewards[newLevel]",
168
168
  then: [
169
169
  // Remove previous rewards if not stacking
170
170
  {
171
171
  action: "flow_if",
172
- condition: "${!config.leveling.stackRewards && currentLevel > 0 && config.leveling.rewards[currentLevel]}",
172
+ condition: "!config.leveling.stackRewards && currentLevel > 0 && config.leveling.rewards[currentLevel]",
173
173
  then: [
174
174
  {
175
175
  action: "remove_role",
@@ -216,7 +216,7 @@ var levelingCommands = [
216
216
  },
217
217
  {
218
218
  action: "flow_if",
219
- condition: "${!userData || userData.length === 0}",
219
+ condition: "!userData || userData.length === 0",
220
220
  then: [
221
221
  {
222
222
  action: "reply",
@@ -248,7 +248,7 @@ var levelingCommands = [
248
248
  // Render rank card or embed
249
249
  {
250
250
  action: "flow_if",
251
- condition: "${config.leveling.useRankCard}",
251
+ condition: "config.leveling.useRankCard",
252
252
  then: [
253
253
  {
254
254
  action: "canvas_render",
@@ -280,7 +280,7 @@ var levelingCommands = [
280
280
  embed: {
281
281
  title: "${targetUser.username}'s Rank",
282
282
  color: "#5865f2",
283
- thumbnail: "${targetUser.avatarURL}",
283
+ thumbnail: "${targetUser.avatar}",
284
284
  fields: [
285
285
  { name: "Rank", value: "#${rank}", inline: true },
286
286
  { name: "Level", value: "${userData[0].level}", inline: true },
@@ -416,7 +416,7 @@ var levelingCanvasGenerators = {
416
416
  // User avatar
417
417
  {
418
418
  type: "circle_image",
419
- url: "${user.avatarURL || user.defaultAvatarURL}",
419
+ url: "${user.avatar}",
420
420
  x: 120,
421
421
  y: 141,
422
422
  radius: 80,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/leveling/index.ts"],"sourcesContent":["/**\n * Leveling builtin module\n * Handles XP, levels, rewards, leaderboards, and rank cards\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, CanvasGenerator, TableDefinition } from '@furlow/schema';\n\nexport interface LevelingConfig {\n /** XP per message range [min, max] */\n xpPerMessage?: [number, number];\n /** Cooldown between XP gains (seconds) */\n xpCooldown?: number;\n /** XP multiplier for roles */\n roleMultipliers?: Record<string, number>;\n /** Channels where XP is disabled */\n ignoredChannels?: string[];\n /** Roles that don't earn XP */\n ignoredRoles?: string[];\n /** Channel for level up announcements */\n announceChannel?: string;\n /** Level up message */\n levelUpMessage?: string;\n /** Use embed for level up */\n levelUpEmbed?: boolean;\n /** Level rewards (level -> role IDs) */\n rewards?: Record<number, string[]>;\n /** Stack rewards or remove previous */\n stackRewards?: boolean;\n /** Use rank card images */\n useRankCard?: boolean;\n /** Rank card generator name */\n rankCardGenerator?: string;\n /** XP curve formula */\n xpCurve?: 'linear' | 'exponential' | 'custom';\n /** Base XP for level 1 */\n baseXP?: number;\n /** XP multiplier per level */\n xpMultiplier?: number;\n}\n\nexport const levelingTables: Record<string, TableDefinition> = {\n levels: {\n columns: {\n id: { type: 'number', primary: true },\n user_id: { type: 'string', index: true },\n guild_id: { type: 'string', index: true },\n xp: { type: 'number', default: 0 },\n level: { type: 'number', default: 0 },\n total_messages: { type: 'number', default: 0 },\n last_xp_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const levelingEventHandlers: EventHandler[] = [\n {\n event: 'message',\n condition: '${!message.author.bot && !config.leveling.ignoredChannels?.includes(channel.id)}',\n actions: [\n // Check cooldown\n {\n action: 'db_query',\n table: 'levels',\n where: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n },\n as: 'userData',\n },\n // Initialize if new user\n {\n action: 'flow_if',\n condition: '${!userData || userData.length === 0}',\n then: [\n {\n action: 'db_insert',\n table: 'levels',\n data: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n xp: 0,\n level: 0,\n total_messages: 0,\n last_xp_at: '${now()}',\n },\n },\n {\n action: 'set',\n key: 'userData',\n value: [{ xp: 0, level: 0, total_messages: 0, last_xp_at: null }],\n },\n ],\n },\n // Check cooldown\n {\n action: 'set',\n key: 'cooldownPassed',\n value: '${!userData[0].last_xp_at || (now() - userData[0].last_xp_at) > (config.leveling.xpCooldown || 60) * 1000}',\n },\n {\n action: 'flow_if',\n condition: '${cooldownPassed}',\n then: [\n // Calculate XP gain\n {\n action: 'set',\n key: 'xpRange',\n value: '${config.leveling.xpPerMessage || [15, 25]}',\n },\n {\n action: 'set',\n key: 'baseXpGain',\n value: '${random(xpRange[0], xpRange[1])}',\n },\n // Apply role multiplier\n {\n action: 'set',\n key: 'multiplier',\n value: '${config.leveling.roleMultipliers ? (member.roles | map(r => config.leveling.roleMultipliers[r.id] || 1) | max) : 1}',\n },\n {\n action: 'set',\n key: 'xpGain',\n value: '${floor(baseXpGain * multiplier)}',\n },\n // Calculate new XP and level\n {\n action: 'set',\n key: 'newXP',\n value: '${userData[0].xp + xpGain}',\n },\n {\n action: 'set',\n key: 'currentLevel',\n value: '${userData[0].level}',\n },\n // Calculate XP needed for next level\n {\n action: 'set',\n key: 'xpForNextLevel',\n value: '${config.leveling.xpCurve === \"exponential\" ? floor((config.leveling.baseXP || 100) * pow(config.leveling.xpMultiplier || 1.5, currentLevel)) : (config.leveling.baseXP || 100) * (currentLevel + 1)}',\n },\n // Check for level up\n {\n action: 'set',\n key: 'newLevel',\n value: '${newXP >= xpForNextLevel ? currentLevel + 1 : currentLevel}',\n },\n {\n action: 'set',\n key: 'leveledUp',\n value: '${newLevel > currentLevel}',\n },\n // Update database\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n xp: '${leveledUp ? newXP - xpForNextLevel : newXP}',\n level: '${newLevel}',\n total_messages: '${userData[0].total_messages + 1}',\n last_xp_at: '${now()}',\n },\n },\n // Handle level up\n {\n action: 'flow_if',\n condition: '${leveledUp}',\n then: [\n // Announce level up\n {\n action: 'flow_if',\n condition: '${config.leveling.announceChannel}',\n then: [\n {\n action: 'flow_if',\n condition: '${config.leveling.levelUpEmbed}',\n then: [\n {\n action: 'send_message',\n channel: '${config.leveling.announceChannel}',\n embed: {\n title: 'Level Up!',\n description: '${config.leveling.levelUpMessage || member.displayName + \" has reached level \" + newLevel + \"!\"}',\n color: '#ffd700',\n thumbnail: '${member.avatarURL}',\n },\n },\n ],\n else: [\n {\n action: 'send_message',\n channel: '${config.leveling.announceChannel}',\n content: '${config.leveling.levelUpMessage || \"Congratulations \" + member.displayName + \"! You reached level \" + newLevel + \"!\"}',\n },\n ],\n },\n ],\n },\n // Award role rewards\n {\n action: 'flow_if',\n condition: '${config.leveling.rewards && config.leveling.rewards[newLevel]}',\n then: [\n // Remove previous rewards if not stacking\n {\n action: 'flow_if',\n condition: '${!config.leveling.stackRewards && currentLevel > 0 && config.leveling.rewards[currentLevel]}',\n then: [\n {\n action: 'remove_role',\n user: '${member.id}',\n role: '${config.leveling.rewards[currentLevel]}',\n },\n ],\n },\n {\n action: 'assign_role',\n user: '${member.id}',\n role: '${config.leveling.rewards[newLevel]}',\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n },\n];\n\nexport const levelingCommands: CommandDefinition[] = [\n {\n name: 'rank',\n description: 'View your rank or another user\\'s rank',\n options: [\n { name: 'user', description: 'User to check', type: 'user', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'targetUser',\n value: '${args.user || user}',\n },\n {\n action: 'db_query',\n table: 'levels',\n where: {\n user_id: '${targetUser.id}',\n guild_id: '${guild.id}',\n },\n as: 'userData',\n },\n {\n action: 'flow_if',\n condition: '${!userData || userData.length === 0}',\n then: [\n {\n action: 'reply',\n content: '${targetUser.username} has no XP yet!',\n ephemeral: true,\n },\n ],\n else: [\n // Get leaderboard position\n {\n action: 'db_query',\n table: 'levels',\n where: {\n guild_id: '${guild.id}',\n },\n order_by: 'level DESC, xp DESC',\n as: 'leaderboard',\n },\n {\n action: 'set',\n key: 'rank',\n value: '${(leaderboard | findIndex(u => u.user_id === targetUser.id)) + 1}',\n },\n {\n action: 'set',\n key: 'xpForNextLevel',\n value: '${config.leveling.xpCurve === \"exponential\" ? floor((config.leveling.baseXP || 100) * pow(config.leveling.xpMultiplier || 1.5, userData[0].level)) : (config.leveling.baseXP || 100) * (userData[0].level + 1)}',\n },\n // Render rank card or embed\n {\n action: 'flow_if',\n condition: '${config.leveling.useRankCard}',\n then: [\n {\n action: 'canvas_render',\n generator: '${config.leveling.rankCardGenerator || \"rank_card\"}',\n context: {\n user: '${targetUser}',\n member: '${guild.members.cache.get(targetUser.id)}',\n level: '${userData[0].level}',\n xp: '${userData[0].xp}',\n xpNeeded: '${xpForNextLevel}',\n rank: '${rank}',\n totalMessages: '${userData[0].total_messages}',\n },\n as: 'rankCard',\n },\n {\n action: 'reply',\n files: [\n {\n attachment: '${rankCard}',\n name: 'rank.png',\n },\n ],\n },\n ],\n else: [\n {\n action: 'reply',\n embed: {\n title: '${targetUser.username}\\'s Rank',\n color: '#5865f2',\n thumbnail: '${targetUser.avatarURL}',\n fields: [\n { name: 'Rank', value: '#${rank}', inline: true },\n { name: 'Level', value: '${userData[0].level}', inline: true },\n { name: 'XP', value: '${userData[0].xp}/${xpForNextLevel}', inline: true },\n { name: 'Messages', value: '${userData[0].total_messages}', inline: true },\n ],\n },\n },\n ],\n },\n ],\n },\n ],\n },\n {\n name: 'leaderboard',\n description: 'View the server leaderboard',\n options: [\n { name: 'page', description: 'Page number', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'page',\n value: '${args.page || 1}',\n },\n {\n action: 'set',\n key: 'perPage',\n value: 10,\n },\n {\n action: 'db_query',\n table: 'levels',\n where: {\n guild_id: '${guild.id}',\n },\n order_by: 'level DESC, xp DESC',\n limit: '${perPage}',\n offset: '${(page - 1) * perPage}',\n as: 'leaderboard',\n },\n {\n action: 'set',\n key: 'leaderboardText',\n value: '${leaderboard | mapIndex((entry, i) => \"#\" + ((page - 1) * perPage + i + 1) + \" <@\" + entry.user_id + \"> - Level \" + entry.level + \" (\" + entry.xp + \" XP)\") | join(\"\\\\n\")}',\n },\n {\n action: 'reply',\n embed: {\n title: '${guild.name} Leaderboard',\n description: '${leaderboardText || \"No entries yet!\"}',\n color: '#ffd700',\n footer: {\n text: 'Page ${page}',\n },\n },\n },\n ],\n },\n {\n name: 'setxp',\n description: 'Set a user\\'s XP (admin)',\n options: [\n { name: 'user', description: 'User to modify', type: 'user', required: true },\n { name: 'xp', description: 'XP amount', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n xp: '${args.xp}',\n },\n upsert: true,\n },\n {\n action: 'reply',\n content: 'Set ${args.user.username}\\'s XP to ${args.xp}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'setlevel',\n description: 'Set a user\\'s level (admin)',\n options: [\n { name: 'user', description: 'User to modify', type: 'user', required: true },\n { name: 'level', description: 'Level', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n level: '${args.level}',\n xp: 0,\n },\n upsert: true,\n },\n {\n action: 'reply',\n content: 'Set ${args.user.username}\\'s level to ${args.level}',\n ephemeral: true,\n },\n ],\n },\n];\n\nexport const levelingCanvasGenerators: Record<string, CanvasGenerator> = {\n rank_card: {\n width: 934,\n height: 282,\n background: '#23272a',\n layers: [\n // Background gradient\n {\n type: 'rect',\n x: 0,\n y: 0,\n width: 934,\n height: 282,\n color: 'linear-gradient(135deg, #1a1c20 0%, #2c2f33 100%)',\n radius: 20,\n },\n // User avatar\n {\n type: 'circle_image',\n url: '${user.avatarURL || user.defaultAvatarURL}',\n x: 120,\n y: 141,\n radius: 80,\n border: {\n width: 5,\n color: '#5865f2',\n },\n },\n // Username\n {\n type: 'text',\n text: '${user.username}',\n x: 260,\n y: 120,\n font: 'bold 36px \"Poppins\", sans-serif',\n color: '#ffffff',\n align: 'left',\n },\n // Rank badge\n {\n type: 'text',\n text: 'RANK #${rank}',\n x: 850,\n y: 60,\n font: 'bold 24px \"Poppins\", sans-serif',\n color: '#7289da',\n align: 'right',\n },\n // Level badge\n {\n type: 'text',\n text: 'LEVEL ${level}',\n x: 850,\n y: 90,\n font: 'bold 32px \"Poppins\", sans-serif',\n color: '#ffffff',\n align: 'right',\n },\n // XP Progress bar background\n {\n type: 'rect',\n x: 260,\n y: 180,\n width: 600,\n height: 30,\n color: '#484b4e',\n radius: 15,\n },\n // XP Progress bar fill\n {\n type: 'progress_bar',\n x: 260,\n y: 180,\n width: 600,\n height: 30,\n progress: '${xp / xpNeeded}',\n color: 'linear-gradient(90deg, #5865f2 0%, #7289da 100%)',\n radius: 15,\n },\n // XP Text\n {\n type: 'text',\n text: '${xp} / ${xpNeeded} XP',\n x: 560,\n y: 160,\n font: '18px \"Poppins\", sans-serif',\n color: '#99aab5',\n align: 'center',\n },\n // Messages count\n {\n type: 'text',\n text: '${totalMessages} messages',\n x: 260,\n y: 240,\n font: '16px \"Poppins\", sans-serif',\n color: '#72767d',\n align: 'left',\n },\n ],\n },\n};\n\nexport function getLevelingSpec(config: LevelingConfig = {}): Partial<FurlowSpec> {\n return {\n events: levelingEventHandlers,\n commands: levelingCommands,\n state: {\n tables: levelingTables,\n },\n canvas: {\n generators: levelingCanvasGenerators,\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/leveling/index.ts"],"sourcesContent":["/**\n * Leveling builtin module\n * Handles XP, levels, rewards, leaderboards, and rank cards\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler, CanvasGenerator, TableDefinition } from '@furlow/schema';\n\nexport interface LevelingConfig {\n /** XP per message range [min, max] */\n xpPerMessage?: [number, number];\n /** Cooldown between XP gains (seconds) */\n xpCooldown?: number;\n /** XP multiplier for roles */\n roleMultipliers?: Record<string, number>;\n /** Channels where XP is disabled */\n ignoredChannels?: string[];\n /** Roles that don't earn XP */\n ignoredRoles?: string[];\n /** Channel for level up announcements */\n announceChannel?: string;\n /** Level up message */\n levelUpMessage?: string;\n /** Use embed for level up */\n levelUpEmbed?: boolean;\n /** Level rewards (level -> role IDs) */\n rewards?: Record<number, string[]>;\n /** Stack rewards or remove previous */\n stackRewards?: boolean;\n /** Use rank card images */\n useRankCard?: boolean;\n /** Rank card generator name */\n rankCardGenerator?: string;\n /** XP curve formula */\n xpCurve?: 'linear' | 'exponential' | 'custom';\n /** Base XP for level 1 */\n baseXP?: number;\n /** XP multiplier per level */\n xpMultiplier?: number;\n}\n\nexport const levelingTables: Record<string, TableDefinition> = {\n levels: {\n columns: {\n id: { type: 'number', primary: true },\n user_id: { type: 'string', index: true },\n guild_id: { type: 'string', index: true },\n xp: { type: 'number', default: 0 },\n level: { type: 'number', default: 0 },\n total_messages: { type: 'number', default: 0 },\n last_xp_at: { type: 'timestamp' },\n },\n },\n};\n\nexport const levelingEventHandlers: EventHandler[] = [\n {\n event: 'message',\n condition: '!message.author.bot && !config.leveling.ignoredChannels?.includes(channel.id)',\n actions: [\n // Check cooldown\n {\n action: 'db_query',\n table: 'levels',\n where: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n },\n as: 'userData',\n },\n // Initialize if new user\n {\n action: 'flow_if',\n condition: '!userData || userData.length === 0',\n then: [\n {\n action: 'db_insert',\n table: 'levels',\n data: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n xp: 0,\n level: 0,\n total_messages: 0,\n last_xp_at: '${now()}',\n },\n },\n {\n action: 'set',\n key: 'userData',\n value: [{ xp: 0, level: 0, total_messages: 0, last_xp_at: null }],\n },\n ],\n },\n // Check cooldown\n {\n action: 'set',\n key: 'cooldownPassed',\n value: '${!userData[0].last_xp_at || (now() - userData[0].last_xp_at) > (config.leveling.xpCooldown || 60) * 1000}',\n },\n {\n action: 'flow_if',\n condition: 'cooldownPassed',\n then: [\n // Calculate XP gain\n {\n action: 'set',\n key: 'xpRange',\n value: '${config.leveling.xpPerMessage || [15, 25]}',\n },\n {\n action: 'set',\n key: 'baseXpGain',\n value: '${random(xpRange[0], xpRange[1])}',\n },\n // Apply role multiplier\n {\n action: 'set',\n key: 'multiplier',\n value: '${config.leveling.roleMultipliers ? (member.roles | map(r => config.leveling.roleMultipliers[r.id] || 1) | max) : 1}',\n },\n {\n action: 'set',\n key: 'xpGain',\n value: '${floor(baseXpGain * multiplier)}',\n },\n // Calculate new XP and level\n {\n action: 'set',\n key: 'newXP',\n value: '${userData[0].xp + xpGain}',\n },\n {\n action: 'set',\n key: 'currentLevel',\n value: '${userData[0].level}',\n },\n // Calculate XP needed for next level\n {\n action: 'set',\n key: 'xpForNextLevel',\n value: '${config.leveling.xpCurve === \"exponential\" ? floor((config.leveling.baseXP || 100) * pow(config.leveling.xpMultiplier || 1.5, currentLevel)) : (config.leveling.baseXP || 100) * (currentLevel + 1)}',\n },\n // Check for level up\n {\n action: 'set',\n key: 'newLevel',\n value: '${newXP >= xpForNextLevel ? currentLevel + 1 : currentLevel}',\n },\n {\n action: 'set',\n key: 'leveledUp',\n value: '${newLevel > currentLevel}',\n },\n // Update database\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n xp: '${leveledUp ? newXP - xpForNextLevel : newXP}',\n level: '${newLevel}',\n total_messages: '${userData[0].total_messages + 1}',\n last_xp_at: '${now()}',\n },\n },\n // Handle level up\n {\n action: 'flow_if',\n condition: 'leveledUp',\n then: [\n // Announce level up\n {\n action: 'flow_if',\n condition: 'config.leveling.announceChannel',\n then: [\n {\n action: 'flow_if',\n condition: 'config.leveling.levelUpEmbed',\n then: [\n {\n action: 'send_message',\n channel: '${config.leveling.announceChannel}',\n embed: {\n title: 'Level Up!',\n description: '${config.leveling.levelUpMessage || member.display_name + \" has reached level \" + newLevel + \"!\"}',\n color: '#ffd700',\n thumbnail: '${member.avatar}',\n },\n },\n ],\n else: [\n {\n action: 'send_message',\n channel: '${config.leveling.announceChannel}',\n content: '${config.leveling.levelUpMessage || \"Congratulations \" + member.display_name + \"! You reached level \" + newLevel + \"!\"}',\n },\n ],\n },\n ],\n },\n // Award role rewards\n {\n action: 'flow_if',\n condition: 'config.leveling.rewards && config.leveling.rewards[newLevel]',\n then: [\n // Remove previous rewards if not stacking\n {\n action: 'flow_if',\n condition: '!config.leveling.stackRewards && currentLevel > 0 && config.leveling.rewards[currentLevel]',\n then: [\n {\n action: 'remove_role',\n user: '${member.id}',\n role: '${config.leveling.rewards[currentLevel]}',\n },\n ],\n },\n {\n action: 'assign_role',\n user: '${member.id}',\n role: '${config.leveling.rewards[newLevel]}',\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n },\n];\n\nexport const levelingCommands: CommandDefinition[] = [\n {\n name: 'rank',\n description: 'View your rank or another user\\'s rank',\n options: [\n { name: 'user', description: 'User to check', type: 'user', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'targetUser',\n value: '${args.user || user}',\n },\n {\n action: 'db_query',\n table: 'levels',\n where: {\n user_id: '${targetUser.id}',\n guild_id: '${guild.id}',\n },\n as: 'userData',\n },\n {\n action: 'flow_if',\n condition: '!userData || userData.length === 0',\n then: [\n {\n action: 'reply',\n content: '${targetUser.username} has no XP yet!',\n ephemeral: true,\n },\n ],\n else: [\n // Get leaderboard position\n {\n action: 'db_query',\n table: 'levels',\n where: {\n guild_id: '${guild.id}',\n },\n order_by: 'level DESC, xp DESC',\n as: 'leaderboard',\n },\n {\n action: 'set',\n key: 'rank',\n value: '${(leaderboard | findIndex(u => u.user_id === targetUser.id)) + 1}',\n },\n {\n action: 'set',\n key: 'xpForNextLevel',\n value: '${config.leveling.xpCurve === \"exponential\" ? floor((config.leveling.baseXP || 100) * pow(config.leveling.xpMultiplier || 1.5, userData[0].level)) : (config.leveling.baseXP || 100) * (userData[0].level + 1)}',\n },\n // Render rank card or embed\n {\n action: 'flow_if',\n condition: 'config.leveling.useRankCard',\n then: [\n {\n action: 'canvas_render',\n generator: '${config.leveling.rankCardGenerator || \"rank_card\"}',\n context: {\n user: '${targetUser}',\n member: '${guild.members.cache.get(targetUser.id)}',\n level: '${userData[0].level}',\n xp: '${userData[0].xp}',\n xpNeeded: '${xpForNextLevel}',\n rank: '${rank}',\n totalMessages: '${userData[0].total_messages}',\n },\n as: 'rankCard',\n },\n {\n action: 'reply',\n files: [\n {\n attachment: '${rankCard}',\n name: 'rank.png',\n },\n ],\n },\n ],\n else: [\n {\n action: 'reply',\n embed: {\n title: '${targetUser.username}\\'s Rank',\n color: '#5865f2',\n thumbnail: '${targetUser.avatar}',\n fields: [\n { name: 'Rank', value: '#${rank}', inline: true },\n { name: 'Level', value: '${userData[0].level}', inline: true },\n { name: 'XP', value: '${userData[0].xp}/${xpForNextLevel}', inline: true },\n { name: 'Messages', value: '${userData[0].total_messages}', inline: true },\n ],\n },\n },\n ],\n },\n ],\n },\n ],\n },\n {\n name: 'leaderboard',\n description: 'View the server leaderboard',\n options: [\n { name: 'page', description: 'Page number', type: 'integer', required: false },\n ],\n actions: [\n {\n action: 'set',\n key: 'page',\n value: '${args.page || 1}',\n },\n {\n action: 'set',\n key: 'perPage',\n value: 10,\n },\n {\n action: 'db_query',\n table: 'levels',\n where: {\n guild_id: '${guild.id}',\n },\n order_by: 'level DESC, xp DESC',\n limit: '${perPage}',\n offset: '${(page - 1) * perPage}',\n as: 'leaderboard',\n },\n {\n action: 'set',\n key: 'leaderboardText',\n value: '${leaderboard | mapIndex((entry, i) => \"#\" + ((page - 1) * perPage + i + 1) + \" <@\" + entry.user_id + \"> - Level \" + entry.level + \" (\" + entry.xp + \" XP)\") | join(\"\\\\n\")}',\n },\n {\n action: 'reply',\n embed: {\n title: '${guild.name} Leaderboard',\n description: '${leaderboardText || \"No entries yet!\"}',\n color: '#ffd700',\n footer: {\n text: 'Page ${page}',\n },\n },\n },\n ],\n },\n {\n name: 'setxp',\n description: 'Set a user\\'s XP (admin)',\n options: [\n { name: 'user', description: 'User to modify', type: 'user', required: true },\n { name: 'xp', description: 'XP amount', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n xp: '${args.xp}',\n },\n upsert: true,\n },\n {\n action: 'reply',\n content: 'Set ${args.user.username}\\'s XP to ${args.xp}',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'setlevel',\n description: 'Set a user\\'s level (admin)',\n options: [\n { name: 'user', description: 'User to modify', type: 'user', required: true },\n { name: 'level', description: 'Level', type: 'integer', required: true },\n ],\n actions: [\n {\n action: 'db_update',\n table: 'levels',\n where: {\n user_id: '${args.user.id}',\n guild_id: '${guild.id}',\n },\n data: {\n level: '${args.level}',\n xp: 0,\n },\n upsert: true,\n },\n {\n action: 'reply',\n content: 'Set ${args.user.username}\\'s level to ${args.level}',\n ephemeral: true,\n },\n ],\n },\n];\n\nexport const levelingCanvasGenerators: Record<string, CanvasGenerator> = {\n rank_card: {\n width: 934,\n height: 282,\n background: '#23272a',\n layers: [\n // Background gradient\n {\n type: 'rect',\n x: 0,\n y: 0,\n width: 934,\n height: 282,\n color: 'linear-gradient(135deg, #1a1c20 0%, #2c2f33 100%)',\n radius: 20,\n },\n // User avatar\n {\n type: 'circle_image',\n url: '${user.avatar}',\n x: 120,\n y: 141,\n radius: 80,\n border: {\n width: 5,\n color: '#5865f2',\n },\n },\n // Username\n {\n type: 'text',\n text: '${user.username}',\n x: 260,\n y: 120,\n font: 'bold 36px \"Poppins\", sans-serif',\n color: '#ffffff',\n align: 'left',\n },\n // Rank badge\n {\n type: 'text',\n text: 'RANK #${rank}',\n x: 850,\n y: 60,\n font: 'bold 24px \"Poppins\", sans-serif',\n color: '#7289da',\n align: 'right',\n },\n // Level badge\n {\n type: 'text',\n text: 'LEVEL ${level}',\n x: 850,\n y: 90,\n font: 'bold 32px \"Poppins\", sans-serif',\n color: '#ffffff',\n align: 'right',\n },\n // XP Progress bar background\n {\n type: 'rect',\n x: 260,\n y: 180,\n width: 600,\n height: 30,\n color: '#484b4e',\n radius: 15,\n },\n // XP Progress bar fill\n {\n type: 'progress_bar',\n x: 260,\n y: 180,\n width: 600,\n height: 30,\n progress: '${xp / xpNeeded}',\n color: 'linear-gradient(90deg, #5865f2 0%, #7289da 100%)',\n radius: 15,\n },\n // XP Text\n {\n type: 'text',\n text: '${xp} / ${xpNeeded} XP',\n x: 560,\n y: 160,\n font: '18px \"Poppins\", sans-serif',\n color: '#99aab5',\n align: 'center',\n },\n // Messages count\n {\n type: 'text',\n text: '${totalMessages} messages',\n x: 260,\n y: 240,\n font: '16px \"Poppins\", sans-serif',\n color: '#72767d',\n align: 'left',\n },\n ],\n },\n};\n\nexport function getLevelingSpec(config: LevelingConfig = {}): Partial<FurlowSpec> {\n return {\n events: levelingEventHandlers,\n commands: levelingCommands,\n state: {\n tables: levelingTables,\n },\n canvas: {\n generators: levelingCanvasGenerators,\n },\n };\n}\n"],"mappings":";AAwCO,IAAM,iBAAkD;AAAA,EAC7D,QAAQ;AAAA,IACN,SAAS;AAAA,MACP,IAAI,EAAE,MAAM,UAAU,SAAS,KAAK;AAAA,MACpC,SAAS,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MACvC,UAAU,EAAE,MAAM,UAAU,OAAO,KAAK;AAAA,MACxC,IAAI,EAAE,MAAM,UAAU,SAAS,EAAE;AAAA,MACjC,OAAO,EAAE,MAAM,UAAU,SAAS,EAAE;AAAA,MACpC,gBAAgB,EAAE,MAAM,UAAU,SAAS,EAAE;AAAA,MAC7C,YAAY,EAAE,MAAM,YAAY;AAAA,IAClC;AAAA,EACF;AACF;AAEO,IAAM,wBAAwC;AAAA,EACnD;AAAA,IACE,OAAO;AAAA,IACP,WAAW;AAAA,IACX,SAAS;AAAA;AAAA,MAEP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,QACA,IAAI;AAAA,MACN;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,SAAS;AAAA,cACT,UAAU;AAAA,cACV,IAAI;AAAA,cACJ,OAAO;AAAA,cACP,gBAAgB;AAAA,cAChB,YAAY;AAAA,YACd;AAAA,UACF;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO,CAAC,EAAE,IAAI,GAAG,OAAO,GAAG,gBAAgB,GAAG,YAAY,KAAK,CAAC;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAEA;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA;AAAA,UAEJ;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO;AAAA,cACL,SAAS;AAAA,cACT,UAAU;AAAA,YACZ;AAAA,YACA,MAAM;AAAA,cACJ,IAAI;AAAA,cACJ,OAAO;AAAA,cACP,gBAAgB;AAAA,cAChB,YAAY;AAAA,YACd;AAAA,UACF;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA;AAAA,cAEJ;AAAA,gBACE,QAAQ;AAAA,gBACR,WAAW;AAAA,gBACX,MAAM;AAAA,kBACJ;AAAA,oBACE,QAAQ;AAAA,oBACR,WAAW;AAAA,oBACX,MAAM;AAAA,sBACJ;AAAA,wBACE,QAAQ;AAAA,wBACR,SAAS;AAAA,wBACT,OAAO;AAAA,0BACL,OAAO;AAAA,0BACP,aAAa;AAAA,0BACb,OAAO;AAAA,0BACP,WAAW;AAAA,wBACb;AAAA,sBACF;AAAA,oBACF;AAAA,oBACA,MAAM;AAAA,sBACJ;AAAA,wBACE,QAAQ;AAAA,wBACR,SAAS;AAAA,wBACT,SAAS;AAAA,sBACX;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA;AAAA,cAEA;AAAA,gBACE,QAAQ;AAAA,gBACR,WAAW;AAAA,gBACX,MAAM;AAAA;AAAA,kBAEJ;AAAA,oBACE,QAAQ;AAAA,oBACR,WAAW;AAAA,oBACX,MAAM;AAAA,sBACJ;AAAA,wBACE,QAAQ;AAAA,wBACR,MAAM;AAAA,wBACN,MAAM;AAAA,sBACR;AAAA,oBACF;AAAA,kBACF;AAAA,kBACA;AAAA,oBACE,QAAQ;AAAA,oBACR,MAAM;AAAA,oBACN,MAAM;AAAA,kBACR;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,mBAAwC;AAAA,EACnD;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,QAAQ,aAAa,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,IAC9E;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,QACA,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,UACJ;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,MAAM;AAAA;AAAA,UAEJ;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,OAAO;AAAA,cACL,UAAU;AAAA,YACZ;AAAA,YACA,UAAU;AAAA,YACV,IAAI;AAAA,UACN;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK;AAAA,YACL,OAAO;AAAA,UACT;AAAA;AAAA,UAEA;AAAA,YACE,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,MAAM;AAAA,cACJ;AAAA,gBACE,QAAQ;AAAA,gBACR,WAAW;AAAA,gBACX,SAAS;AAAA,kBACP,MAAM;AAAA,kBACN,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,IAAI;AAAA,kBACJ,UAAU;AAAA,kBACV,MAAM;AAAA,kBACN,eAAe;AAAA,gBACjB;AAAA,gBACA,IAAI;AAAA,cACN;AAAA,cACA;AAAA,gBACE,QAAQ;AAAA,gBACR,OAAO;AAAA,kBACL;AAAA,oBACE,YAAY;AAAA,oBACZ,MAAM;AAAA,kBACR;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,YACA,MAAM;AAAA,cACJ;AAAA,gBACE,QAAQ;AAAA,gBACR,OAAO;AAAA,kBACL,OAAO;AAAA,kBACP,OAAO;AAAA,kBACP,WAAW;AAAA,kBACX,QAAQ;AAAA,oBACN,EAAE,MAAM,QAAQ,OAAO,YAAY,QAAQ,KAAK;AAAA,oBAChD,EAAE,MAAM,SAAS,OAAO,wBAAwB,QAAQ,KAAK;AAAA,oBAC7D,EAAE,MAAM,MAAM,OAAO,uCAAuC,QAAQ,KAAK;AAAA,oBACzE,EAAE,MAAM,YAAY,OAAO,iCAAiC,QAAQ,KAAK;AAAA,kBAC3E;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,QAAQ,aAAa,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,IAC/E;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;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,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,UACP,QAAQ;AAAA,YACN,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,QAAQ,aAAa,kBAAkB,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC5E,EAAE,MAAM,MAAM,aAAa,aAAa,MAAM,WAAW,UAAU,KAAK;AAAA,IAC1E;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,QACA,MAAM;AAAA,UACJ,IAAI;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,MACP,EAAE,MAAM,QAAQ,aAAa,kBAAkB,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC5E,EAAE,MAAM,SAAS,aAAa,SAAS,MAAM,WAAW,UAAU,KAAK;AAAA,IACzE;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,QACA,MAAM;AAAA,UACJ,OAAO;AAAA,UACP,IAAI;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,2BAA4D;AAAA,EACvE,WAAW;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA;AAAA,MAEN;AAAA,QACE,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,KAAK;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB,SAAyB,CAAC,GAAwB;AAChF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,MACL,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,YAAY;AAAA,IACd;AAAA,EACF;AACF;","names":[]}
@@ -3,7 +3,7 @@ var loggingEventHandlers = [
3
3
  // Message Delete
4
4
  {
5
5
  event: "message_delete",
6
- condition: "${config.logging.events?.messageDelete !== false && !config.logging.ignoredChannels?.includes(channel.id)}",
6
+ condition: "config.logging.events?.messageDelete !== false && !config.logging.ignoredChannels?.includes(channel.id)",
7
7
  actions: [
8
8
  {
9
9
  action: "send_message",
@@ -28,7 +28,7 @@ var loggingEventHandlers = [
28
28
  // Message Edit
29
29
  {
30
30
  event: "message_update",
31
- condition: "${config.logging.events?.messageEdit !== false && oldMessage.content !== newMessage.content && !config.logging.ignoredChannels?.includes(channel.id)}",
31
+ condition: "config.logging.events?.messageEdit !== false && oldMessage.content !== newMessage.content && !config.logging.ignoredChannels?.includes(channel.id)",
32
32
  actions: [
33
33
  {
34
34
  action: "send_message",
@@ -54,7 +54,7 @@ var loggingEventHandlers = [
54
54
  // Bulk Message Delete
55
55
  {
56
56
  event: "message_bulk_delete",
57
- condition: "${config.logging.events?.messageBulkDelete !== false}",
57
+ condition: "config.logging.events?.messageBulkDelete !== false",
58
58
  actions: [
59
59
  {
60
60
  action: "set",
@@ -82,12 +82,12 @@ var loggingEventHandlers = [
82
82
  // Member Join
83
83
  {
84
84
  event: "member_join",
85
- condition: "${config.logging.events?.memberJoin !== false}",
85
+ condition: "config.logging.events?.memberJoin !== false",
86
86
  actions: [
87
87
  {
88
88
  action: "set",
89
89
  key: "accountAge",
90
- value: "${floor((now() - member.user.createdAt) / (1000 * 60 * 60 * 24))}"
90
+ value: "${floor((now() - member.joined_at) / (1000 * 60 * 60 * 24))}"
91
91
  },
92
92
  {
93
93
  action: "send_message",
@@ -96,11 +96,11 @@ var loggingEventHandlers = [
96
96
  title: "Member Joined",
97
97
  description: "<@${member.id}> joined the server",
98
98
  color: "#57f287",
99
- thumbnail: "${member.user.avatarURL}",
99
+ thumbnail: "${member.avatar}",
100
100
  fields: [
101
101
  { name: "Username", value: "${member.user.tag}", inline: true },
102
102
  { name: "Account Age", value: "${accountAge} days", inline: true },
103
- { name: "Member Count", value: "${guild.memberCount}", inline: true }
103
+ { name: "Member Count", value: "${guild.member_count}", inline: true }
104
104
  ],
105
105
  footer: {
106
106
  text: "User ID: ${member.id}"
@@ -113,7 +113,7 @@ var loggingEventHandlers = [
113
113
  // Member Leave
114
114
  {
115
115
  event: "member_leave",
116
- condition: "${config.logging.events?.memberLeave !== false}",
116
+ condition: "config.logging.events?.memberLeave !== false",
117
117
  actions: [
118
118
  {
119
119
  action: "set",
@@ -127,11 +127,11 @@ var loggingEventHandlers = [
127
127
  title: "Member Left",
128
128
  description: "<@${member.id}> left the server",
129
129
  color: "#ed4245",
130
- thumbnail: "${member.user.avatarURL}",
130
+ thumbnail: "${member.avatar}",
131
131
  fields: [
132
132
  { name: "Username", value: "${member.user.tag}", inline: true },
133
133
  { name: "Roles", value: "${truncate(roles, 1024)}", inline: false },
134
- { name: "Joined At", value: '${timestamp(member.joinedAt, "R")}', inline: true }
134
+ { name: "Joined At", value: '${timestamp(member.joined_at, "R")}', inline: true }
135
135
  ],
136
136
  footer: {
137
137
  text: "User ID: ${member.id}"
@@ -144,12 +144,12 @@ var loggingEventHandlers = [
144
144
  // Member Update (roles, nickname)
145
145
  {
146
146
  event: "member_update",
147
- condition: "${config.logging.events?.memberUpdate !== false}",
147
+ condition: "config.logging.events?.memberUpdate !== false",
148
148
  actions: [
149
149
  // Role changes
150
150
  {
151
151
  action: "flow_if",
152
- condition: "${oldMember.roles.cache.size !== newMember.roles.cache.size}",
152
+ condition: "oldMember.roles.cache.size !== newMember.roles.cache.size",
153
153
  then: [
154
154
  {
155
155
  action: "set",
@@ -183,7 +183,7 @@ var loggingEventHandlers = [
183
183
  // Nickname changes
184
184
  {
185
185
  action: "flow_if",
186
- condition: "${oldMember.nickname !== newMember.nickname}",
186
+ condition: "oldMember.nickname !== newMember.nickname",
187
187
  then: [
188
188
  {
189
189
  action: "send_message",
@@ -209,7 +209,7 @@ var loggingEventHandlers = [
209
209
  // Member Ban
210
210
  {
211
211
  event: "member_ban",
212
- condition: "${config.logging.events?.memberBan !== false}",
212
+ condition: "config.logging.events?.memberBan !== false",
213
213
  actions: [
214
214
  {
215
215
  action: "send_message",
@@ -218,7 +218,7 @@ var loggingEventHandlers = [
218
218
  title: "Member Banned",
219
219
  description: "<@${user.id}> was banned",
220
220
  color: "#ed4245",
221
- thumbnail: "${user.avatarURL}",
221
+ thumbnail: "${user.avatar}",
222
222
  fields: [
223
223
  { name: "Username", value: "${user.tag}", inline: true },
224
224
  { name: "Reason", value: '${ban.reason || "No reason provided"}', inline: false }
@@ -234,7 +234,7 @@ var loggingEventHandlers = [
234
234
  // Member Unban
235
235
  {
236
236
  event: "member_unban",
237
- condition: "${config.logging.events?.memberUnban !== false}",
237
+ condition: "config.logging.events?.memberUnban !== false",
238
238
  actions: [
239
239
  {
240
240
  action: "send_message",
@@ -243,7 +243,7 @@ var loggingEventHandlers = [
243
243
  title: "Member Unbanned",
244
244
  description: "<@${user.id}> was unbanned",
245
245
  color: "#57f287",
246
- thumbnail: "${user.avatarURL}",
246
+ thumbnail: "${user.avatar}",
247
247
  fields: [
248
248
  { name: "Username", value: "${user.tag}", inline: true }
249
249
  ],
@@ -258,7 +258,7 @@ var loggingEventHandlers = [
258
258
  // Voice Join
259
259
  {
260
260
  event: "voice_join",
261
- condition: "${config.logging.events?.voiceJoin !== false}",
261
+ condition: "config.logging.events?.voiceJoin !== false",
262
262
  actions: [
263
263
  {
264
264
  action: "send_message",
@@ -278,7 +278,7 @@ var loggingEventHandlers = [
278
278
  // Voice Leave
279
279
  {
280
280
  event: "voice_leave",
281
- condition: "${config.logging.events?.voiceLeave !== false}",
281
+ condition: "config.logging.events?.voiceLeave !== false",
282
282
  actions: [
283
283
  {
284
284
  action: "send_message",
@@ -298,7 +298,7 @@ var loggingEventHandlers = [
298
298
  // Voice Move
299
299
  {
300
300
  event: "voice_move",
301
- condition: "${config.logging.events?.voiceMove !== false}",
301
+ condition: "config.logging.events?.voiceMove !== false",
302
302
  actions: [
303
303
  {
304
304
  action: "send_message",
@@ -322,7 +322,7 @@ var loggingEventHandlers = [
322
322
  // Channel Create
323
323
  {
324
324
  event: "channel_create",
325
- condition: "${config.logging.events?.channelCreate !== false}",
325
+ condition: "config.logging.events?.channelCreate !== false",
326
326
  actions: [
327
327
  {
328
328
  action: "send_message",
@@ -343,7 +343,7 @@ var loggingEventHandlers = [
343
343
  // Channel Delete
344
344
  {
345
345
  event: "channel_delete",
346
- condition: "${config.logging.events?.channelDelete !== false}",
346
+ condition: "config.logging.events?.channelDelete !== false",
347
347
  actions: [
348
348
  {
349
349
  action: "send_message",
@@ -363,7 +363,7 @@ var loggingEventHandlers = [
363
363
  // Role Create
364
364
  {
365
365
  event: "role_create",
366
- condition: "${config.logging.events?.roleCreate !== false}",
366
+ condition: "config.logging.events?.roleCreate !== false",
367
367
  actions: [
368
368
  {
369
369
  action: "send_message",
@@ -388,7 +388,7 @@ var loggingEventHandlers = [
388
388
  // Role Delete
389
389
  {
390
390
  event: "role_delete",
391
- condition: "${config.logging.events?.roleDelete !== false}",
391
+ condition: "config.logging.events?.roleDelete !== false",
392
392
  actions: [
393
393
  {
394
394
  action: "send_message",
@@ -428,7 +428,7 @@ var loggingCommands = [
428
428
  actions: [
429
429
  {
430
430
  action: "flow_if",
431
- condition: '${!args.category || args.category === "all"}',
431
+ condition: '!args.category || args.category === "all"',
432
432
  then: [
433
433
  {
434
434
  action: "set",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/logging/index.ts"],"sourcesContent":["/**\n * Logging builtin module\n * Handles comprehensive server logging for messages, members, voice, and moderation\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler } from '@furlow/schema';\n\nexport interface LoggingConfig {\n /** Default log channel */\n channel?: string;\n /** Separate channels per category */\n channels?: {\n messages?: string;\n members?: string;\n voice?: string;\n server?: string;\n moderation?: string;\n };\n /** Ignored channels */\n ignoredChannels?: string[];\n /** Ignored roles (users with these roles won't be logged) */\n ignoredRoles?: string[];\n /** Events to log */\n events?: {\n messageDelete?: boolean;\n messageEdit?: boolean;\n messageBulkDelete?: boolean;\n memberJoin?: boolean;\n memberLeave?: boolean;\n memberUpdate?: boolean;\n memberBan?: boolean;\n memberUnban?: boolean;\n voiceJoin?: boolean;\n voiceLeave?: boolean;\n voiceMove?: boolean;\n channelCreate?: boolean;\n channelDelete?: boolean;\n channelUpdate?: boolean;\n roleCreate?: boolean;\n roleDelete?: boolean;\n roleUpdate?: boolean;\n inviteCreate?: boolean;\n inviteDelete?: boolean;\n };\n /** Include images in logs */\n includeImages?: boolean;\n}\n\nexport const loggingEventHandlers: EventHandler[] = [\n // Message Delete\n {\n event: 'message_delete',\n condition: '${config.logging.events?.messageDelete !== false && !config.logging.ignoredChannels?.includes(channel.id)}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Message Deleted',\n description: '${message.content || \"*No text content*\"}',\n color: '#ed4245',\n fields: [\n { name: 'Author', value: '${message.author ? \"<@\" + message.author.id + \">\" : \"Unknown\"}', inline: true },\n { name: 'Channel', value: '<#${channel.id}>', inline: true },\n { name: 'Message ID', value: '${message.id}', inline: true },\n ],\n footer: {\n text: 'User ID: ${message.author?.id || \"Unknown\"}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Message Edit\n {\n event: 'message_update',\n condition: '${config.logging.events?.messageEdit !== false && oldMessage.content !== newMessage.content && !config.logging.ignoredChannels?.includes(channel.id)}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Message Edited',\n color: '#fee75c',\n fields: [\n { name: 'Before', value: '${truncate(oldMessage.content || \"*Empty*\", 1024)}' },\n { name: 'After', value: '${truncate(newMessage.content || \"*Empty*\", 1024)}' },\n { name: 'Author', value: '<@${newMessage.author.id}>', inline: true },\n { name: 'Channel', value: '<#${channel.id}>', inline: true },\n { name: 'Jump to Message', value: '[Click here](${newMessage.url})', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMessage.author.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Bulk Message Delete\n {\n event: 'message_bulk_delete',\n condition: '${config.logging.events?.messageBulkDelete !== false}',\n actions: [\n {\n action: 'set',\n key: 'logContent',\n value: '${messages | map(m => \"[\" + (m.author?.tag || \"Unknown\") + \"]: \" + (m.content || \"*attachment/embed*\")) | join(\"\\\\n\")}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Bulk Messages Deleted',\n description: '${messages.length} messages were deleted in <#${channel.id}>',\n color: '#ed4245',\n timestamp: '${now()}',\n },\n files: [\n {\n attachment: '${Buffer.from(logContent)}',\n name: 'deleted-messages.txt',\n },\n ],\n },\n ],\n },\n // Member Join\n {\n event: 'member_join',\n condition: '${config.logging.events?.memberJoin !== false}',\n actions: [\n {\n action: 'set',\n key: 'accountAge',\n value: '${floor((now() - member.user.createdAt) / (1000 * 60 * 60 * 24))}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Joined',\n description: '<@${member.id}> joined the server',\n color: '#57f287',\n thumbnail: '${member.user.avatarURL}',\n fields: [\n { name: 'Username', value: '${member.user.tag}', inline: true },\n { name: 'Account Age', value: '${accountAge} days', inline: true },\n { name: 'Member Count', value: '${guild.memberCount}', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Leave\n {\n event: 'member_leave',\n condition: '${config.logging.events?.memberLeave !== false}',\n actions: [\n {\n action: 'set',\n key: 'roles',\n value: '${member.roles?.cache?.filter(r => r.id !== guild.id)?.map(r => r.name)?.join(\", \") || \"None\"}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Left',\n description: '<@${member.id}> left the server',\n color: '#ed4245',\n thumbnail: '${member.user.avatarURL}',\n fields: [\n { name: 'Username', value: '${member.user.tag}', inline: true },\n { name: 'Roles', value: '${truncate(roles, 1024)}', inline: false },\n { name: 'Joined At', value: '${timestamp(member.joinedAt, \"R\")}', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Update (roles, nickname)\n {\n event: 'member_update',\n condition: '${config.logging.events?.memberUpdate !== false}',\n actions: [\n // Role changes\n {\n action: 'flow_if',\n condition: '${oldMember.roles.cache.size !== newMember.roles.cache.size}',\n then: [\n {\n action: 'set',\n key: 'addedRoles',\n value: '${newMember.roles.cache.filter(r => !oldMember.roles.cache.has(r.id)).map(r => r.name).join(\", \")}',\n },\n {\n action: 'set',\n key: 'removedRoles',\n value: '${oldMember.roles.cache.filter(r => !newMember.roles.cache.has(r.id)).map(r => r.name).join(\", \")}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Roles Updated',\n description: '<@${newMember.id}>\\'s roles were changed',\n color: '#5865f2',\n fields: [\n { name: 'Added Roles', value: '${addedRoles || \"None\"}', inline: true },\n { name: 'Removed Roles', value: '${removedRoles || \"None\"}', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMember.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Nickname changes\n {\n action: 'flow_if',\n condition: '${oldMember.nickname !== newMember.nickname}',\n then: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Nickname Changed',\n description: '<@${newMember.id}>\\'s nickname was changed',\n color: '#5865f2',\n fields: [\n { name: 'Before', value: '${oldMember.nickname || oldMember.user.username}', inline: true },\n { name: 'After', value: '${newMember.nickname || newMember.user.username}', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMember.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n ],\n },\n // Member Ban\n {\n event: 'member_ban',\n condition: '${config.logging.events?.memberBan !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.moderation || config.logging.channel}',\n embed: {\n title: 'Member Banned',\n description: '<@${user.id}> was banned',\n color: '#ed4245',\n thumbnail: '${user.avatarURL}',\n fields: [\n { name: 'Username', value: '${user.tag}', inline: true },\n { name: 'Reason', value: '${ban.reason || \"No reason provided\"}', inline: false },\n ],\n footer: {\n text: 'User ID: ${user.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Unban\n {\n event: 'member_unban',\n condition: '${config.logging.events?.memberUnban !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.moderation || config.logging.channel}',\n embed: {\n title: 'Member Unbanned',\n description: '<@${user.id}> was unbanned',\n color: '#57f287',\n thumbnail: '${user.avatarURL}',\n fields: [\n { name: 'Username', value: '${user.tag}', inline: true },\n ],\n footer: {\n text: 'User ID: ${user.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Join\n {\n event: 'voice_join',\n condition: '${config.logging.events?.voiceJoin !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Joined',\n description: '<@${member.id}> joined <#${channel.id}>',\n color: '#57f287',\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Leave\n {\n event: 'voice_leave',\n condition: '${config.logging.events?.voiceLeave !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Left',\n description: '<@${member.id}> left <#${channel.id}>',\n color: '#ed4245',\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Move\n {\n event: 'voice_move',\n condition: '${config.logging.events?.voiceMove !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Switch',\n description: '<@${member.id}> switched voice channels',\n color: '#5865f2',\n fields: [\n { name: 'From', value: '<#${oldChannel.id}>', inline: true },\n { name: 'To', value: '<#${newChannel.id}>', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Channel Create\n {\n event: 'channel_create',\n condition: '${config.logging.events?.channelCreate !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Channel Created',\n description: '<#${channel.id}> was created',\n color: '#57f287',\n fields: [\n { name: 'Name', value: '${channel.name}', inline: true },\n { name: 'Type', value: '${channel.type}', inline: true },\n ],\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Channel Delete\n {\n event: 'channel_delete',\n condition: '${config.logging.events?.channelDelete !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Channel Deleted',\n description: '#${channel.name} was deleted',\n color: '#ed4245',\n fields: [\n { name: 'Type', value: '${channel.type}', inline: true },\n ],\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Role Create\n {\n event: 'role_create',\n condition: '${config.logging.events?.roleCreate !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Role Created',\n description: '${role.name} was created',\n color: '${role.hexColor || \"#57f287\"}',\n fields: [\n { name: 'Color', value: '${role.hexColor || \"None\"}', inline: true },\n { name: 'Hoisted', value: '${role.hoist ? \"Yes\" : \"No\"}', inline: true },\n { name: 'Mentionable', value: '${role.mentionable ? \"Yes\" : \"No\"}', inline: true },\n ],\n footer: {\n text: 'Role ID: ${role.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Role Delete\n {\n event: 'role_delete',\n condition: '${config.logging.events?.roleDelete !== false}',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Role Deleted',\n description: '${role.name} was deleted',\n color: '#ed4245',\n footer: {\n text: 'Role ID: ${role.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n];\n\nexport const loggingCommands: CommandDefinition[] = [\n {\n name: 'logging',\n description: 'Logging configuration commands',\n subcommands: [\n {\n name: 'set-channel',\n description: 'Set the logging channel',\n options: [\n { name: 'channel', description: 'Channel for logs', type: 'channel', required: true },\n { name: 'category', description: 'Log category', type: 'string', required: false, choices: [\n { name: 'All', value: 'all' },\n { name: 'Messages', value: 'messages' },\n { name: 'Members', value: 'members' },\n { name: 'Voice', value: 'voice' },\n { name: 'Server', value: 'server' },\n { name: 'Moderation', value: 'moderation' },\n ]},\n ],\n actions: [\n {\n action: 'flow_if',\n condition: '${!args.category || args.category === \"all\"}',\n then: [\n {\n action: 'set',\n key: 'config.logging.channel',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n ],\n else: [\n {\n action: 'set',\n key: 'config.logging.channels.${args.category}',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n ],\n },\n {\n action: 'reply',\n content: 'Logging channel updated!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'toggle',\n description: 'Toggle a logging event',\n options: [\n { name: 'event', description: 'Event to toggle', type: 'string', required: true, choices: [\n { name: 'Message Delete', value: 'messageDelete' },\n { name: 'Message Edit', value: 'messageEdit' },\n { name: 'Member Join', value: 'memberJoin' },\n { name: 'Member Leave', value: 'memberLeave' },\n { name: 'Member Ban', value: 'memberBan' },\n { name: 'Voice Join', value: 'voiceJoin' },\n { name: 'Voice Leave', value: 'voiceLeave' },\n ]},\n { name: 'enabled', description: 'Enable or disable', type: 'boolean', required: true },\n ],\n actions: [\n {\n action: 'set',\n key: 'config.logging.events.${args.event}',\n value: '${args.enabled}',\n scope: 'guild',\n },\n {\n action: 'reply',\n content: '${args.event} logging ${args.enabled ? \"enabled\" : \"disabled\"}!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'ignore-channel',\n description: 'Ignore a channel from logging',\n options: [\n { name: 'channel', description: 'Channel to ignore', type: 'channel', required: true },\n ],\n actions: [\n {\n action: 'list_push',\n key: 'config.logging.ignoredChannels',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n {\n action: 'reply',\n content: '${args.channel} will now be ignored from logs.',\n ephemeral: true,\n },\n ],\n },\n ],\n },\n];\n\nexport function getLoggingSpec(config: LoggingConfig = {}): Partial<FurlowSpec> {\n return {\n events: loggingEventHandlers,\n commands: loggingCommands,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/logging/index.ts"],"sourcesContent":["/**\n * Logging builtin module\n * Handles comprehensive server logging for messages, members, voice, and moderation\n */\n\nimport type { FurlowSpec, CommandDefinition, EventHandler } from '@furlow/schema';\n\nexport interface LoggingConfig {\n /** Default log channel */\n channel?: string;\n /** Separate channels per category */\n channels?: {\n messages?: string;\n members?: string;\n voice?: string;\n server?: string;\n moderation?: string;\n };\n /** Ignored channels */\n ignoredChannels?: string[];\n /** Ignored roles (users with these roles won't be logged) */\n ignoredRoles?: string[];\n /** Events to log */\n events?: {\n messageDelete?: boolean;\n messageEdit?: boolean;\n messageBulkDelete?: boolean;\n memberJoin?: boolean;\n memberLeave?: boolean;\n memberUpdate?: boolean;\n memberBan?: boolean;\n memberUnban?: boolean;\n voiceJoin?: boolean;\n voiceLeave?: boolean;\n voiceMove?: boolean;\n channelCreate?: boolean;\n channelDelete?: boolean;\n channelUpdate?: boolean;\n roleCreate?: boolean;\n roleDelete?: boolean;\n roleUpdate?: boolean;\n inviteCreate?: boolean;\n inviteDelete?: boolean;\n };\n /** Include images in logs */\n includeImages?: boolean;\n}\n\nexport const loggingEventHandlers: EventHandler[] = [\n // Message Delete\n {\n event: 'message_delete',\n condition: 'config.logging.events?.messageDelete !== false && !config.logging.ignoredChannels?.includes(channel.id)',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Message Deleted',\n description: '${message.content || \"*No text content*\"}',\n color: '#ed4245',\n fields: [\n { name: 'Author', value: '${message.author ? \"<@\" + message.author.id + \">\" : \"Unknown\"}', inline: true },\n { name: 'Channel', value: '<#${channel.id}>', inline: true },\n { name: 'Message ID', value: '${message.id}', inline: true },\n ],\n footer: {\n text: 'User ID: ${message.author?.id || \"Unknown\"}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Message Edit\n {\n event: 'message_update',\n condition: 'config.logging.events?.messageEdit !== false && oldMessage.content !== newMessage.content && !config.logging.ignoredChannels?.includes(channel.id)',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Message Edited',\n color: '#fee75c',\n fields: [\n { name: 'Before', value: '${truncate(oldMessage.content || \"*Empty*\", 1024)}' },\n { name: 'After', value: '${truncate(newMessage.content || \"*Empty*\", 1024)}' },\n { name: 'Author', value: '<@${newMessage.author.id}>', inline: true },\n { name: 'Channel', value: '<#${channel.id}>', inline: true },\n { name: 'Jump to Message', value: '[Click here](${newMessage.url})', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMessage.author.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Bulk Message Delete\n {\n event: 'message_bulk_delete',\n condition: 'config.logging.events?.messageBulkDelete !== false',\n actions: [\n {\n action: 'set',\n key: 'logContent',\n value: '${messages | map(m => \"[\" + (m.author?.tag || \"Unknown\") + \"]: \" + (m.content || \"*attachment/embed*\")) | join(\"\\\\n\")}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.messages || config.logging.channel}',\n embed: {\n title: 'Bulk Messages Deleted',\n description: '${messages.length} messages were deleted in <#${channel.id}>',\n color: '#ed4245',\n timestamp: '${now()}',\n },\n files: [\n {\n attachment: '${Buffer.from(logContent)}',\n name: 'deleted-messages.txt',\n },\n ],\n },\n ],\n },\n // Member Join\n {\n event: 'member_join',\n condition: 'config.logging.events?.memberJoin !== false',\n actions: [\n {\n action: 'set',\n key: 'accountAge',\n value: '${floor((now() - member.joined_at) / (1000 * 60 * 60 * 24))}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Joined',\n description: '<@${member.id}> joined the server',\n color: '#57f287',\n thumbnail: '${member.avatar}',\n fields: [\n { name: 'Username', value: '${member.user.tag}', inline: true },\n { name: 'Account Age', value: '${accountAge} days', inline: true },\n { name: 'Member Count', value: '${guild.member_count}', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Leave\n {\n event: 'member_leave',\n condition: 'config.logging.events?.memberLeave !== false',\n actions: [\n {\n action: 'set',\n key: 'roles',\n value: '${member.roles?.cache?.filter(r => r.id !== guild.id)?.map(r => r.name)?.join(\", \") || \"None\"}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Left',\n description: '<@${member.id}> left the server',\n color: '#ed4245',\n thumbnail: '${member.avatar}',\n fields: [\n { name: 'Username', value: '${member.user.tag}', inline: true },\n { name: 'Roles', value: '${truncate(roles, 1024)}', inline: false },\n { name: 'Joined At', value: '${timestamp(member.joined_at, \"R\")}', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Update (roles, nickname)\n {\n event: 'member_update',\n condition: 'config.logging.events?.memberUpdate !== false',\n actions: [\n // Role changes\n {\n action: 'flow_if',\n condition: 'oldMember.roles.cache.size !== newMember.roles.cache.size',\n then: [\n {\n action: 'set',\n key: 'addedRoles',\n value: '${newMember.roles.cache.filter(r => !oldMember.roles.cache.has(r.id)).map(r => r.name).join(\", \")}',\n },\n {\n action: 'set',\n key: 'removedRoles',\n value: '${oldMember.roles.cache.filter(r => !newMember.roles.cache.has(r.id)).map(r => r.name).join(\", \")}',\n },\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Member Roles Updated',\n description: '<@${newMember.id}>\\'s roles were changed',\n color: '#5865f2',\n fields: [\n { name: 'Added Roles', value: '${addedRoles || \"None\"}', inline: true },\n { name: 'Removed Roles', value: '${removedRoles || \"None\"}', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMember.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Nickname changes\n {\n action: 'flow_if',\n condition: 'oldMember.nickname !== newMember.nickname',\n then: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.members || config.logging.channel}',\n embed: {\n title: 'Nickname Changed',\n description: '<@${newMember.id}>\\'s nickname was changed',\n color: '#5865f2',\n fields: [\n { name: 'Before', value: '${oldMember.nickname || oldMember.user.username}', inline: true },\n { name: 'After', value: '${newMember.nickname || newMember.user.username}', inline: true },\n ],\n footer: {\n text: 'User ID: ${newMember.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n ],\n },\n // Member Ban\n {\n event: 'member_ban',\n condition: 'config.logging.events?.memberBan !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.moderation || config.logging.channel}',\n embed: {\n title: 'Member Banned',\n description: '<@${user.id}> was banned',\n color: '#ed4245',\n thumbnail: '${user.avatar}',\n fields: [\n { name: 'Username', value: '${user.tag}', inline: true },\n { name: 'Reason', value: '${ban.reason || \"No reason provided\"}', inline: false },\n ],\n footer: {\n text: 'User ID: ${user.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Member Unban\n {\n event: 'member_unban',\n condition: 'config.logging.events?.memberUnban !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.moderation || config.logging.channel}',\n embed: {\n title: 'Member Unbanned',\n description: '<@${user.id}> was unbanned',\n color: '#57f287',\n thumbnail: '${user.avatar}',\n fields: [\n { name: 'Username', value: '${user.tag}', inline: true },\n ],\n footer: {\n text: 'User ID: ${user.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Join\n {\n event: 'voice_join',\n condition: 'config.logging.events?.voiceJoin !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Joined',\n description: '<@${member.id}> joined <#${channel.id}>',\n color: '#57f287',\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Leave\n {\n event: 'voice_leave',\n condition: 'config.logging.events?.voiceLeave !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Left',\n description: '<@${member.id}> left <#${channel.id}>',\n color: '#ed4245',\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Voice Move\n {\n event: 'voice_move',\n condition: 'config.logging.events?.voiceMove !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.voice || config.logging.channel}',\n embed: {\n title: 'Voice Channel Switch',\n description: '<@${member.id}> switched voice channels',\n color: '#5865f2',\n fields: [\n { name: 'From', value: '<#${oldChannel.id}>', inline: true },\n { name: 'To', value: '<#${newChannel.id}>', inline: true },\n ],\n footer: {\n text: 'User ID: ${member.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Channel Create\n {\n event: 'channel_create',\n condition: 'config.logging.events?.channelCreate !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Channel Created',\n description: '<#${channel.id}> was created',\n color: '#57f287',\n fields: [\n { name: 'Name', value: '${channel.name}', inline: true },\n { name: 'Type', value: '${channel.type}', inline: true },\n ],\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Channel Delete\n {\n event: 'channel_delete',\n condition: 'config.logging.events?.channelDelete !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Channel Deleted',\n description: '#${channel.name} was deleted',\n color: '#ed4245',\n fields: [\n { name: 'Type', value: '${channel.type}', inline: true },\n ],\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Role Create\n {\n event: 'role_create',\n condition: 'config.logging.events?.roleCreate !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Role Created',\n description: '${role.name} was created',\n color: '${role.hexColor || \"#57f287\"}',\n fields: [\n { name: 'Color', value: '${role.hexColor || \"None\"}', inline: true },\n { name: 'Hoisted', value: '${role.hoist ? \"Yes\" : \"No\"}', inline: true },\n { name: 'Mentionable', value: '${role.mentionable ? \"Yes\" : \"No\"}', inline: true },\n ],\n footer: {\n text: 'Role ID: ${role.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n // Role Delete\n {\n event: 'role_delete',\n condition: 'config.logging.events?.roleDelete !== false',\n actions: [\n {\n action: 'send_message',\n channel: '${config.logging.channels?.server || config.logging.channel}',\n embed: {\n title: 'Role Deleted',\n description: '${role.name} was deleted',\n color: '#ed4245',\n footer: {\n text: 'Role ID: ${role.id}',\n },\n timestamp: '${now()}',\n },\n },\n ],\n },\n];\n\nexport const loggingCommands: CommandDefinition[] = [\n {\n name: 'logging',\n description: 'Logging configuration commands',\n subcommands: [\n {\n name: 'set-channel',\n description: 'Set the logging channel',\n options: [\n { name: 'channel', description: 'Channel for logs', type: 'channel', required: true },\n { name: 'category', description: 'Log category', type: 'string', required: false, choices: [\n { name: 'All', value: 'all' },\n { name: 'Messages', value: 'messages' },\n { name: 'Members', value: 'members' },\n { name: 'Voice', value: 'voice' },\n { name: 'Server', value: 'server' },\n { name: 'Moderation', value: 'moderation' },\n ]},\n ],\n actions: [\n {\n action: 'flow_if',\n condition: '!args.category || args.category === \"all\"',\n then: [\n {\n action: 'set',\n key: 'config.logging.channel',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n ],\n else: [\n {\n action: 'set',\n key: 'config.logging.channels.${args.category}',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n ],\n },\n {\n action: 'reply',\n content: 'Logging channel updated!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'toggle',\n description: 'Toggle a logging event',\n options: [\n { name: 'event', description: 'Event to toggle', type: 'string', required: true, choices: [\n { name: 'Message Delete', value: 'messageDelete' },\n { name: 'Message Edit', value: 'messageEdit' },\n { name: 'Member Join', value: 'memberJoin' },\n { name: 'Member Leave', value: 'memberLeave' },\n { name: 'Member Ban', value: 'memberBan' },\n { name: 'Voice Join', value: 'voiceJoin' },\n { name: 'Voice Leave', value: 'voiceLeave' },\n ]},\n { name: 'enabled', description: 'Enable or disable', type: 'boolean', required: true },\n ],\n actions: [\n {\n action: 'set',\n key: 'config.logging.events.${args.event}',\n value: '${args.enabled}',\n scope: 'guild',\n },\n {\n action: 'reply',\n content: '${args.event} logging ${args.enabled ? \"enabled\" : \"disabled\"}!',\n ephemeral: true,\n },\n ],\n },\n {\n name: 'ignore-channel',\n description: 'Ignore a channel from logging',\n options: [\n { name: 'channel', description: 'Channel to ignore', type: 'channel', required: true },\n ],\n actions: [\n {\n action: 'list_push',\n key: 'config.logging.ignoredChannels',\n value: '${args.channel.id}',\n scope: 'guild',\n },\n {\n action: 'reply',\n content: '${args.channel} will now be ignored from logs.',\n ephemeral: true,\n },\n ],\n },\n ],\n },\n];\n\nexport function getLoggingSpec(config: LoggingConfig = {}): Partial<FurlowSpec> {\n return {\n events: loggingEventHandlers,\n commands: loggingCommands,\n };\n}\n"],"mappings":"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","names":[]}