nodebb-plugin-niki-loyalty 1.5.10 → 1.6.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.
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(node:*)"
4
+ "Bash(node:*)",
5
+ "WebSearch",
6
+ "WebFetch(domain:www.kentkart.com)",
7
+ "WebFetch(domain:odepos.com)"
5
8
  ]
6
9
  }
7
10
  }
package/library.js CHANGED
@@ -10,8 +10,14 @@ const SocketPlugins = require.main.require('./src/socket.io/plugins');
10
10
  const groups = require.main.require('./src/groups');
11
11
  const Plugin = {};
12
12
 
13
- // Ödül kullanabilecek gruplar
14
- const WALLET_GROUPS = ['Premium', 'Lite', 'VIP'];
13
+ // Grup bazlı kazanım çarpanları (en yüksek üyelik kazanır).
14
+ // Bu gruplara üye olmayan kullanıcılar DEFAULT_EARN_RATE alır.
15
+ const EARN_RATES = {
16
+ VIP: 1.5,
17
+ Premium: 1.25,
18
+ Lite: 1.0,
19
+ };
20
+ const DEFAULT_EARN_RATE = 0.5;
15
21
 
16
22
  // =========================
17
23
  // ⚙️ AYARLAR & KURALLAR (GAME LOGIC)
@@ -136,6 +142,24 @@ async function addKasaLog(staffUid, customerName, customerUid, rewardName, amoun
136
142
  await db.listTrim('niki:kasa:history', -100, -1);
137
143
  }
138
144
 
145
+ // Kademeli çarpan: VIP 1.5x, Premium 1.25x, Lite 1.0x, üyelik yok 0.5x.
146
+ // Birden fazla gruba üyeyse en yüksek çarpan uygulanır.
147
+ async function getEarnMultiplier(uid) {
148
+ try {
149
+ const groupNames = Object.keys(EARN_RATES);
150
+ const memberChecks = await Promise.all(groupNames.map(g => groups.isMember(uid, g)));
151
+ let maxRate = DEFAULT_EARN_RATE;
152
+ groupNames.forEach((g, i) => {
153
+ if (memberChecks[i] && EARN_RATES[g] > maxRate) {
154
+ maxRate = EARN_RATES[g];
155
+ }
156
+ });
157
+ return maxRate;
158
+ } catch (err) {
159
+ return DEFAULT_EARN_RATE;
160
+ }
161
+ }
162
+
139
163
  // 🔥 MERKEZİ PUAN DAĞITIM FONKSİYONU 🔥
140
164
  // Bütün puan işlemleri buradan geçer, limitleri kontrol eder.
141
165
  async function awardDailyAction(uid, actionKey) {
@@ -165,8 +189,9 @@ async function awardDailyAction(uid, actionKey) {
165
189
  return { success: false, reason: 'action_limit_reached' };
166
190
  }
167
191
 
168
- // 3. Puan Hesapla
169
- let pointsToGive = rule.points;
192
+ // 3. Puan Hesapla (premium-dışı kullanıcı için yarım puan)
193
+ const multiplier = await getEarnMultiplier(uid);
194
+ let pointsToGive = rule.points * multiplier;
170
195
  if (currentDailyScore + pointsToGive > SETTINGS.dailyCap) {
171
196
  pointsToGive = SETTINGS.dailyCap - currentDailyScore;
172
197
  }
@@ -393,18 +418,26 @@ Plugin.init = async function (params) {
393
418
  const uid = req.uid;
394
419
  const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
395
420
 
421
+ const earnGroupNames = Object.keys(EARN_RATES);
422
+
396
423
  // Veritabanından verileri çek
397
424
  const [userData, dailyData, actionCounts, historyRaw, memberChecks] = await Promise.all([
398
425
  user.getUserFields(uid, ['niki_points']),
399
426
  db.getObject(`niki:daily:${uid}:${today}`),
400
427
  db.getObject(`niki:daily:${uid}:${today}:counts`),
401
428
  db.getListRange(`niki:activity:${uid}`, 0, -1),
402
- Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g))),
429
+ Promise.all(earnGroupNames.map(g => groups.isMember(uid, g))),
403
430
  ]);
404
431
 
405
- // Kullanıcı WALLET_GROUPS'dan herhangi birinde mi?
406
- const canRedeem = memberChecks.some(Boolean);
407
- const userGroup = canRedeem ? WALLET_GROUPS[memberChecks.indexOf(true)] : null;
432
+ // En yüksek kazanım grubunu seç (üye olduğu birden fazla varsa en iyisi).
433
+ let earnRate = DEFAULT_EARN_RATE;
434
+ let userGroup = null;
435
+ earnGroupNames.forEach((g, i) => {
436
+ if (memberChecks[i] && EARN_RATES[g] > earnRate) {
437
+ earnRate = EARN_RATES[g];
438
+ userGroup = g;
439
+ }
440
+ });
408
441
 
409
442
  const dailyScore = parseFloat(dailyData?.score || 0);
410
443
  let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
@@ -421,9 +454,10 @@ Plugin.init = async function (params) {
421
454
  actions: ACTIONS,
422
455
  history,
423
456
  rewards: REWARDS,
424
- canRedeem,
457
+ canRedeem: true,
425
458
  userGroup,
426
- walletGroups: WALLET_GROUPS,
459
+ earnRate,
460
+ walletGroups: earnGroupNames,
427
461
  });
428
462
  } catch (err) {
429
463
  return res.status(500).json({ points: 0, history: [] });
@@ -543,12 +577,6 @@ Plugin.init = async function (params) {
543
577
  try {
544
578
  const uid = req.uid;
545
579
 
546
- // Grup kontrolü
547
- const memberChecks = await Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g)));
548
- if (!memberChecks.some(Boolean)) {
549
- return res.json({ success: false, message: 'Ödül kullanmak için Premium, Lite veya VIP grubuna katılmalısın.' });
550
- }
551
-
552
580
  const rewardIndex = parseInt(req.body.rewardIndex, 10);
553
581
  const points = parseFloat((await user.getUserField(uid, 'niki_points')) || 0);
554
582
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-niki-loyalty",
3
- "version": "1.5.10",
3
+ "version": "1.6.0",
4
4
  "description": "Niki The Cat Coffee Loyalty System - Earn points while studying on IEU Forum.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {
@@ -330,6 +330,42 @@ $(document).ready(function () {
330
330
  $('#daily-score').text(formatPoints(data.dailyScore));
331
331
  $('#daily-cap').text(data.dailyCap);
332
332
 
333
+ // Kademeli upsell rozeti: bir üst kademeyi göster (VIP'te yok)
334
+ $('#niki-earn-rate-badge').remove();
335
+ const TIERS = [
336
+ { rate: 0.5, label: null },
337
+ { rate: 1.0, label: 'Lite' },
338
+ { rate: 1.25, label: 'Premium' },
339
+ { rate: 1.5, label: 'VIP' },
340
+ ];
341
+ const currentRate = parseFloat(data.earnRate) || 0.5;
342
+ const currentIdx = TIERS.findIndex(t => Math.abs(t.rate - currentRate) < 0.01);
343
+ const nextTier = currentIdx >= 0 ? TIERS[currentIdx + 1] : null;
344
+
345
+ if (nextTier && $('#user-points').length) {
346
+ const factor = (nextTier.rate / currentRate).toFixed(2).replace(/\.?0+$/, '');
347
+ const badgeHtml = `
348
+ <div id="niki-earn-rate-badge" style="
349
+ display: inline-flex;
350
+ align-items: center;
351
+ gap: 6px;
352
+ margin-top: 10px;
353
+ padding: 6px 12px;
354
+ background: linear-gradient(135deg, #FFD700 0%, #FFA000 100%);
355
+ color: #3E2723;
356
+ border-radius: 20px;
357
+ font-size: 12px;
358
+ font-weight: 700;
359
+ box-shadow: 0 2px 6px rgba(0,0,0,0.15);
360
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
361
+ ">
362
+ <span>💎</span>
363
+ <span>${nextTier.label} ile <strong style="font-size:13px;">${factor}x</strong> daha hızlı kazan</span>
364
+ </div>
365
+ `;
366
+ $('#user-points').after(badgeHtml);
367
+ }
368
+
333
369
  // Progress Bar
334
370
  const percent = data.dailyPercent > 100 ? 100 : data.dailyPercent;
335
371
  $('#daily-progress').css('width', percent + '%').text(Math.round(percent) + '%');