chrxmaticc-framework 1.1.4 → 1.2.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/README.md CHANGED
@@ -183,4 +183,78 @@ AI_KEY=your_ai_key
183
183
  ```
184
184
  ---
185
185
 
186
- ## Made by Chrxmee-Bits
186
+ ## Slash Command Addon
187
+
188
+ ## chrxmaticc-framework v2.0.0
189
+
190
+ ### What's new
191
+
192
+ **ChrxCommandBuilder** — write commands in as little as 6 lines.
193
+
194
+ No more `SlashCommandBuilder` boilerplate, no more manual `module.exports = { data, execute }`, no more writing cooldowns and permission checks yourself. Just name, description, and your logic.
195
+
196
+ ### How it works
197
+ ```js
198
+ const { ChrxCommandBuilder } = require("chrxmaticc-framework");
199
+
200
+ module.exports = new ChrxCommandBuilder({
201
+ name: "balance",
202
+ description: "Check your coin balance",
203
+ cooldown: 5,
204
+ options: [
205
+ { name: "user", description: "User to check", type: "user", required: false }
206
+ ],
207
+ async run(interaction, { economy }) {
208
+ const target = interaction.options.getUser("user") ?? interaction.user;
209
+ const data = await economy.getBalance(target.id, interaction.guild.id);
210
+ interaction.reply(`💰 **${target.username}** — ${data.balance} coins`);
211
+ }
212
+ });
213
+ ```
214
+
215
+ ### Features
216
+
217
+ - **Auto option type mapping** — write `"user"` instead of `ApplicationCommandOptionType.User`. Supports `string`, `number`, `integer`, `boolean`, `user`, `channel`, `role`, `mentionable`, `attachment`
218
+ - **Built-in cooldowns** — just pass `cooldown: 5` (seconds), handled automatically per user
219
+ - **Built-in permission checks** — pass `permission: "BanMembers"` and it checks before running
220
+ - **Built-in error handling** — any error in `run()` is caught and sends a clean error message automatically. Custom errors via `throw new Error("your message")`
221
+ - **Plugin injection** — all plugins injected into `run()` automatically, no manual requires needed. Access `economy`, `moderation`, `giveaways`, `tickets`, `welcome`, `polls`, `reminders`, `starboard`, `automod`, `xp`, `ai`, `db`
222
+ - **Owner only** — pass `ownerOnly: true` to restrict a command to the bot owner
223
+ - **Choices** — pass `choices: [{ name, value }]` on string/integer/number options for dropdown menus
224
+ - **Still fully flexible** — `run()` is raw discord.js, no ceiling, do anything you could do in a normal command
225
+
226
+ ### Changes
227
+ - Added `ChrxCommandBuilder` to core
228
+ - Updated `Client.js` with `registerPlugin()` method for automatic plugin injection
229
+ - Updated `index.js` to export `ChrxCommandBuilder`
230
+ - Added `GuildMessageReactions` intent and `Partials.Reaction` automatically for Starboard plugin
231
+
232
+ ### Breaking changes
233
+ - None — all v1.x code works as is
234
+
235
+ ### Migration from raw commands
236
+
237
+ **Before:**
238
+ ```js
239
+ const { SlashCommandBuilder } = require("discord.js");
240
+ module.exports = {
241
+ data: new SlashCommandBuilder()
242
+ .setName("ping")
243
+ .setDescription("Pong!"),
244
+ async execute(interaction) {
245
+ interaction.reply("Pong!");
246
+ }
247
+ }
248
+ ```
249
+
250
+ **After:**
251
+ ```js
252
+ module.exports = new ChrxCommandBuilder({
253
+ name: "ping",
254
+ description: "Pong!",
255
+ cooldown: 5,
256
+ async run(interaction) {
257
+ interaction.reply("Pong!");
258
+ }
259
+ });
260
+ ```
package/core/AIWrapper.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * src/modules/AIWrapper.js
3
- * Lightweight wrapper for AI completions.
4
- * Pass your API key and model in the options.
3
+ * Lightweight wrapper for AI completions. Nothing big.
4
+ * Put in your API key and model in the options.
5
5
  */
6
6
 
7
7
  class AIWrapper {
@@ -70,4 +70,4 @@ class AIWrapper {
70
70
  }
71
71
  }
72
72
 
73
- module.exports = AIWrapper;
73
+ module.exports = AIWrapper;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * core/ChrxCommandBuilder.js
3
+ * Simple command builder — write a command in 10 lines.
4
+ * Handles SlashCommandBuilder, option types, and plugin injection automatically.
5
+ */
6
+
7
+ const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js");
8
+
9
+ // ── Option type map ───────────────────────────────────────────────────────
10
+ // Write "user" instead of ApplicationCommandOptionType.User etc.
11
+ const TYPE_MAP = {
12
+ string: "addStringOption",
13
+ number: "addNumberOption",
14
+ integer: "addIntegerOption",
15
+ boolean: "addBooleanOption",
16
+ user: "addUserOption",
17
+ channel: "addChannelOption",
18
+ role: "addRoleOption",
19
+ mentionable: "addMentionableOption",
20
+ attachment: "addAttachmentOption",
21
+ };
22
+
23
+ class ChrxCommandBuilder {
24
+ /**
25
+ * @param {object} options
26
+ * @param {string} options.name Command name (lowercase, no spaces)
27
+ * @param {string} options.description Command description
28
+ * @param {object[]} [options.options] Command options/arguments
29
+ * @param {string} options.options[].name Option name
30
+ * @param {string} options.options[].description Option description
31
+ * @param {string} options.options[].type Option type — "string" | "number" | "integer" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment"
32
+ * @param {boolean} [options.options[].required] Whether option is required (default: false)
33
+ * @param {Array} [options.options[].choices] Choices array [{ name, value }]
34
+ * @param {string} [options.category] Category label for help menus
35
+ * @param {string} [options.permission] Required permission e.g. "BanMembers"
36
+ * @param {boolean} [options.ownerOnly] Restrict to bot owner
37
+ * @param {boolean} [options.guildOnly] Restrict to guilds only (default: true)
38
+ * @param {number} [options.cooldown] Cooldown in seconds
39
+ * @param {Function} options.run Command function (interaction, plugins) => void
40
+ */
41
+ constructor(options = {}) {
42
+ if (!options.name) throw new Error("[ChrxCommand] name is required.");
43
+ if (!options.description) throw new Error("[ChrxCommand] description is required.");
44
+ if (!options.run) throw new Error("[ChrxCommand] run function is required.");
45
+
46
+ this._options = options;
47
+ this._cooldowns = new Map();
48
+
49
+ // ── Build SlashCommandBuilder ─────────────────────────────────────────
50
+ const builder = new SlashCommandBuilder()
51
+ .setName(options.name)
52
+ .setDescription(options.description);
53
+
54
+ if (options.permission) {
55
+ builder.setDefaultMemberPermissions(PermissionFlagsBits[options.permission]);
56
+ }
57
+
58
+ if (options.guildOnly !== false) {
59
+ builder.setDMPermission(false);
60
+ }
61
+
62
+ // ── Add options ───────────────────────────────────────────────────────
63
+ for (const opt of options.options ?? []) {
64
+ const method = TYPE_MAP[opt.type ?? "string"];
65
+ if (!method) throw new Error(`[ChrxCommand] Unknown option type: "${opt.type}"`);
66
+
67
+ builder[method]((o) => {
68
+ o.setName(opt.name).setDescription(opt.description);
69
+ if (opt.required) o.setRequired(true);
70
+ if (opt.choices && (opt.type === "string" || opt.type === "integer" || opt.type === "number")) {
71
+ o.addChoices(...opt.choices);
72
+ }
73
+ return o;
74
+ });
75
+ }
76
+
77
+ this.data = builder;
78
+ }
79
+
80
+ /**
81
+ * Called by CommandLoader automatically.
82
+ * Handles cooldowns, permissions, ownerOnly, then calls run().
83
+ */
84
+ async execute(interaction) {
85
+ const client = interaction.client;
86
+ const opts = this._options;
87
+
88
+ // ── Guild only ────────────────────────────────────────────────────────
89
+ if (opts.guildOnly !== false && !interaction.guild) {
90
+ return interaction.reply({ content: "❌ This command can only be used in a server.", ephemeral: true });
91
+ }
92
+
93
+ // ── Owner only ────────────────────────────────────────────────────────
94
+ if (opts.ownerOnly && interaction.user.id !== process.env.OWNER_ID) {
95
+ return interaction.reply({ content: "❌ This command is restricted to the bot owner.", ephemeral: true });
96
+ }
97
+
98
+ // ── Permission check ──────────────────────────────────────────────────
99
+ if (opts.permission) {
100
+ const member = interaction.member;
101
+ if (!member?.permissions.has(PermissionFlagsBits[opts.permission])) {
102
+ return interaction.reply({ content: `❌ You need the **${opts.permission}** permission to use this.`, ephemeral: true });
103
+ }
104
+ }
105
+
106
+ // ── Cooldown ──────────────────────────────────────────────────────────
107
+ if (opts.cooldown) {
108
+ const key = `${interaction.user.id}-${opts.name}`;
109
+ const now = Date.now();
110
+ if (this._cooldowns.has(key)) {
111
+ const remaining = (opts.cooldown * 1000) - (now - this._cooldowns.get(key));
112
+ if (remaining > 0) {
113
+ return interaction.reply({
114
+ content: `⏱ Slow down! Wait **${(remaining / 1000).toFixed(1)}s** before using this again.`,
115
+ ephemeral: true,
116
+ });
117
+ }
118
+ }
119
+ this._cooldowns.set(key, now);
120
+ setTimeout(() => this._cooldowns.delete(key), opts.cooldown * 1000);
121
+ }
122
+
123
+ // ── Inject plugins from client ────────────────────────────────────────
124
+ // Plugins are attached to client.chrx.* if using ChrxClient
125
+ const plugins = {
126
+ economy: client.chrx?.economy,
127
+ moderation: client.chrx?.moderation,
128
+ giveaways: client.chrx?.giveaways,
129
+ tickets: client.chrx?.tickets,
130
+ welcome: client.chrx?.welcome,
131
+ polls: client.chrx?.polls,
132
+ reminders: client.chrx?.reminders,
133
+ starboard: client.chrx?.starboard,
134
+ automod: client.chrx?.automod,
135
+ xp: client.chrx?.xp,
136
+ ai: client.chrx?.ai,
137
+ db: client.db,
138
+ };
139
+
140
+ // ── Run ───────────────────────────────────────────────────────────────
141
+ try {
142
+ await opts.run(interaction, plugins);
143
+ } catch (err) {
144
+ console.error(`[ChrxCommand] Error in /${opts.name}:`, err);
145
+ const msg = { content: "❌ Something went wrong running this command.", ephemeral: true };
146
+ if (interaction.replied || interaction.deferred) {
147
+ interaction.followUp(msg).catch(() => {});
148
+ } else {
149
+ interaction.reply(msg).catch(() => {});
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ module.exports = ChrxCommandBuilder;
package/core/Client.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * src/core/Client.js
3
- * The main ChrxClient class — wraps discord.js Client with
4
- * auto loaders, Lavalink, and optional modules.
2
+ * core/Client.js
3
+ * The main ChrxClient class.
5
4
  */
5
+
6
6
  require("dotenv").config();
7
7
  const { Client, GatewayIntentBits, Collection, Partials } = require("discord.js");
8
8
  const { LavalinkManager } = require("lavalink-client");
@@ -14,29 +14,12 @@ const XPSystem = require("./XPSystem");
14
14
  const AIWrapper = require("./AIWrapper");
15
15
 
16
16
  class ChrxClient {
17
- /**
18
- * @param {object} options
19
- * @param {string} options.token - Bot token
20
- * @param {string} [options.commandsPath] - Path to commands folder (default: ./commands)
21
- * @param {string} [options.eventsPath] - Path to events folder (default: ./events)
22
- * @param {object} [options.lavalink] - Lavalink config
23
- * @param {string} options.lavalink.host
24
- * @param {number} [options.lavalink.port]
25
- * @param {string} options.lavalink.password
26
- * @param {boolean} [options.lavalink.secure]
27
- * @param {object} [options.modules] - Optional modules to enable
28
- * @param {boolean} [options.modules.music] - Enable music system
29
- * @param {boolean} [options.modules.xp] - Enable XP system
30
- * @param {boolean} [options.modules.ai] - Enable AI wrapper
31
- * @param {string} [options.modules.database] - Postgres connection string
32
- * @param {number[]} [options.intents] - Override default intents
33
- */
34
17
  constructor(options = {}) {
35
- if (!options.token) throw new Error("[ChrxClient] token is required.");
18
+ if (!options.token) throw new Error("[ChrxClient] token is required. Check your bot token or this must be a error with the package.");
36
19
 
37
20
  this._options = options;
38
21
 
39
- // ── Discord client ───────────────────────────────────────────────────
22
+ // ── Discord client ────────────────────────────────────────────────────
40
23
  this.client = new Client({
41
24
  intents: options.intents ?? [
42
25
  GatewayIntentBits.Guilds,
@@ -45,19 +28,32 @@ class ChrxClient {
45
28
  GatewayIntentBits.GuildMembers,
46
29
  GatewayIntentBits.GuildVoiceStates,
47
30
  GatewayIntentBits.DirectMessages,
31
+ GatewayIntentBits.GuildMessageReactions,
48
32
  ],
49
- partials: [Partials.Channel, Partials.Message],
33
+ partials: [Partials.Channel, Partials.Message, Partials.Reaction],
50
34
  });
51
35
 
52
36
  this.client.commands = new Collection();
53
- this.client.chrx = this; // expose framework on client for use in commands/events
54
-
55
- // ── Lavalink ─────────────────────────────────────────────────────────
37
+ this.client.chrx = this; // expose framework on client for plugin injection
38
+
39
+ // ── Plugin registry ───────────────────────────────────────────────────
40
+ // Plugins attach themselves here via registerPlugin()
41
+ this.economy = null;
42
+ this.moderation = null;
43
+ this.giveaways = null;
44
+ this.tickets = null;
45
+ this.welcome = null;
46
+ this.polls = null;
47
+ this.reminders = null;
48
+ this.starboard = null;
49
+ this.automod = null;
50
+
51
+ // ── Lavalink ──────────────────────────────────────────────────────────
56
52
  if (options.lavalink && options.modules?.music !== false) {
57
53
  this._setupLavalink(options.lavalink);
58
54
  }
59
55
 
60
- // ── Optional modules ─────────────────────────────────────────────────
56
+ // ── Built-in modules ──────────────────────────────────────────────────
61
57
  if (options.modules?.database) {
62
58
  this.db = new Database(options.modules.database);
63
59
  this.client.db = this.db;
@@ -74,9 +70,9 @@ class ChrxClient {
74
70
 
75
71
  // ── Loaders ───────────────────────────────────────────────────────────
76
72
  this._commandLoader = new CommandLoader(this.client, options.commandsPath);
77
- this._eventLoader = new EventLoader(this.client, options.eventsPath);
73
+ this._eventLoader = new EventLoader(this.client, options.eventsPath);
78
74
 
79
- // ── Ready event ───────────────────────────────────────────────────────
75
+ // ── Ready ─────────────────────────────────────────────────────────────
80
76
  this.client.once("ready", async () => {
81
77
  if (this.client.lavalink) {
82
78
  await this.client.lavalink.init({
@@ -94,7 +90,7 @@ class ChrxClient {
94
90
  console.log(`[ChrxClient] Ready! Logged in as ${this.client.user.tag}`);
95
91
  });
96
92
 
97
- // ── Voice state forwarding for Lavalink ───────────────────────────────
93
+ // ── Voice state forwarding ────────────────────────────────────────────
98
94
  this.client.on("raw", (d) => {
99
95
  if (d.t === "VOICE_SERVER_UPDATE" || d.t === "VOICE_STATE_UPDATE") {
100
96
  this.client.lavalink?.sendRawData(d);
@@ -111,8 +107,16 @@ class ChrxClient {
111
107
  }
112
108
 
113
109
  /**
114
- * Set up the LavalinkManager and attach it to the client.
110
+ * Register a plugin so it gets injected into ChrxCommand run() calls.
111
+ * Called automatically when you do new Economy(bot.client) etc.
112
+ * Or call manually: bot.registerPlugin("economy", economyInstance)
115
113
  */
114
+ registerPlugin(name, instance) {
115
+ this[name] = instance;
116
+ this.client.chrx[name] = instance;
117
+ console.log(`[ChrxClient] Plugin registered: ${name}`);
118
+ }
119
+
116
120
  _setupLavalink(cfg) {
117
121
  this.client.lavalink = new LavalinkManager({
118
122
  nodes: [
@@ -139,7 +143,6 @@ class ChrxClient {
139
143
  },
140
144
  });
141
145
 
142
- // Attach music manager (handles events + marker system)
143
146
  this.music = new MusicManager(this.client);
144
147
 
145
148
  this.client.lavalink.on("nodeConnect", (node) =>
@@ -150,9 +153,6 @@ class ChrxClient {
150
153
  );
151
154
  }
152
155
 
153
- /**
154
- * Load commands and events then log in.
155
- */
156
156
  async start() {
157
157
  this._commandLoader.load();
158
158
  this._eventLoader.load();
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * src/core/CommandLoader.js
3
3
  * Auto-loads slash commands from the user's commands folder.
4
+ * Also. Please have a deploy-commands.js file or anything that refreshes (loads) commands.
4
5
  */
5
6
 
6
7
  const fs = require("fs");
@@ -14,7 +15,7 @@ class CommandLoader {
14
15
 
15
16
  load() {
16
17
  if (!fs.existsSync(this.commandsPath)) {
17
- console.warn(`[CommandLoader] Commands folder not found at ${this.commandsPath}`);
18
+ console.warn(`[CommandLoader] Commands folder not found at ${this.commandsPath}. Make a commands folder with your remaining commands.`);
18
19
  return;
19
20
  }
20
21
 
@@ -48,4 +49,4 @@ class CommandLoader {
48
49
  }
49
50
  }
50
51
 
51
- module.exports = CommandLoader;
52
+ module.exports = CommandLoader;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * src/core/EventLoader.js
3
3
  * Auto-loads events from the user's events folder.
4
+ * Make a events folder, its not needed but can be useful for other materials if making a discord bot.
4
5
  */
5
6
 
6
7
  const fs = require("fs");
@@ -35,4 +36,4 @@ class EventLoader {
35
36
  }
36
37
  }
37
38
 
38
- module.exports = EventLoader;
39
+ module.exports = EventLoader;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * src/music/MusicManager.js
3
3
  * Handles all Lavalink events and wires in the song marker system.
4
+ * Make music commands, this wont have pre-commands. Make an /play command, /stop and extras if needed.
4
5
  */
5
6
 
6
7
  const { EmbedBuilder } = require("discord.js");
@@ -75,4 +76,4 @@ class MusicManager {
75
76
  }
76
77
  }
77
78
 
78
- module.exports = MusicManager;
79
+ module.exports = MusicManager;
package/core/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * src/index.js
3
3
  * Main entry point — exports everything the user needs.
4
+ * An duplicate of index.js. You can delete if you want.
4
5
  */
5
6
 
6
7
  const { ChrxClient } = require("./Client");
@@ -38,4 +39,4 @@ module.exports = {
38
39
  applyStartMarker,
39
40
  startEndMarkerWatcher,
40
41
  stopEndMarkerWatcher,
41
- };
42
+ };
@@ -3,7 +3,7 @@
3
3
  * Per-track start/end marker system — lavalink-client v2 compatible.
4
4
  *
5
5
  * Markers are stored in memory. They persist as long as the bot is running
6
- * but reset on restart. Swap the Map for Postgres if you want them permanent.
6
+ * but reset on restart. Swap the Map for Postgres or MongoDB if you want them permanent.
7
7
  */
8
8
 
9
9
  // Key: track URI (or title fallback) → { start?: ms, end?: ms, loop?: bool }
package/index.js CHANGED
@@ -1,10 +1,15 @@
1
- // FINALLY IT L0ADS FUCKING PLUGINS AND FUCKING CORE SHIT
2
- // Chrxmaticc Framework is so tuff gng💔
1
+ /**
2
+ * index.js (not for a discord bot)
3
+ * Main entry point, will ALWAYS export everything.
4
+ * Listen to Project4play / SVJ at SoundCloud NOW!
5
+ */
6
+
3
7
  const { ChrxClient } = require("./core/Client");
4
8
  const Database = require("./core/Database");
5
9
  const XPSystem = require("./core/XPSystem");
6
10
  const AIWrapper = require("./core/AIWrapper");
7
11
  const MusicManager = require("./core/MusicManager");
12
+ const ChrxCommandBuilder = require("./core/ChrxCommandBuilder");
8
13
  const {
9
14
  parseTime,
10
15
  formatTime,
@@ -17,22 +22,28 @@ const {
17
22
  } = require("./core/songMarkers");
18
23
 
19
24
  // ── Plugins ───────────────────────────────────────────────────────────────
20
- const Economy = require("./plugins/Economy");
25
+ const Economy = require("./plugins/Economy");
21
26
  const Moderation = require("./plugins/Moderation");
22
- const Giveaways = require("./plugins/Giveaways");
23
- const Tickets = require("./plugins/Tickets");
24
- const Welcome = require("./plugins/Welcome");
25
- const Polls = require("./plugins/Polls");
26
- const Reminders = require("./plugins/Reminders");
27
- const Starboard = require("./plugins/Starboard");
28
- const AutoMod = require("./plugins/AutoMod");
27
+ const Giveaways = require("./plugins/Giveaways");
28
+ const Tickets = require("./plugins/Tickets");
29
+ const Welcome = require("./plugins/Welcome");
30
+ const Polls = require("./plugins/Polls");
31
+ const Reminders = require("./plugins/Reminders");
32
+ const Starboard = require("./plugins/Starboard");
33
+ const AutoMod = require("./plugins/AutoMod");
29
34
 
30
35
  module.exports = {
36
+ // Core
31
37
  ChrxClient,
38
+ ChrxCommandBuilder,
39
+
40
+ // Modules
32
41
  Database,
33
42
  XPSystem,
34
43
  AIWrapper,
35
44
  MusicManager,
45
+
46
+ // Song marker utilities
36
47
  parseTime,
37
48
  formatTime,
38
49
  setMarker,
@@ -41,6 +52,8 @@ module.exports = {
41
52
  applyStartMarker,
42
53
  startEndMarkerWatcher,
43
54
  stopEndMarkerWatcher,
55
+
56
+ // Plugins
44
57
  Economy,
45
58
  Moderation,
46
59
  Giveaways,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrxmaticc-framework",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "A batteries-included Discord bot framework with music, AI, XP and database support.",
5
5
  "main": "index.js",
6
6
  "files": [