@marshmallow-stoat/mally 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # @marshmallow/mally
2
+
3
+ A high-performance, decorator-based command handler for the [Stoat](https://github.com/valarium/stoat.js) ecosystem. Inspired by [discordx](https://github.com/discordx-ts/discordx).
4
+
5
+ ## Features
6
+
7
+ - **Decorator-based** - Use `@Stoat()` and `@SimpleCommand()` decorators like discordx
8
+ - **Guards** - Built-in guard system for permissions and checks
9
+ - **Cooldowns** - Per-command cooldown support
10
+ - **Organized** - Group multiple commands in a single class
11
+ - **Type-safe** - Full TypeScript support
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @marshmallow/mally reflect-metadata
17
+ # or
18
+ pnpm add @marshmallow/mally reflect-metadata
19
+ ```
20
+
21
+ Make sure to enable decorators in your `tsconfig.json`:
22
+
23
+ ```json
24
+ {
25
+ "compilerOptions": {
26
+ "experimentalDecorators": true,
27
+ "emitDecoratorMetadata": true
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### 1. Create your handler
35
+
36
+ ```typescript
37
+ // index.ts
38
+ import 'reflect-metadata';
39
+ import { Client } from 'stoat.js';
40
+ import { MallyHandler } from '@marshmallow/mally';
41
+ import { join } from 'path';
42
+
43
+ const client = new Client();
44
+
45
+ const handler = new MallyHandler({
46
+ client,
47
+ prefix: '!',
48
+ owners: ['your-user-id'],
49
+ commandsDir: join(__dirname, "commands")
50
+ });
51
+
52
+ await handler.init();
53
+
54
+ client.on('messageCreate', (message) => {
55
+ handler.handleMessage(message);
56
+ });
57
+
58
+ client.login('your-token');
59
+ ```
60
+
61
+ ### 2. Create commands
62
+
63
+ ```typescript
64
+ // commands/general.ts
65
+ import { Stoat, SimpleCommand, Context } from '@marshmallow/mally';
66
+
67
+ @Stoat()
68
+ export class GeneralCommands {
69
+ @SimpleCommand({ name: 'ping', description: 'Check bot latency' })
70
+ async ping(ctx: Context) {
71
+ await ctx.reply(`Pong! 🏓`);
72
+ }
73
+
74
+ @SimpleCommand({ name: 'hello', aliases: ['hi', 'hey'] })
75
+ async hello(ctx: Context) {
76
+ await ctx.reply(`Hello, <@${ctx.authorId}>!`);
77
+ }
78
+ }
79
+ ```
80
+
81
+ That's it! No manual imports needed - commands are auto-discovered.
82
+
83
+ ## Decorators
84
+
85
+ ### @Stoat()
86
+
87
+ Marks a class as a command container. All `@SimpleCommand()` methods inside will be registered.
88
+
89
+ ```typescript
90
+ @Stoat()
91
+ export class MyCommands {
92
+ // commands go here
93
+ }
94
+ ```
95
+
96
+ ### @SimpleCommand(options)
97
+
98
+ Marks a method as a command.
99
+
100
+ ```typescript
101
+ @SimpleCommand({
102
+ name: 'ban', // Command name (defaults to method name)
103
+ description: 'Ban a user',
104
+ aliases: ['b'], // Alternative names
105
+ permissions: ['BanMembers'], // This is currently not implemented, but will be in the future
106
+ cooldown: 5000, // 5 seconds
107
+ ownerOnly: false,
108
+ nsfw: false,
109
+ })
110
+ async ban(ctx: Context) {
111
+ // ...
112
+ }
113
+ ```
114
+
115
+ ### @Guard(GuardClass)
116
+
117
+ Adds a guard check before command execution.
118
+
119
+ ```typescript
120
+ import { Stoat, SimpleCommand, Guard, MallyGuard, Context } from '@marshmallow/mally';
121
+
122
+ // Define a guard
123
+ class IsAdmin implements MallyGuard {
124
+ run(ctx: Context): boolean {
125
+ return ctx.message.member?.hasPermission('Administrator') ?? false;
126
+ }
127
+
128
+ guardFail(ctx: Context): void {
129
+ ctx.reply('You need Administrator permission!');
130
+ }
131
+ }
132
+
133
+ @Stoat()
134
+ @Guard(IsAdmin)
135
+ export class AdminCommands {
136
+ @SimpleCommand({ name: 'shutdown' })
137
+ async shutdown(ctx: Context) {
138
+ await ctx.reply('Shutting down...');
139
+ }
140
+ }
141
+ ```
142
+
143
+ ## Context
144
+
145
+ The `Context` object provides:
146
+
147
+ ```typescript
148
+ interface Context {
149
+ client: Client; // Stoat client instance
150
+ message: Message; // Original message
151
+ content: string; // Raw message content
152
+ authorId: string; // Author's user ID
153
+ channelId: string; // Channel ID
154
+ serverId?: string; // Server/Guild ID
155
+ args: string[]; // Parsed arguments
156
+ prefix: string; // Prefix used
157
+ commandName: string; // Command name used
158
+
159
+ reply(content: string): Promise<void>;
160
+ }
161
+ ```
162
+
163
+ ## Handler Options
164
+
165
+ ```typescript
166
+ interface MallyHandlerOptions {
167
+ client: Client;
168
+ commandsDir: string; // Directory to scan for commands
169
+ prefix: string | ((ctx: { serverId?: string }) => string | Promise<string>);
170
+ owners?: string[]; // Owner user IDs
171
+ extensions?: string[]; // File extensions (default: ['.js', '.ts'])
172
+ disableMentionPrefix?: boolean; // Disable @bot prefix
173
+ }
174
+ ```
175
+
176
+ ## Dynamic Prefix
177
+
178
+ ```typescript
179
+ const handler = new MallyHandler({
180
+ client,
181
+ commandsDir: join(__dirname, 'commands'),
182
+ prefix: async ({ serverId }) => {
183
+ // Fetch from database, etc.
184
+ return serverId ? await getServerPrefix(serverId) : '!';
185
+ },
186
+ });
187
+ ```
188
+
189
+ ## Legacy Class-Based Commands
190
+
191
+ You can also use the class-based approach:
192
+
193
+ ```typescript
194
+ import { Command, BaseCommand, Context } from '@marshmallow/mally';
195
+
196
+ @Command({
197
+ name: 'ping',
198
+ description: 'Ping command',
199
+ })
200
+ export class PingCommand extends BaseCommand {
201
+ async run(ctx: Context) {
202
+ await ctx.reply('Pong!');
203
+ }
204
+ }
205
+ ```
206
+
207
+ ## License
208
+
209
+ AGPL-3.0-or-later
210
+
211
+
212
+