dankgrinder 1.0.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,47 @@
1
+ # DankGrinder CLI
2
+
3
+ Dank Memer automation engine — grind coins while you sleep.
4
+
5
+ ## Installation & Usage
6
+
7
+ ```bash
8
+ # Run directly (no install needed)
9
+ npx dankgrinder --key dkg_your_api_key
10
+
11
+ # Or install globally
12
+ npm install -g dankgrinder
13
+ dankgrinder --key dkg_your_api_key
14
+ ```
15
+
16
+ ## Options
17
+
18
+ | Flag | Description | Default |
19
+ |------|-------------|---------|
20
+ | `--key <key>` | Your DankGrinder API key (required) | — |
21
+ | `--url <url>` | API server URL | `http://localhost:3000` |
22
+ | `--help` | Show help | — |
23
+ | `--version` | Show version | — |
24
+
25
+ ## Setup
26
+
27
+ 1. Sign up at your DankGrinder dashboard
28
+ 2. Go to **Config** → set your Discord token and channel ID
29
+ 3. Enable the commands you want to automate
30
+ 4. Go to **API Keys** → create a new key
31
+ 5. Run: `npx dankgrinder --key dkg_your_key`
32
+
33
+ ## Supported Commands
34
+
35
+ - `pls hunt` — Hunt animals for coins
36
+ - `pls dig` — Dig for treasure
37
+ - `pls beg` — Beg for coins
38
+ - `pls search` — Search locations
39
+ - `pls hl` — Play highlow
40
+ - `pls crime` — Commit crime
41
+ - `pls pm` — Post memes
42
+
43
+ ## Requirements
44
+
45
+ - Node.js 18+
46
+ - A DankGrinder account with API key
47
+ - Discord token configured in dashboard
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { start } = require('../lib/grinder');
4
+
5
+ const args = process.argv.slice(2);
6
+
7
+ if (args.includes('--help') || args.includes('-h')) {
8
+ console.log(`
9
+ \x1b[35m╔══════════════════════════════════════╗
10
+ ║ \x1b[1mDankGrinder CLI v2.0\x1b[0m\x1b[35m ║
11
+ ║ Multi-Account Automation Engine ║
12
+ ╚══════════════════════════════════════╝\x1b[0m
13
+
14
+ \x1b[1mUsage:\x1b[0m
15
+ npx dankgrinder --key <API_KEY> [options]
16
+
17
+ \x1b[1mOptions:\x1b[0m
18
+ --key <key> Your DankGrinder API key (required)
19
+ --url <url> API server URL (default: http://localhost:3000)
20
+ --help, -h Show this help message
21
+ --version, -v Show version
22
+
23
+ \x1b[1mExamples:\x1b[0m
24
+ npx dankgrinder --key dkg_abc123def456
25
+ npx dankgrinder --key dkg_abc123def456 --url https://myserver.com
26
+
27
+ \x1b[1mSetup:\x1b[0m
28
+ 1. Sign up at your DankGrinder dashboard
29
+ 2. Go to the Accounts page and add your Discord accounts
30
+ 3. Configure per-account commands and cooldowns
31
+ 4. Generate an API key from Auth Tokens
32
+ 5. Run this CLI — it spawns one worker per active account
33
+ `);
34
+ process.exit(0);
35
+ }
36
+
37
+ if (args.includes('--version') || args.includes('-v')) {
38
+ const pkg = require('../package.json');
39
+ console.log(`dankgrinder v${pkg.version}`);
40
+ process.exit(0);
41
+ }
42
+
43
+ let apiKey = '';
44
+ let apiUrl = 'http://localhost:3000';
45
+
46
+ for (let i = 0; i < args.length; i++) {
47
+ if (args[i] === '--key' && args[i + 1]) apiKey = args[i + 1];
48
+ if (args[i] === '--url' && args[i + 1]) apiUrl = args[i + 1];
49
+ }
50
+
51
+ if (!apiKey) {
52
+ console.error('\x1b[31m✗ Missing API key.\x1b[0m');
53
+ console.error('');
54
+ console.error(' Usage: npx dankgrinder --key <YOUR_API_KEY>');
55
+ console.error('');
56
+ console.error(' Get your API key from the DankGrinder dashboard.');
57
+ console.error(' Run \x1b[36mnpx dankgrinder --help\x1b[0m for more info.');
58
+ process.exit(1);
59
+ }
60
+
61
+ start(apiKey, apiUrl);
package/lib/grinder.js ADDED
@@ -0,0 +1,387 @@
1
+ const { Client } = require('discord.js-selfbot-v13');
2
+
3
+ const c = {
4
+ reset: '\x1b[0m',
5
+ green: '\x1b[32m',
6
+ red: '\x1b[31m',
7
+ yellow: '\x1b[33m',
8
+ cyan: '\x1b[36m',
9
+ magenta: '\x1b[35m',
10
+ dim: '\x1b[2m',
11
+ bold: '\x1b[1m',
12
+ white: '\x1b[37m',
13
+ blue: '\x1b[34m',
14
+ };
15
+
16
+ const ACCOUNT_COLORS = [c.cyan, c.magenta, c.yellow, c.green, c.blue, c.red];
17
+
18
+ let API_KEY = '';
19
+ let API_URL = '';
20
+ const workers = [];
21
+
22
+ function log(type, msg, label) {
23
+ const time = new Date().toLocaleTimeString();
24
+ const prefix = {
25
+ info: `${c.cyan}ℹ${c.reset}`,
26
+ success: `${c.green}✓${c.reset}`,
27
+ error: `${c.red}✗${c.reset}`,
28
+ warn: `${c.yellow}⚠${c.reset}`,
29
+ cmd: `${c.magenta}▸${c.reset}`,
30
+ };
31
+ const tag = label ? `${c.bold}[${label}]${c.reset} ` : '';
32
+ console.log(` ${c.dim}${time}${c.reset} ${prefix[type] || prefix.info} ${tag}${msg}`);
33
+ }
34
+
35
+ async function fetchConfig() {
36
+ try {
37
+ const res = await fetch(`${API_URL}/api/grinder/config`, {
38
+ headers: { Authorization: `Bearer ${API_KEY}` },
39
+ });
40
+ const data = await res.json();
41
+ if (data.error) {
42
+ log('error', `Config fetch failed: ${data.error}`);
43
+ return null;
44
+ }
45
+ return data;
46
+ } catch (err) {
47
+ log('error', `Cannot reach API: ${err.message}`);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ async function sendLog(command, response, status) {
53
+ try {
54
+ await fetch(`${API_URL}/api/grinder/log`, {
55
+ method: 'POST',
56
+ headers: {
57
+ Authorization: `Bearer ${API_KEY}`,
58
+ 'Content-Type': 'application/json',
59
+ },
60
+ body: JSON.stringify({ command, response, status }),
61
+ });
62
+ } catch {
63
+ // silent fail for logging
64
+ }
65
+ }
66
+
67
+ function randomDelay(min, max) {
68
+ const ms = (Math.random() * (max - min) + min) * 1000;
69
+ return new Promise((resolve) => setTimeout(resolve, ms));
70
+ }
71
+
72
+ function humanDelay() {
73
+ return new Promise((resolve) => setTimeout(resolve, 200 + Math.random() * 600));
74
+ }
75
+
76
+ function safeParseJSON(str, fallback) {
77
+ try {
78
+ return JSON.parse(str || '[]');
79
+ } catch {
80
+ return fallback;
81
+ }
82
+ }
83
+
84
+ // ── Worker: one per Discord account ──────────────────────────────
85
+
86
+ class AccountWorker {
87
+ constructor(account, idx) {
88
+ this.account = account;
89
+ this.label = account.label || `Account ${idx + 1}`;
90
+ this.color = ACCOUNT_COLORS[idx % ACCOUNT_COLORS.length];
91
+ this.client = new Client();
92
+ this.channel = null;
93
+ this.running = false;
94
+ }
95
+
96
+ log(type, msg) {
97
+ log(type, msg, `${this.color}${this.label}${c.reset}`);
98
+ }
99
+
100
+ waitForDankMemer(timeout = 15000) {
101
+ return new Promise((resolve) => {
102
+ const timer = setTimeout(() => {
103
+ this.client.removeListener('messageCreate', handler);
104
+ resolve(null);
105
+ }, timeout);
106
+
107
+ const self = this;
108
+ function handler(msg) {
109
+ if (msg.author.id === '270904126974590976' && msg.channel.id === self.channel.id) {
110
+ clearTimeout(timer);
111
+ self.client.removeListener('messageCreate', handler);
112
+ resolve(msg);
113
+ }
114
+ }
115
+
116
+ this.client.on('messageCreate', handler);
117
+ });
118
+ }
119
+
120
+ async handleInteraction(msg) {
121
+ if (!msg) return null;
122
+
123
+ if (msg.components && msg.components.length > 0) {
124
+ for (const row of msg.components) {
125
+ if (row.components) {
126
+ for (const component of row.components) {
127
+ if (component.type === 2) {
128
+ const label = component.label?.toLowerCase() || '';
129
+ const searchAnswers = safeParseJSON(this.account.search_answers, []);
130
+ const crimeAnswers = safeParseJSON(this.account.crime_answers, []);
131
+ const allSafe = [...searchAnswers, ...crimeAnswers];
132
+
133
+ if (allSafe.length > 0) {
134
+ const match = allSafe.find((a) => label.includes(a.toLowerCase()));
135
+ if (match) {
136
+ await humanDelay();
137
+ try {
138
+ await component.click();
139
+ return `Clicked: ${label}`;
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ const buttons = row.components.filter((comp) => comp.type === 2 && !comp.disabled);
149
+ if (buttons.length > 0) {
150
+ const btn = buttons[Math.floor(Math.random() * buttons.length)];
151
+ await humanDelay();
152
+ try {
153
+ await btn.click();
154
+ return `Clicked: ${btn.label || 'button'}`;
155
+ } catch {
156
+ return null;
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ return msg.content?.substring(0, 100) || 'Response received';
164
+ }
165
+
166
+ async handleHighLow(msg) {
167
+ if (!msg) return null;
168
+
169
+ if (msg.embeds && msg.embeds.length > 0) {
170
+ const embed = msg.embeds[0];
171
+ const desc = embed.description || '';
172
+ const match = desc.match(/(\d+)/);
173
+ if (match) {
174
+ const num = parseInt(match[1]);
175
+ const buttons = [];
176
+ if (msg.components) {
177
+ for (const row of msg.components) {
178
+ if (row.components) {
179
+ for (const comp of row.components) {
180
+ if (comp.type === 2) buttons.push(comp);
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ if (buttons.length >= 2) {
187
+ await humanDelay();
188
+ let targetBtn;
189
+ if (num > 50) {
190
+ targetBtn = buttons.find((b) => b.label?.toLowerCase().includes('lower')) || buttons[1];
191
+ } else if (num < 50) {
192
+ targetBtn = buttons.find((b) => b.label?.toLowerCase().includes('higher')) || buttons[0];
193
+ } else {
194
+ targetBtn = buttons[Math.floor(Math.random() * 2)];
195
+ }
196
+
197
+ try {
198
+ await targetBtn.click();
199
+ return `HL: number was ${num}, clicked ${targetBtn.label}`;
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ return null;
208
+ }
209
+
210
+ async runCommand(cmdName, prefix) {
211
+ const cmdString = `${prefix} ${cmdName}`;
212
+ this.log('cmd', `${c.white}${cmdString}${c.reset}`);
213
+
214
+ try {
215
+ await this.channel.send(cmdString);
216
+ const response = await this.waitForDankMemer();
217
+
218
+ if (!response) {
219
+ this.log('warn', `No response for ${cmdString}`);
220
+ await sendLog(cmdString, 'No response (timeout)', 'timeout');
221
+ return;
222
+ }
223
+
224
+ let result;
225
+ if (cmdName === 'hl') {
226
+ result = await this.handleHighLow(response);
227
+ } else if (['search', 'crime'].includes(cmdName)) {
228
+ result = await this.handleInteraction(response);
229
+ } else {
230
+ result = response.content?.substring(0, 100) || 'Embed response';
231
+ if (response.components && response.components.length > 0) {
232
+ result = await this.handleInteraction(response);
233
+ }
234
+ }
235
+
236
+ this.log('success', `${cmdString} ${c.dim}→${c.reset} ${c.green}${result || 'done'}${c.reset}`);
237
+ await sendLog(cmdString, result || 'done', 'success');
238
+ } catch (err) {
239
+ this.log('error', `${cmdString} failed: ${err.message}`);
240
+ await sendLog(cmdString, err.message, 'error');
241
+ }
242
+ }
243
+
244
+ async grindLoop() {
245
+ if (this.running) return;
246
+ this.running = true;
247
+
248
+ const commandMap = {
249
+ cmd_hunt: 'hunt',
250
+ cmd_dig: 'dig',
251
+ cmd_beg: 'beg',
252
+ cmd_search: 'search',
253
+ cmd_hl: 'hl',
254
+ cmd_crime: 'crime',
255
+ cmd_pm: 'pm',
256
+ };
257
+
258
+ while (this.running) {
259
+ const acc = this.account;
260
+ const prefix = acc.use_slash ? '/' : 'pls';
261
+ const minCD = acc.cooldown_min || 3;
262
+ const maxCD = acc.cooldown_max || 8;
263
+
264
+ const enabledCommands = Object.entries(commandMap)
265
+ .filter(([key]) => acc[key] === true || acc[key] === 1)
266
+ .map(([, cmd]) => cmd);
267
+
268
+ if (enabledCommands.length === 0) {
269
+ this.log('warn', 'No commands enabled. Waiting 30s...');
270
+ await new Promise((r) => setTimeout(r, 30000));
271
+ continue;
272
+ }
273
+
274
+ for (const cmd of enabledCommands) {
275
+ if (!this.running) break;
276
+ await this.runCommand(cmd, prefix);
277
+ await randomDelay(minCD, maxCD);
278
+ }
279
+
280
+ const cycleDelay = 2 + Math.random() * 3;
281
+ this.log('info', `${c.dim}Cycle done. Next in ${cycleDelay.toFixed(1)}s${c.reset}`);
282
+ await new Promise((r) => setTimeout(r, cycleDelay * 1000));
283
+ }
284
+ }
285
+
286
+ async start() {
287
+ if (!this.account.discord_token) {
288
+ this.log('error', 'No Discord token configured.');
289
+ return;
290
+ }
291
+ if (!this.account.channel_id) {
292
+ this.log('error', 'No channel ID configured.');
293
+ return;
294
+ }
295
+
296
+ this.log('info', 'Connecting to Discord...');
297
+
298
+ return new Promise((resolve) => {
299
+ this.client.on('ready', async () => {
300
+ this.log('success', `Logged in as ${c.bold}${this.client.user.tag}${c.reset}`);
301
+ this.log('info', `Servers: ${c.white}${this.client.guilds.cache.size}${c.reset}`);
302
+
303
+ this.channel = await this.client.channels.fetch(this.account.channel_id).catch(() => null);
304
+
305
+ if (!this.channel) {
306
+ this.log('error', `Cannot find channel ${this.account.channel_id}`);
307
+ resolve();
308
+ return;
309
+ }
310
+
311
+ this.log('info', `Channel: ${c.white}#${this.channel.name || this.account.channel_id}${c.reset}`);
312
+
313
+ const enabled = Object.keys({ cmd_hunt: 1, cmd_dig: 1, cmd_beg: 1, cmd_search: 1, cmd_hl: 1, cmd_crime: 1, cmd_pm: 1 })
314
+ .filter((k) => this.account[k] === true || this.account[k] === 1)
315
+ .map((k) => k.replace('cmd_', ''));
316
+ this.log('info', `Commands: ${c.white}${enabled.join(', ') || 'none'}${c.reset}`);
317
+ this.log('info', `Cooldown: ${c.white}${this.account.cooldown_min || 3}-${this.account.cooldown_max || 8}s${c.reset}`);
318
+ console.log('');
319
+
320
+ this.grindLoop();
321
+ resolve();
322
+ });
323
+
324
+ this.client.login(this.account.discord_token);
325
+ });
326
+ }
327
+
328
+ stop() {
329
+ this.running = false;
330
+ try { this.client.destroy(); } catch {}
331
+ }
332
+ }
333
+
334
+ // ── Main Entry ──────────────────────────────────
335
+
336
+ async function start(apiKey, apiUrl) {
337
+ API_KEY = apiKey;
338
+ API_URL = apiUrl;
339
+
340
+ console.log('');
341
+ console.log(` ${c.magenta}${c.bold}╔══════════════════════════════════════════╗${c.reset}`);
342
+ console.log(` ${c.magenta}${c.bold}║${c.reset} ${c.magenta}${c.bold}║${c.reset}`);
343
+ console.log(` ${c.magenta}${c.bold}║${c.reset} ${c.white}${c.bold}DankGrinder${c.reset} ${c.dim}v2.0${c.reset} ${c.magenta}${c.bold}║${c.reset}`);
344
+ console.log(` ${c.magenta}${c.bold}║${c.reset} ${c.dim}Multi-Account Automation Engine${c.reset} ${c.magenta}${c.bold}║${c.reset}`);
345
+ console.log(` ${c.magenta}${c.bold}║${c.reset} ${c.magenta}${c.bold}║${c.reset}`);
346
+ console.log(` ${c.magenta}${c.bold}╚══════════════════════════════════════════╝${c.reset}`);
347
+ console.log('');
348
+
349
+ log('info', `API: ${c.dim}${API_URL}${c.reset}`);
350
+ log('info', 'Fetching config & accounts...');
351
+
352
+ const data = await fetchConfig();
353
+
354
+ if (!data) {
355
+ log('error', 'Failed to fetch config. Check your API key and server.');
356
+ process.exit(1);
357
+ }
358
+
359
+ const { accounts } = data;
360
+
361
+ if (!accounts || accounts.length === 0) {
362
+ log('error', 'No active accounts found. Add accounts in the dashboard.');
363
+ process.exit(1);
364
+ }
365
+
366
+ log('success', `Found ${c.bold}${accounts.length}${c.reset} active account(s)`);
367
+ console.log('');
368
+
369
+ // Spawn a worker per account
370
+ for (let i = 0; i < accounts.length; i++) {
371
+ const worker = new AccountWorker(accounts[i], i);
372
+ workers.push(worker);
373
+ await worker.start();
374
+ }
375
+
376
+ log('info', `${c.bold}All workers running. Press Ctrl+C to stop.${c.reset}`);
377
+ console.log('');
378
+
379
+ process.on('SIGINT', () => {
380
+ console.log('');
381
+ log('warn', 'Shutting down all workers...');
382
+ for (const w of workers) w.stop();
383
+ setTimeout(() => process.exit(0), 2000);
384
+ });
385
+ }
386
+
387
+ module.exports = { start };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "dankgrinder",
3
+ "version": "1.0.0",
4
+ "description": "Dank Memer automation engine — grind coins while you sleep",
5
+ "bin": {
6
+ "dankgrinder": "./bin/dankgrinder.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "lib/",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "dank-memer",
15
+ "discord",
16
+ "grinder",
17
+ "automation",
18
+ "selfbot"
19
+ ],
20
+ "author": "DankGrinder",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "discord.js-selfbot-v13": "^3.6.1"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ }
28
+ }