nodebb-plugin-niki-loyalty 1.3.16 → 1.5.1
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/.claude/settings.local.json +7 -0
- package/library.js +153 -41
- package/niki-admin.txt +1286 -0
- package/niki-kasa.txt +1036 -0
- package/niki-prize.txt +200 -0
- package/niki-wallet.txt +415 -0
- package/package.json +1 -1
- package/plugin.json +2 -5
- package/static/lib/client.js +105 -108
- package/static/samplefile.html +1 -1
- package/static/templates/niki-kasa.tpl +13 -0
- package/yeedek +313 -0
package/library.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
1
2
|
const db = require.main.require('./src/database');
|
|
2
3
|
const user = require.main.require('./src/user');
|
|
3
4
|
const posts = require.main.require('./src/posts');
|
|
@@ -5,23 +6,33 @@ const routeHelpers = require.main.require('./src/controllers/helpers');
|
|
|
5
6
|
const nconf = require.main.require('nconf');
|
|
6
7
|
const socketHelpers = require.main.require('./src/socket.io/index');
|
|
7
8
|
const SocketPlugins = require.main.require('./src/socket.io/plugins');
|
|
9
|
+
const groups = require.main.require('./src/groups');
|
|
8
10
|
const Plugin = {};
|
|
9
11
|
|
|
12
|
+
// Ödül kullanabilecek gruplar
|
|
13
|
+
const WALLET_GROUPS = ['Premium', 'Lite', 'VIP'];
|
|
14
|
+
|
|
10
15
|
// =========================
|
|
11
16
|
// ⚙️ AYARLAR & KURALLAR (GAME LOGIC)
|
|
12
17
|
// =========================
|
|
13
18
|
const SETTINGS = {
|
|
14
|
-
dailyCap:
|
|
19
|
+
dailyCap: 35, // Günlük Maksimum Limit
|
|
15
20
|
};
|
|
16
21
|
|
|
17
|
-
// Puan Tablosu ve Limitleri (Toplam Potansiyel ~
|
|
22
|
+
// Puan Tablosu ve Limitleri (Toplam Potansiyel ~45 Puan)
|
|
18
23
|
const ACTIONS = {
|
|
19
|
-
login: { points:
|
|
20
|
-
new_topic: { points:
|
|
21
|
-
reply: { points:
|
|
22
|
-
read: { points:
|
|
23
|
-
like_given: { points: 5, limit: 2, name: 'Beğeni Atma ❤️' },
|
|
24
|
-
like_taken: { points:
|
|
24
|
+
login: { points: 5, limit: 1, name: 'Günlük Giriş 👋' }, // 5 Puan
|
|
25
|
+
new_topic: { points: 5, limit: 1, name: 'Yeni Konu 📝' }, // 5 Puan
|
|
26
|
+
reply: { points: 5, limit: 2, name: 'Yorum Yazma 💬' }, // 5 x 2 = 10 Puan
|
|
27
|
+
read: { points: 1, limit: 10, name: 'Konu Okuma 👀' }, // 1 x 10 = 10 Puan
|
|
28
|
+
like_given: { points: 2.5, limit: 2, name: 'Beğeni Atma ❤️' }, // 2.5 x 2 = 5 Puan
|
|
29
|
+
like_taken: { points: 5, limit: 2, name: 'Beğeni Alma 🌟' } // 5 x 2 = 10 Puan
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Grup Katılım Bonusları
|
|
33
|
+
const GROUP_BONUSES = {
|
|
34
|
+
'Premium': 30,
|
|
35
|
+
'VIP': 50,
|
|
25
36
|
};
|
|
26
37
|
|
|
27
38
|
// Ödüller
|
|
@@ -67,7 +78,6 @@ async function addKasaLog(staffUid, customerName, customerUid, rewardName, amoun
|
|
|
67
78
|
|
|
68
79
|
// 🔥 MERKEZİ PUAN DAĞITIM FONKSİYONU 🔥
|
|
69
80
|
// Bütün puan işlemleri buradan geçer, limitleri kontrol eder.
|
|
70
|
-
// 🔥 MERKEZİ PUAN DAĞITIM FONKSİYONU 🔥
|
|
71
81
|
async function awardDailyAction(uid, actionKey) {
|
|
72
82
|
try {
|
|
73
83
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
@@ -108,6 +118,10 @@ async function awardDailyAction(uid, actionKey) {
|
|
|
108
118
|
await db.incrObjectFieldBy(dailyScoreKey, 'score', pointsToGive);
|
|
109
119
|
await db.incrObjectFieldBy(actionCountKey, actionKey, 1);
|
|
110
120
|
|
|
121
|
+
// Günlük anahtarlara TTL koy (48 saat) - eski anahtarların birikmesini önle
|
|
122
|
+
await db.expire(dailyScoreKey, 172800);
|
|
123
|
+
await db.expire(actionCountKey, 172800);
|
|
124
|
+
|
|
111
125
|
// Logla
|
|
112
126
|
await addUserLog(uid, 'earn', pointsToGive, rule.name);
|
|
113
127
|
|
|
@@ -218,7 +232,7 @@ Plugin.onUpvote = async function (data) {
|
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
// Like Alan Kazanır (Post sahibi - kendine beğeni atamaz):
|
|
221
|
-
if (postOwnerUid && postOwnerUid !== voterUid) {
|
|
235
|
+
if (postOwnerUid && String(postOwnerUid) !== String(voterUid)) {
|
|
222
236
|
const likeTakenKey = `niki:liked_taken:${postOwnerUid}:${today}`;
|
|
223
237
|
const alreadyTaken = await db.isSetMember(likeTakenKey, pid.toString());
|
|
224
238
|
|
|
@@ -236,6 +250,46 @@ Plugin.onUpvote = async function (data) {
|
|
|
236
250
|
};
|
|
237
251
|
|
|
238
252
|
|
|
253
|
+
// 5. GRUP KATILIM BONUSU (Premium / VIP)
|
|
254
|
+
Plugin.onGroupJoin = async function (data) {
|
|
255
|
+
try {
|
|
256
|
+
const groupName = data.groupName;
|
|
257
|
+
const uid = data.uid;
|
|
258
|
+
if (!groupName || !uid) return;
|
|
259
|
+
|
|
260
|
+
const bonus = GROUP_BONUSES[groupName];
|
|
261
|
+
if (!bonus) return; // Bu grup için bonus tanımlı değil
|
|
262
|
+
|
|
263
|
+
// Aynı gruba tekrar katılırsa çift puan vermesin
|
|
264
|
+
const flagKey = `niki:group_bonus:${uid}:${groupName}`;
|
|
265
|
+
const alreadyClaimed = await db.get(flagKey);
|
|
266
|
+
if (alreadyClaimed) {
|
|
267
|
+
console.log(`[Niki-Loyalty] Grup bonusu zaten alınmış. UID: ${uid}, Group: ${groupName}`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
await user.incrementUserFieldBy(uid, 'niki_points', bonus);
|
|
272
|
+
await db.set(flagKey, '1');
|
|
273
|
+
await addUserLog(uid, 'earn', bonus, `${groupName} Grubu Katılım Bonusu 🎉`);
|
|
274
|
+
|
|
275
|
+
console.log(`[Niki-Loyalty] ✅ GRUP BONUSU! UID: ${uid}, Group: ${groupName}, Points: +${bonus}`);
|
|
276
|
+
|
|
277
|
+
// Socket bildirimi
|
|
278
|
+
try {
|
|
279
|
+
if (socketHelpers && socketHelpers.server && socketHelpers.server.sockets) {
|
|
280
|
+
socketHelpers.server.sockets.in('uid_' + uid).emit('event:niki_award', {
|
|
281
|
+
title: 'Grup Bonusu! 🎉',
|
|
282
|
+
message: `${groupName} grubuna katıldığın için <strong style="color:#ffd700">+${bonus} Puan</strong> kazandın!`,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
} catch (socketErr) {
|
|
286
|
+
console.error('[Niki-Loyalty] Socket emit hatası:', socketErr.message);
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error('[Niki-Loyalty] Grup bonus hatası:', err);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
239
293
|
// =========================
|
|
240
294
|
// 🚀 INIT & ROUTES
|
|
241
295
|
// =========================
|
|
@@ -249,10 +303,10 @@ Plugin.init = async function (params) {
|
|
|
249
303
|
try {
|
|
250
304
|
const uid = req.uid;
|
|
251
305
|
// Heartbeat geldiğinde "read" aksiyonunu tetikle
|
|
252
|
-
await awardDailyAction(uid, 'read');
|
|
306
|
+
const result = await awardDailyAction(uid, 'read');
|
|
253
307
|
|
|
254
308
|
const newBalance = await user.getUserField(uid, 'niki_points');
|
|
255
|
-
return res.json({ earned:
|
|
309
|
+
return res.json({ earned: result.success, total: newBalance });
|
|
256
310
|
} catch (err) {
|
|
257
311
|
return res.status(500).json({ error: 'error' });
|
|
258
312
|
}
|
|
@@ -284,21 +338,25 @@ Plugin.init = async function (params) {
|
|
|
284
338
|
}
|
|
285
339
|
});
|
|
286
340
|
|
|
287
|
-
// 2) WALLET DATA (Cüzdan Bilgileri)
|
|
288
|
-
// 2) WALLET DATA (Sayaçlar Eklendi)
|
|
341
|
+
// 2) WALLET DATA (Cüzdan Bilgileri + Sayaçlar)
|
|
289
342
|
router.get('/api/niki-loyalty/wallet-data', middleware.ensureLoggedIn, async (req, res) => {
|
|
290
343
|
try {
|
|
291
344
|
const uid = req.uid;
|
|
292
345
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
293
346
|
|
|
294
347
|
// Veritabanından verileri çek
|
|
295
|
-
const [userData, dailyData, actionCounts, historyRaw] = await Promise.all([
|
|
348
|
+
const [userData, dailyData, actionCounts, historyRaw, memberChecks] = await Promise.all([
|
|
296
349
|
user.getUserFields(uid, ['niki_points']),
|
|
297
350
|
db.getObject(`niki:daily:${uid}:${today}`),
|
|
298
|
-
db.getObject(`niki:daily:${uid}:${today}:counts`),
|
|
351
|
+
db.getObject(`niki:daily:${uid}:${today}:counts`),
|
|
299
352
|
db.getListRange(`niki:activity:${uid}`, 0, -1),
|
|
353
|
+
Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g))),
|
|
300
354
|
]);
|
|
301
355
|
|
|
356
|
+
// Kullanıcı WALLET_GROUPS'dan herhangi birinde mi?
|
|
357
|
+
const canRedeem = memberChecks.some(Boolean);
|
|
358
|
+
const userGroup = canRedeem ? WALLET_GROUPS[memberChecks.indexOf(true)] : null;
|
|
359
|
+
|
|
302
360
|
const dailyScore = parseFloat(dailyData?.score || 0);
|
|
303
361
|
let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
|
|
304
362
|
if (dailyPercent > 100) dailyPercent = 100;
|
|
@@ -310,9 +368,13 @@ Plugin.init = async function (params) {
|
|
|
310
368
|
dailyScore,
|
|
311
369
|
dailyCap: SETTINGS.dailyCap,
|
|
312
370
|
dailyPercent,
|
|
313
|
-
counts: actionCounts || {},
|
|
371
|
+
counts: actionCounts || {},
|
|
372
|
+
actions: ACTIONS,
|
|
314
373
|
history,
|
|
315
374
|
rewards: REWARDS,
|
|
375
|
+
canRedeem,
|
|
376
|
+
userGroup,
|
|
377
|
+
walletGroups: WALLET_GROUPS,
|
|
316
378
|
});
|
|
317
379
|
} catch (err) {
|
|
318
380
|
return res.status(500).json({ points: 0, history: [] });
|
|
@@ -432,42 +494,67 @@ Plugin.init = async function (params) {
|
|
|
432
494
|
router.post('/api/niki-loyalty/generate-qr', middleware.ensureLoggedIn, async (req, res) => {
|
|
433
495
|
try {
|
|
434
496
|
const uid = req.uid;
|
|
497
|
+
|
|
498
|
+
// Grup kontrolü
|
|
499
|
+
const memberChecks = await Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g)));
|
|
500
|
+
if (!memberChecks.some(Boolean)) {
|
|
501
|
+
return res.json({ success: false, message: 'Ödül kullanmak için Premium, Lite veya VIP grubuna katılmalısın.' });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const rewardIndex = parseInt(req.body.rewardIndex, 10);
|
|
435
505
|
const points = parseFloat((await user.getUserField(uid, 'niki_points')) || 0);
|
|
436
|
-
const minCost = REWARDS[REWARDS.length - 1].cost; // En ucuz ödül
|
|
437
506
|
|
|
438
|
-
|
|
439
|
-
|
|
507
|
+
// Seçilen ödülü bul
|
|
508
|
+
if (isNaN(rewardIndex) || rewardIndex < 0 || rewardIndex >= REWARDS.length) {
|
|
509
|
+
return res.json({ success: false, message: 'Geçersiz ödül seçimi.' });
|
|
510
|
+
}
|
|
511
|
+
const selectedReward = REWARDS[rewardIndex];
|
|
512
|
+
|
|
513
|
+
if (!TEST_MODE_UNLIMITED && points < selectedReward.cost) {
|
|
514
|
+
return res.json({ success: false, message: `Yetersiz Puan. ${selectedReward.name} için ${selectedReward.cost} puan gerekli.` });
|
|
440
515
|
}
|
|
441
|
-
const token =
|
|
442
|
-
|
|
516
|
+
const token = crypto.randomBytes(16).toString('hex');
|
|
517
|
+
// Token'a kullanıcı ve seçilen ödül bilgisini kaydet
|
|
518
|
+
await db.setObject(`niki:qr:${token}`, { uid: String(uid), rewardIndex: String(rewardIndex) });
|
|
443
519
|
await db.expire(`niki:qr:${token}`, 120); // 2 dakika geçerli
|
|
444
|
-
return res.json({ success: true, token });
|
|
520
|
+
return res.json({ success: true, token, rewardName: selectedReward.name, rewardCost: selectedReward.cost });
|
|
445
521
|
} catch (e) { return res.status(500).json({ success: false }); }
|
|
446
522
|
});
|
|
447
523
|
|
|
448
524
|
// 5) QR TARATMA (Kasa İşlemi)
|
|
449
525
|
router.post('/api/niki-loyalty/scan-qr', middleware.ensureLoggedIn, async (req, res) => {
|
|
450
|
-
// ... (Mevcut kodunun aynısı)
|
|
451
526
|
try {
|
|
452
527
|
const token = req.body.token;
|
|
453
528
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
454
529
|
const isMod = await user.isGlobalModerator(req.uid);
|
|
455
530
|
if (!isAdmin && !isMod) return res.status(403).json({ success: false, message: 'Yetkisiz' });
|
|
456
531
|
|
|
457
|
-
const
|
|
458
|
-
if (!
|
|
532
|
+
const qrData = await db.getObject(`niki:qr:${token}`);
|
|
533
|
+
if (!qrData || !qrData.uid) return res.json({ success: false, message: 'Geçersiz Kod' });
|
|
459
534
|
|
|
535
|
+
const custUid = qrData.uid;
|
|
536
|
+
const rewardIndex = parseInt(qrData.rewardIndex, 10);
|
|
460
537
|
const pts = parseFloat(await user.getUserField(custUid, 'niki_points') || 0);
|
|
461
538
|
|
|
462
539
|
let selectedReward = null;
|
|
463
540
|
if (!TEST_MODE_UNLIMITED) {
|
|
464
|
-
|
|
465
|
-
|
|
541
|
+
// Token'daki ödül index'ini kullan
|
|
542
|
+
if (!isNaN(rewardIndex) && rewardIndex >= 0 && rewardIndex < REWARDS.length) {
|
|
543
|
+
selectedReward = REWARDS[rewardIndex];
|
|
544
|
+
} else {
|
|
545
|
+
// Fallback: en yüksek ödülü seç
|
|
546
|
+
for (const r of REWARDS) {
|
|
547
|
+
if (pts >= r.cost) { selectedReward = r; break; }
|
|
548
|
+
}
|
|
466
549
|
}
|
|
467
550
|
if (!selectedReward) return res.json({ success: false, message: 'Puan Yetersiz' });
|
|
468
551
|
} else { selectedReward = REWARDS[0]; }
|
|
469
552
|
|
|
470
553
|
if (!TEST_MODE_UNLIMITED) {
|
|
554
|
+
if (pts < selectedReward.cost) {
|
|
555
|
+
await db.delete(`niki:qr:${token}`);
|
|
556
|
+
return res.json({ success: false, message: 'Puan yetersiz, işlem iptal edildi.' });
|
|
557
|
+
}
|
|
471
558
|
await user.decrementUserFieldBy(custUid, 'niki_points', selectedReward.cost);
|
|
472
559
|
}
|
|
473
560
|
await db.delete(`niki:qr:${token}`);
|
|
@@ -483,7 +570,7 @@ Plugin.init = async function (params) {
|
|
|
483
570
|
// 6) SAYFA ROTALARI
|
|
484
571
|
routeHelpers.setupPageRoute(router, '/niki-kasa', middleware, [], async (req, res) => {
|
|
485
572
|
const isStaff = await user.isAdministrator(req.uid) || await user.isGlobalModerator(req.uid);
|
|
486
|
-
if (!isStaff) return
|
|
573
|
+
if (!isStaff) return routeHelpers.notAllowed(req, res);
|
|
487
574
|
return res.render('niki-kasa', { title: 'Niki Kasa' });
|
|
488
575
|
});
|
|
489
576
|
|
|
@@ -569,20 +656,31 @@ Plugin.socketScanQR = async function (socket, data) {
|
|
|
569
656
|
const token = data.token;
|
|
570
657
|
if (!token) throw new Error('Geçersiz Token');
|
|
571
658
|
|
|
572
|
-
const
|
|
573
|
-
if (!
|
|
659
|
+
const qrData = await db.getObject(`niki:qr:${token}`);
|
|
660
|
+
if (!qrData || !qrData.uid) throw new Error('QR Kod Geçersiz veya Süresi Dolmuş');
|
|
574
661
|
|
|
662
|
+
const custUid = qrData.uid;
|
|
663
|
+
const rewardIndex = parseInt(qrData.rewardIndex, 10);
|
|
575
664
|
const pts = parseFloat((await user.getUserField(custUid, 'niki_points')) || 0);
|
|
576
665
|
|
|
577
666
|
let selectedReward = null;
|
|
578
667
|
if (!TEST_MODE_UNLIMITED) {
|
|
579
|
-
|
|
580
|
-
|
|
668
|
+
if (!isNaN(rewardIndex) && rewardIndex >= 0 && rewardIndex < REWARDS.length) {
|
|
669
|
+
selectedReward = REWARDS[rewardIndex];
|
|
670
|
+
} else {
|
|
671
|
+
for (const r of REWARDS) {
|
|
672
|
+
if (pts >= r.cost) { selectedReward = r; break; }
|
|
673
|
+
}
|
|
581
674
|
}
|
|
582
675
|
if (!selectedReward) throw new Error('Puan Yetersiz');
|
|
583
676
|
} else { selectedReward = REWARDS[0]; }
|
|
584
677
|
|
|
585
678
|
if (!TEST_MODE_UNLIMITED) {
|
|
679
|
+
const currentPts = parseFloat(await user.getUserField(custUid, 'niki_points') || 0);
|
|
680
|
+
if (currentPts < selectedReward.cost) {
|
|
681
|
+
await db.delete(`niki:qr:${token}`);
|
|
682
|
+
throw new Error('Puan yetersiz, işlem iptal edildi.');
|
|
683
|
+
}
|
|
586
684
|
await user.decrementUserFieldBy(custUid, 'niki_points', selectedReward.cost);
|
|
587
685
|
}
|
|
588
686
|
await db.delete(`niki:qr:${token}`);
|
|
@@ -651,7 +749,12 @@ Plugin.adminManagePoints = async function (socket, data) {
|
|
|
651
749
|
if (action === 'add') {
|
|
652
750
|
await user.incrementUserFieldBy(targetUid, 'niki_points', amount);
|
|
653
751
|
} else if (action === 'remove') {
|
|
654
|
-
|
|
752
|
+
// Negatif bakiye kontrolü
|
|
753
|
+
const currentPts = parseFloat(await user.getUserField(targetUid, 'niki_points') || 0);
|
|
754
|
+
const deduction = Math.min(amount, currentPts);
|
|
755
|
+
if (deduction > 0) {
|
|
756
|
+
await user.decrementUserFieldBy(targetUid, 'niki_points', deduction);
|
|
757
|
+
}
|
|
655
758
|
} else {
|
|
656
759
|
throw new Error('Geçersiz işlem türü.');
|
|
657
760
|
}
|
|
@@ -660,7 +763,9 @@ Plugin.adminManagePoints = async function (socket, data) {
|
|
|
660
763
|
const adminUserData = await user.getUserFields(uid, ['username']);
|
|
661
764
|
const logMsg = `Admin (${adminUserData.username}) tarafından ${action === 'add' ? '+' : '-'}${amount} puan. Sebep: ${reason}`;
|
|
662
765
|
|
|
663
|
-
|
|
766
|
+
// Negatif amount ile logla, böylece frontend doğru gösterebilir
|
|
767
|
+
const logAmount = action === 'remove' ? -amount : amount;
|
|
768
|
+
await addUserLog(targetUid, 'admin_adjust', logAmount, logMsg);
|
|
664
769
|
|
|
665
770
|
// Denetim Logu
|
|
666
771
|
const auditLog = { ts: Date.now(), adminUid: uid, adminName: adminUserData.username, targetUid: targetUid, action: action, amount: amount, reason: reason };
|
|
@@ -700,16 +805,22 @@ Plugin.adminGetUserDetail = async function (socket, data) {
|
|
|
700
805
|
const spendHistory = [];
|
|
701
806
|
|
|
702
807
|
activities.forEach(a => {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
808
|
+
const amt = parseFloat(a.amt) || 0;
|
|
809
|
+
if (a.type === 'admin_adjust') {
|
|
810
|
+
// amt negatifse çıkarma, pozitifse ekleme (eski kayıtlarda text'e bakarak fallback)
|
|
811
|
+
const isDeduction = amt < 0 || (amt > 0 && a.txt && a.txt.includes('-'));
|
|
812
|
+
if (isDeduction) {
|
|
813
|
+
totalSpent += Math.abs(amt);
|
|
706
814
|
spendHistory.push(a);
|
|
707
815
|
} else {
|
|
708
|
-
totalEarned +=
|
|
816
|
+
totalEarned += amt;
|
|
709
817
|
earnHistory.push(a);
|
|
710
818
|
}
|
|
819
|
+
} else if (a.type === 'earn') {
|
|
820
|
+
totalEarned += amt;
|
|
821
|
+
earnHistory.push(a);
|
|
711
822
|
} else if (a.type === 'spend') {
|
|
712
|
-
totalSpent +=
|
|
823
|
+
totalSpent += Math.abs(amt);
|
|
713
824
|
spendHistory.push(a);
|
|
714
825
|
}
|
|
715
826
|
});
|
|
@@ -747,7 +858,8 @@ Plugin.adminGetUserDetail = async function (socket, data) {
|
|
|
747
858
|
totalSpent,
|
|
748
859
|
currentPoints: parseFloat(userData.niki_points || 0),
|
|
749
860
|
todayScore: parseFloat((dailyData && dailyData.score) || 0),
|
|
750
|
-
todayCounts: actionCounts || {}
|
|
861
|
+
todayCounts: actionCounts || {},
|
|
862
|
+
dailyCap: SETTINGS.dailyCap
|
|
751
863
|
},
|
|
752
864
|
earnHistory: earnHistory.slice(0, 30),
|
|
753
865
|
spendHistory: spendHistory.slice(0, 30),
|