nodebb-plugin-niki-loyalty 1.2.9 → 1.3.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/library.js +182 -26
- package/package.json +1 -1
- package/static/widgets/niki-admin.tpl +686 -0
- package/static/widgets/niki-kasa-debug.html +1020 -0
- package/static/widgets/niki-kasa-isolated.html +419 -0
- package/static/widgets/niki-kasa-simple.html +623 -0
- package/static/widgets/niki-kasa-widget.html +506 -0
- package/templates/niki-kasa.tpl +207 -0
package/library.js
CHANGED
|
@@ -169,51 +169,69 @@ Plugin.onPostCreate = async function (data) {
|
|
|
169
169
|
};
|
|
170
170
|
|
|
171
171
|
// 4. BEĞENİ (Like Atma ve Alma) - Spam Korumalı + Debug Loglı
|
|
172
|
+
// NodeBB upvote hook'u { pid, uid, ... } formatında data gönderir (post nesnesi değil!)
|
|
172
173
|
Plugin.onUpvote = async function (data) {
|
|
173
|
-
console.log('[Niki-Loyalty] 👍 Upvote hook tetiklendi. Data:', JSON.stringify(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
console.log('[Niki-Loyalty] 👍 Upvote hook tetiklendi. Raw Data:', JSON.stringify(data));
|
|
175
|
+
|
|
176
|
+
// NodeBB bazen farklı formatlar gönderebilir, hepsini kontrol et
|
|
177
|
+
const pid = data.pid || (data.post && data.post.pid);
|
|
178
|
+
const voterUid = data.uid || (data.current && data.current.uid);
|
|
178
179
|
|
|
179
|
-
const pid = data.post && data.post.pid;
|
|
180
180
|
if (!pid) {
|
|
181
181
|
console.log('[Niki-Loyalty] ⚠️ Post PID bulunamadı, işlem iptal.');
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
if (!voterUid) {
|
|
186
|
+
console.log('[Niki-Loyalty] ⚠️ Voter UID bulunamadı, işlem iptal.');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Post sahibini bul (NodeBB upvote hook'u post sahibini göndermez!)
|
|
191
|
+
let postOwnerUid;
|
|
192
|
+
try {
|
|
193
|
+
postOwnerUid = await posts.getPostField(pid, 'uid');
|
|
194
|
+
console.log(`[Niki-Loyalty] Post sahibi bulundu: PID=${pid}, Owner UID=${postOwnerUid}`);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.log('[Niki-Loyalty] ⚠️ Post sahibi bulunamadı:', err.message);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!postOwnerUid) {
|
|
201
|
+
console.log('[Niki-Loyalty] ⚠️ Post sahibi UID boş, işlem iptal.');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
185
205
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
186
206
|
|
|
187
207
|
// Like Atan Kazanır:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const alreadyLiked = await db.isSetMember(likeGivenKey, pid.toString());
|
|
208
|
+
const likeGivenKey = `niki:liked:${voterUid}:${today}`;
|
|
209
|
+
const alreadyLiked = await db.isSetMember(likeGivenKey, pid.toString());
|
|
191
210
|
|
|
192
|
-
|
|
211
|
+
console.log(`[Niki-Loyalty] Like Atan: UID=${voterUid}, PID=${pid}, Daha önce beğenmiş mi=${alreadyLiked}`);
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
213
|
+
if (!alreadyLiked) {
|
|
214
|
+
const result = await awardDailyAction(voterUid, 'like_given');
|
|
215
|
+
console.log('[Niki-Loyalty] like_given sonuç:', result);
|
|
216
|
+
await db.setAdd(likeGivenKey, pid.toString());
|
|
217
|
+
await db.expire(likeGivenKey, 86400);
|
|
200
218
|
}
|
|
201
219
|
|
|
202
|
-
// Like Alan Kazanır (Post sahibi):
|
|
203
|
-
if (
|
|
204
|
-
const likeTakenKey = `niki:liked_taken:${
|
|
220
|
+
// Like Alan Kazanır (Post sahibi - kendine beğeni atamaz):
|
|
221
|
+
if (postOwnerUid && postOwnerUid !== voterUid) {
|
|
222
|
+
const likeTakenKey = `niki:liked_taken:${postOwnerUid}:${today}`;
|
|
205
223
|
const alreadyTaken = await db.isSetMember(likeTakenKey, pid.toString());
|
|
206
224
|
|
|
207
|
-
console.log(`[Niki-Loyalty] Like Alan: UID=${
|
|
225
|
+
console.log(`[Niki-Loyalty] Like Alan: UID=${postOwnerUid}, PID=${pid}, Daha önce puan almış mı=${alreadyTaken}`);
|
|
208
226
|
|
|
209
227
|
if (!alreadyTaken) {
|
|
210
|
-
const result = await awardDailyAction(
|
|
228
|
+
const result = await awardDailyAction(postOwnerUid, 'like_taken');
|
|
211
229
|
console.log('[Niki-Loyalty] like_taken sonuç:', result);
|
|
212
230
|
await db.setAdd(likeTakenKey, pid.toString());
|
|
213
231
|
await db.expire(likeTakenKey, 86400);
|
|
214
232
|
}
|
|
215
233
|
} else {
|
|
216
|
-
console.log('[Niki-Loyalty] ⚠️
|
|
234
|
+
console.log('[Niki-Loyalty] ⚠️ Kullanıcı kendi postunu beğenmiş veya post sahibi bulunamadı. Post owner:', postOwnerUid, 'Voter:', voterUid);
|
|
217
235
|
}
|
|
218
236
|
};
|
|
219
237
|
|
|
@@ -301,9 +319,8 @@ Plugin.init = async function (params) {
|
|
|
301
319
|
}
|
|
302
320
|
});
|
|
303
321
|
|
|
304
|
-
// 3) KASA HISTORY
|
|
322
|
+
// 3) KASA HISTORY - BASİT VERSİYON
|
|
305
323
|
router.get('/api/niki-loyalty/kasa-history', middleware.ensureLoggedIn, async (req, res) => {
|
|
306
|
-
// ... (Mevcut kodunun aynısı - sadece yetki kontrolü var)
|
|
307
324
|
try {
|
|
308
325
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
309
326
|
const isMod = await user.isGlobalModerator(req.uid);
|
|
@@ -312,7 +329,6 @@ Plugin.init = async function (params) {
|
|
|
312
329
|
const raw = await db.getListRange('niki:kasa:history', 0, -1);
|
|
313
330
|
const rows = (raw || []).map(safeParseMaybeJson).filter(Boolean).reverse();
|
|
314
331
|
|
|
315
|
-
// Kullanıcı detaylarını doldurma (Map logic)
|
|
316
332
|
const uids = rows.map(r => parseInt(r.cuid, 10)).filter(n => Number.isFinite(n) && n > 0);
|
|
317
333
|
const users = await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'icon:bgColor']);
|
|
318
334
|
const userMap = {};
|
|
@@ -331,7 +347,10 @@ Plugin.init = async function (params) {
|
|
|
331
347
|
};
|
|
332
348
|
});
|
|
333
349
|
return res.json(enriched);
|
|
334
|
-
} catch (e) {
|
|
350
|
+
} catch (e) {
|
|
351
|
+
console.error('[Niki-Loyalty] Kasa history error:', e);
|
|
352
|
+
return res.status(500).json([]);
|
|
353
|
+
}
|
|
335
354
|
});
|
|
336
355
|
|
|
337
356
|
// 4) QR OLUŞTURMA
|
|
@@ -566,10 +585,147 @@ Plugin.adminManagePoints = async function (socket, data) {
|
|
|
566
585
|
const newPoints = await user.getUserField(targetUid, 'niki_points');
|
|
567
586
|
return { success: true, newPoints: parseFloat(newPoints) };
|
|
568
587
|
};
|
|
588
|
+
// 4) KULLANICI DETAY (Admin için)
|
|
589
|
+
Plugin.adminGetUserDetail = async function (socket, data) {
|
|
590
|
+
const uid = socket.uid;
|
|
591
|
+
if (!uid) throw new Error('Giriş yapmalısınız.');
|
|
592
|
+
|
|
593
|
+
const isAdmin = await user.isAdministrator(uid);
|
|
594
|
+
const isMod = await user.isGlobalModerator(uid);
|
|
595
|
+
if (!isAdmin && !isMod) throw new Error('Yetkisiz Erişim');
|
|
596
|
+
|
|
597
|
+
const targetUid = data.uid;
|
|
598
|
+
if (!targetUid) throw new Error('Kullanıcı ID gerekli.');
|
|
599
|
+
|
|
600
|
+
// Kullanıcı bilgileri
|
|
601
|
+
const userData = await user.getUserFields(targetUid, [
|
|
602
|
+
'uid', 'username', 'userslug', 'picture', 'email',
|
|
603
|
+
'niki_points', 'icon:bgColor', 'joindate', 'lastonline'
|
|
604
|
+
]);
|
|
605
|
+
|
|
606
|
+
if (!userData || !userData.uid) throw new Error('Kullanıcı bulunamadı.');
|
|
607
|
+
|
|
608
|
+
// Aktivite geçmişi
|
|
609
|
+
const activityRaw = await db.getListRange(`niki:activity:${targetUid}`, 0, -1);
|
|
610
|
+
const activities = (activityRaw || []).map(safeParseMaybeJson).filter(Boolean).reverse();
|
|
611
|
+
|
|
612
|
+
// Kazanılan ve harcanan puanları ayır
|
|
613
|
+
let totalEarned = 0;
|
|
614
|
+
let totalSpent = 0;
|
|
615
|
+
const earnHistory = [];
|
|
616
|
+
const spendHistory = [];
|
|
617
|
+
|
|
618
|
+
activities.forEach(a => {
|
|
619
|
+
if (a.type === 'earn' || a.type === 'admin_adjust') {
|
|
620
|
+
if (a.type === 'admin_adjust' && a.txt && a.txt.includes('-')) {
|
|
621
|
+
totalSpent += parseFloat(a.amt) || 0;
|
|
622
|
+
spendHistory.push(a);
|
|
623
|
+
} else {
|
|
624
|
+
totalEarned += parseFloat(a.amt) || 0;
|
|
625
|
+
earnHistory.push(a);
|
|
626
|
+
}
|
|
627
|
+
} else if (a.type === 'spend') {
|
|
628
|
+
totalSpent += parseFloat(a.amt) || 0;
|
|
629
|
+
spendHistory.push(a);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Günlük limit durumu
|
|
634
|
+
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
635
|
+
const dailyData = await db.getObject(`niki:daily:${targetUid}:${today}`);
|
|
636
|
+
const actionCounts = await db.getObject(`niki:daily:${targetUid}:${today}:counts`);
|
|
637
|
+
|
|
638
|
+
// Bu kullanıcının kasa işlemleri (harcamaları)
|
|
639
|
+
const kasaRaw = await db.getListRange('niki:kasa:history', 0, -1);
|
|
640
|
+
const userKasaHistory = (kasaRaw || [])
|
|
641
|
+
.map(safeParseMaybeJson)
|
|
642
|
+
.filter(k => k && String(k.cuid) === String(targetUid))
|
|
643
|
+
.reverse()
|
|
644
|
+
.slice(0, 20);
|
|
645
|
+
|
|
646
|
+
const rp = require.main.require('nconf').get('relative_path') || '';
|
|
647
|
+
|
|
648
|
+
return {
|
|
649
|
+
user: {
|
|
650
|
+
uid: userData.uid,
|
|
651
|
+
username: userData.username,
|
|
652
|
+
userslug: userData.userslug,
|
|
653
|
+
picture: userData.picture || '',
|
|
654
|
+
email: userData.email || '',
|
|
655
|
+
iconBg: userData['icon:bgColor'] || '#4b5563',
|
|
656
|
+
points: parseFloat(userData.niki_points || 0),
|
|
657
|
+
joindate: userData.joindate,
|
|
658
|
+
lastonline: userData.lastonline,
|
|
659
|
+
profileUrl: userData.userslug ? `${rp}/user/${userData.userslug}` : ''
|
|
660
|
+
},
|
|
661
|
+
stats: {
|
|
662
|
+
totalEarned,
|
|
663
|
+
totalSpent,
|
|
664
|
+
currentPoints: parseFloat(userData.niki_points || 0),
|
|
665
|
+
todayScore: parseFloat(dailyData?.score || 0),
|
|
666
|
+
todayCounts: actionCounts || {}
|
|
667
|
+
},
|
|
668
|
+
earnHistory: earnHistory.slice(0, 30),
|
|
669
|
+
spendHistory: spendHistory.slice(0, 30),
|
|
670
|
+
kasaHistory: userKasaHistory,
|
|
671
|
+
actions: ACTIONS
|
|
672
|
+
};
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// 5) GENEL İSTATİSTİKLER (Dashboard için)
|
|
676
|
+
Plugin.adminGetStats = async function (socket, data) {
|
|
677
|
+
const uid = socket.uid;
|
|
678
|
+
if (!uid) throw new Error('Giriş yapmalısınız.');
|
|
679
|
+
|
|
680
|
+
const isAdmin = await user.isAdministrator(uid);
|
|
681
|
+
const isMod = await user.isGlobalModerator(uid);
|
|
682
|
+
if (!isAdmin && !isMod) throw new Error('Yetkisiz Erişim');
|
|
683
|
+
|
|
684
|
+
// Tüm kullanıcıları al
|
|
685
|
+
const uids = await db.getSortedSetRange('users:joindate', 0, 499);
|
|
686
|
+
if (!uids || uids.length === 0) return { users: 0, totalPoints: 0, avgPoints: 0 };
|
|
687
|
+
|
|
688
|
+
const usersData = await user.getUsersFields(uids, ['niki_points']);
|
|
689
|
+
|
|
690
|
+
let totalPoints = 0;
|
|
691
|
+
let usersWithPoints = 0;
|
|
692
|
+
|
|
693
|
+
usersData.forEach(u => {
|
|
694
|
+
const pts = parseFloat(u.niki_points || 0);
|
|
695
|
+
if (pts > 0) {
|
|
696
|
+
totalPoints += pts;
|
|
697
|
+
usersWithPoints++;
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// Kasa geçmişinden toplam harcama
|
|
702
|
+
const kasaRaw = await db.getListRange('niki:kasa:history', 0, -1);
|
|
703
|
+
const kasaData = (kasaRaw || []).map(safeParseMaybeJson).filter(Boolean);
|
|
704
|
+
const totalRedeemed = kasaData.reduce((sum, k) => sum + (parseFloat(k.amt) || 0), 0);
|
|
705
|
+
|
|
706
|
+
// Bugünkü işlemler
|
|
707
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
708
|
+
const todayTransactions = kasaData.filter(k => {
|
|
709
|
+
const d = new Date(k.ts).toISOString().slice(0, 10);
|
|
710
|
+
return d === today;
|
|
711
|
+
}).length;
|
|
712
|
+
|
|
713
|
+
return {
|
|
714
|
+
usersWithPoints,
|
|
715
|
+
totalPoints: Math.floor(totalPoints),
|
|
716
|
+
avgPoints: usersWithPoints > 0 ? Math.floor(totalPoints / usersWithPoints) : 0,
|
|
717
|
+
totalRedeemed: Math.floor(totalRedeemed),
|
|
718
|
+
totalTransactions: kasaData.length,
|
|
719
|
+
todayTransactions
|
|
720
|
+
};
|
|
721
|
+
};
|
|
722
|
+
|
|
569
723
|
// Soket'e kaydet (Client: socket.emit('plugins.niki.getUsers', ...))
|
|
570
724
|
if (SocketPlugins) {
|
|
571
725
|
SocketPlugins.niki = {
|
|
572
726
|
getUsers: Plugin.adminGetUsers,
|
|
727
|
+
getUserDetail: Plugin.adminGetUserDetail,
|
|
728
|
+
getStats: Plugin.adminGetStats,
|
|
573
729
|
scanQR: Plugin.socketScanQR,
|
|
574
730
|
getKasaHistory: Plugin.socketKasaHistory,
|
|
575
731
|
managePoints: Plugin.adminManagePoints
|