dankgrinder 8.651.0 → 8.844.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/lib/grinder.js CHANGED
@@ -3321,51 +3321,49 @@ async function start(apiKey, apiUrl, opts = {}) {
3321
3321
  }
3322
3322
  }
3323
3323
 
3324
- // Init rawLogger Redis (uses same URL logs all raw gateway data)
3325
- if (REDIS_URL) {
3326
- rawLogger.init(REDIS_URL, { silent: true }).catch(() => {});
3327
- // Listen for DM events across all accounts — update worker state + dashboard LIVE
3328
- rawLogger.onDmEvent((event, raw) => {
3329
- const channelId = raw.channel_id;
3330
- for (const w of workers) {
3331
- const isThisWorker = w.client?.user?.dmChannel?.id === channelId;
3332
- if (!isThisWorker && w.channel?.id !== channelId) continue;
3333
-
3334
- if (event.type === 'death') {
3335
- if (event.lifesaversLeft >= 0) {
3336
- const prev = w._lifesavers;
3337
- w._lifesavers = event.lifesaversLeft;
3338
- if (event.lifesaversLeft === 0) {
3339
- w.lastStatus = 'DEAD';
3340
- w._alert = { type: 'death' };
3341
- w.setCooldown?.('crime', 86400);
3342
- w.setCooldown?.('search', 86400);
3343
- sendWebhook?.('DEATH ALERT (DM)', `**${w.username}** died in DMs! **0 lifesavers!**\nCrime/search auto-disabled.`, 0xef4444);
3344
- ui.log(workers.indexOf(w), `${c.red}E${c.reset} DEATH — 0 lifesavers! Crime/search disabled`);
3345
- } else {
3346
- w.log?.('warn', `DEATH in DMs — ${event.lifesaversLeft} lifesavers remaining`);
3347
- if (prev !== event.lifesaversLeft) {
3348
- w.setCooldown?.('crime', 60);
3349
- w.setCooldown?.('search', 60);
3350
- }
3351
- if (event.lifesaversLeft <= 2) {
3352
- w._alert = { type: 'lowls' };
3353
- sendWebhook?.('LOW LIFESAVERS', `**${w.username}** has only **${event.lifesaversLeft}** lifesaver(s) left!`, 0xfbbf24);
3354
- ui.log(workers.indexOf(w), `⚠ ${event.lifesaversLeft} lifesavers left!`);
3355
- }
3324
+ // Init rawLogger in-memory-only (Redis disabled to save $ on Railway)
3325
+ rawLogger.init(null, { silent: true });
3326
+ // Listen for DM events across all accounts — update worker state + dashboard LIVE
3327
+ rawLogger.onDmEvent((event, raw) => {
3328
+ const channelId = raw.channel_id;
3329
+ for (const w of workers) {
3330
+ const isThisWorker = w.client?.user?.dmChannel?.id === channelId;
3331
+ if (!isThisWorker && w.channel?.id !== channelId) continue;
3332
+
3333
+ if (event.type === 'death') {
3334
+ if (event.lifesaversLeft >= 0) {
3335
+ const prev = w._lifesavers;
3336
+ w._lifesavers = event.lifesaversLeft;
3337
+ if (event.lifesaversLeft === 0) {
3338
+ w.lastStatus = 'DEAD';
3339
+ w._alert = { type: 'death' };
3340
+ w.setCooldown?.('crime', 86400);
3341
+ w.setCooldown?.('search', 86400);
3342
+ sendWebhook?.('DEATH ALERT (DM)', `**${w.username}** died in DMs! **0 lifesavers!**\nCrime/search auto-disabled.`, 0xef4444);
3343
+ ui.log(workers.indexOf(w), `${c.red}E${c.reset} DEATH 0 lifesavers! Crime/search disabled`);
3344
+ } else {
3345
+ w.log?.('warn', `DEATH in DMs — ${event.lifesaversLeft} lifesavers remaining`);
3346
+ if (prev !== event.lifesaversLeft) {
3347
+ w.setCooldown?.('crime', 60);
3348
+ w.setCooldown?.('search', 60);
3349
+ }
3350
+ if (event.lifesaversLeft <= 2) {
3351
+ w._alert = { type: 'lowls' };
3352
+ sendWebhook?.('LOW LIFESAVERS', `**${w.username}** has only **${event.lifesaversLeft}** lifesaver(s) left!`, 0xfbbf24);
3353
+ ui.log(workers.indexOf(w), `⚠ ${event.lifesaversLeft} lifesavers left!`);
3356
3354
  }
3357
3355
  }
3358
3356
  }
3357
+ }
3359
3358
 
3360
- if (event.type === 'levelup') {
3361
- if (event.to > 0) {
3362
- w._level = event.to;
3363
- ui.log(workers.indexOf(w), `↑ level ${event.to}`);
3364
- }
3359
+ if (event.type === 'levelup') {
3360
+ if (event.to > 0) {
3361
+ w._level = event.to;
3362
+ ui.log(workers.indexOf(w), `↑ level ${event.to}`);
3365
3363
  }
3366
3364
  }
3367
- });
3368
- }
3365
+ }
3366
+ });
3369
3367
 
3370
3368
  // ── Phase 1: Login ─────────────────────────────────────────────
3371
3369
  const parsedGapMin = Number.parseInt(String(process.env.LOGIN_GAP_MIN_MS || '50'), 10);
package/lib/rawLogger.js CHANGED
@@ -1,27 +1,23 @@
1
1
  /**
2
- * Raw Gateway Logger — intercepts ALL Dank Memer messages and stores to Redis.
3
- * Captures: CV2 components, embeds, buttons, text, edits, ephemeral, cooldowns.
2
+ * Raw Gateway Logger — intercepts ALL Dank Memer messages (in-memory LRU cache).
4
3
  *
5
- * Redis Keys:
6
- * raw:msg:{msgId} — full parsed message (latest version), TTL 24h
7
- * raw:msg:{msgId}:history — list of all versions (create + updates), TTL 24h
8
- * raw:cmd:{command}:log — list of recent message IDs for that command, TTL 7d
9
- * raw:account:{discordUserId} — list of recent message IDs for that account, TTL 7d
10
- * raw:ephemeral:log — list of all ephemeral messages, TTL 7d
11
- * raw:all:log — list of ALL message IDs (global), TTL 7d, capped at 10000
4
+ * Redis is optional and disabled by default to save costs.
5
+ * Enable via: rawLogger.setRedisDisabled(false) after init, or pass opts.disabled: false.
6
+ *
7
+ * In-memory cache: last 256 messages per instance (LRU).
8
+ * Redis (if enabled): tiny 1h logs for command tracking.
12
9
  *
13
10
  * Usage:
14
11
  * const rawLogger = require('./rawLogger');
15
- * await rawLogger.init('redis://...');
12
+ * await rawLogger.init(null); // memory-only (no Redis)
13
+ * rawLogger.setRedisDisabled(true); // explicit disable
16
14
  * rawLogger.attachRawLogger(client);
17
- * const msg = await rawLogger.getMsg('123456');
18
- * const history = await rawLogger.getMsgHistory('123456');
19
- * const recent = await rawLogger.getRecentForCommand('cointoss', 20);
20
15
  */
21
16
 
22
17
  const LRU_SIZE = 256;
23
18
  let redis = null;
24
19
  let redisReady = false;
20
+ let redisDisabled = true; // Disabled by default — enable with setRedisDisabled(false)
25
21
 
26
22
  // ── In-memory LRU (always available, even without Redis) ──
27
23
  const memStore = new Map();
@@ -32,9 +28,23 @@ let _silent = false;
32
28
  function _log(...args) { if (!_silent) console.log(...args); }
33
29
  function _err(...args) { if (!_silent) console.error(...args); }
34
30
 
31
+ /**
32
+ * Enable or disable Redis logging entirely.
33
+ * When disabled, rawLogger runs in memory-only mode (LRU cache, no Redis writes).
34
+ */
35
+ function setRedisDisabled(disabled) {
36
+ redisDisabled = !!disabled;
37
+ _log(`[rawLogger] Redis logging ${redisDisabled ? 'DISABLED' : 'ENABLED'}`);
38
+ }
39
+
35
40
  // ── Redis init ──
36
41
  async function init(redisUrl, opts = {}) {
37
42
  if (opts.silent) _silent = true;
43
+ if (opts.disabled) {
44
+ redisDisabled = true;
45
+ _log('[rawLogger] Redis logging disabled via opts');
46
+ return;
47
+ }
38
48
  if (!redisUrl) {
39
49
  _log('[rawLogger] No Redis URL — raw logging disabled');
40
50
  return;
@@ -427,59 +437,25 @@ async function store(d, event) {
427
437
  memStore.set(d.id, parsed);
428
438
  channelLast.set(d.channel_id, d.id);
429
439
 
430
- // Redis (non-blocking, fire-and-forget)
431
- if (redisReady && redis && redis.status === 'ready') {
432
- try {
433
- const key = `raw:msg:${d.id}`;
434
- const histKey = `raw:msg:${d.id}:history`;
435
- const json = JSON.stringify(parsed, (k, v) => k === '_raw' ? undefined : v);
436
-
437
- const MSG_TTL = 2592000; // 30 days
438
- const LOG_TTL = 2592000; // 30 days
440
+ // Redis (non-blocking, fire-and-forget) — skip if disabled
441
+ if (redisDisabled || !redisReady || !redis || redis.status !== 'ready') {
442
+ return parsed;
443
+ }
444
+ try {
445
+ const LOG_TTL = 3600; // 1 hour
446
+ const MAX_LOG = 99; // tiny cap
439
447
 
440
- const pipe = redis.pipeline();
441
- // Latest version
442
- pipe.set(key, json, 'EX', MSG_TTL);
443
- // History (all versions)
444
- pipe.rpush(histKey, json);
445
- pipe.expire(histKey, MSG_TTL);
446
- // Per-command log (including 'unknown' so we can track detection gaps)
448
+ const pipe = redis.pipeline();
449
+ // Per-command log — only recent IDs, tiny cap, short TTL
447
450
  if (parsed.command) {
448
451
  const cmdKey = `raw:cmd:${parsed.command}:log`;
449
452
  pipe.lpush(cmdKey, `${d.id}:${parsed._capturedAt}:${event}`);
450
- pipe.ltrim(cmdKey, 0, 4999);
453
+ pipe.ltrim(cmdKey, 0, MAX_LOG);
451
454
  pipe.expire(cmdKey, LOG_TTL);
452
455
  }
453
- // Per-account log
454
- if (parsed.authorId) {
455
- const accKey = `raw:channel:${d.channel_id}:log`;
456
- pipe.lpush(accKey, `${d.id}:${parsed._capturedAt}:${event}:${parsed.command}`);
457
- pipe.ltrim(accKey, 0, 4999);
458
- pipe.expire(accKey, LOG_TTL);
459
- }
460
- // Track ephemeral in memory for quick access before they vanish
461
- if (parsed.isEphemeral || (d.flags & 32832)) {
462
- let chMap = ephemeralByChannel.get(d.channel_id);
463
- if (!chMap) {
464
- chMap = new Map();
465
- ephemeralByChannel.set(d.channel_id, chMap);
466
- }
467
- chMap.set(d.id, parsed);
468
- // Cap per-channel ephemeral buffer at 50
469
- if (chMap.size > 50) {
470
- const firstKey = chMap.keys().next().value;
471
- chMap.delete(firstKey);
472
- }
473
- }
474
- // Ephemeral log
475
- if (parsed.isEphemeral || (d.flags & 32832)) {
476
- pipe.lpush('raw:ephemeral:log', `${d.id}:${parsed._capturedAt}:${parsed.command}:${d.channel_id}`);
477
- pipe.ltrim('raw:ephemeral:log', 0, 4999);
478
- pipe.expire('raw:ephemeral:log', LOG_TTL);
479
- }
480
- // Global log
456
+ // Global log — tiny cap, short TTL
481
457
  pipe.lpush('raw:all:log', `${d.id}:${parsed._capturedAt}:${event}:${parsed.command}:${d.channel_id}`);
482
- pipe.ltrim('raw:all:log', 0, 49999);
458
+ pipe.ltrim('raw:all:log', 0, 499);
483
459
  pipe.expire('raw:all:log', LOG_TTL);
484
460
 
485
461
  pipe.exec().catch(() => {}); // fire and forget
@@ -768,6 +744,7 @@ async function getStats() {
768
744
 
769
745
  module.exports = {
770
746
  init,
747
+ setRedisDisabled,
771
748
  attachRawLogger,
772
749
  attachDmLogger,
773
750
  onDmEvent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "8.651.0",
3
+ "version": "8.844.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"