Skip to content

Commit

Permalink
Merge pull request #1743 from gettakaro/issue689
Browse files Browse the repository at this point in the history
Daily rewards module
  • Loading branch information
niekcandaele authored Oct 29, 2024
2 parents d1231a5 + 266e2d3 commit efc6cbe
Show file tree
Hide file tree
Showing 9 changed files with 468 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/app-api/src/domainInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async function main() {
const results = await Promise.allSettled(domains.results.map(ctx.wrap('domainInit', domainInit)));
const rejected = results.map((r) => (r.status === 'rejected' ? r.reason : null)).filter(Boolean);
if (rejected.length) {
log.error('Failed to initialize some domains', { errors: rejected });
log.error('Failed to initialize some domains', { errors: JSON.stringify(rejected) });
}

process.exit(0);
Expand Down
2 changes: 2 additions & 0 deletions packages/lib-modules/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BuiltinModule } from './BuiltinModule.js';
import { ChatBridge } from './modules/chatBridge/index.js';
import { DailyRewards } from './modules/dailyRewards/index.js';
import { EconomyUtils } from './modules/economyUtils/index.js';
import { GeoBlock } from './modules/geoBlock/index.js';
import { Gimme } from './modules/gimme/index.js';
Expand Down Expand Up @@ -30,6 +31,7 @@ export function getModules(): Array<BuiltinModule<unknown>> {
new Lottery(),
new GeoBlock(),
new TimedShutdown(),
new DailyRewards(),
];
}

Expand Down
118 changes: 118 additions & 0 deletions packages/lib-modules/src/modules/dailyRewards/commands/daily.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';
import { DAILY_KEY, STREAK_KEY, getMultiplier } from './utils.js';

async function main() {
const { pog, gameServerId, module: mod } = data;

if (!checkPermission(pog, 'DAILY_CLAIM')) {
throw new TakaroUserError('You do not have permission to claim daily rewards.');
}

// Get last claim time
const lastClaimRes = await takaro.variable.variableControllerSearch({
filters: {
key: [DAILY_KEY],
gameServerId: [gameServerId],
playerId: [pog.playerId],
moduleId: [mod.moduleId],
},
});

const now = new Date();
let streak = 1;

if (lastClaimRes.data.data.length > 0) {
const lastClaim = new Date(JSON.parse(lastClaimRes.data.data[0].value));
const hoursSinceLastClaim = (now - lastClaim) / (1000 * 60 * 60);

// Check if 24 hours have passed
if (hoursSinceLastClaim < 24) {
const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);
throw new TakaroUserError(`You can claim your next reward at ${nextClaimTime.toLocaleString()}`);
}

// Get current streak
const streakRes = await takaro.variable.variableControllerSearch({
filters: {
key: [STREAK_KEY],
gameServerId: [gameServerId],
playerId: [pog.playerId],
moduleId: [mod.moduleId],
},
});

if (streakRes.data.data.length > 0) {
// If claimed within 48 hours, increment streak
if (hoursSinceLastClaim < 48) {
streak = Math.min(JSON.parse(streakRes.data.data[0].value) + 1, mod.userConfig.maxStreak);
await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {
value: JSON.stringify(streak),
});
} else {
// Reset streak if more than 48 hours
await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {
value: JSON.stringify(1),
});
}
} else {
// Create new streak record
await takaro.variable.variableControllerCreate({
key: STREAK_KEY,
value: JSON.stringify(1),
gameServerId,
playerId: pog.playerId,
moduleId: mod.moduleId,
});
}

// Update last claim time
await takaro.variable.variableControllerUpdate(lastClaimRes.data.data[0].id, {
value: JSON.stringify(now),
});
} else {
// First time claim
await takaro.variable.variableControllerCreate({
key: DAILY_KEY,
value: JSON.stringify(now),
gameServerId,
playerId: pog.playerId,
moduleId: mod.moduleId,
});
await takaro.variable.variableControllerCreate({
key: STREAK_KEY,
value: JSON.stringify(1),
gameServerId,
playerId: pog.playerId,
moduleId: mod.moduleId,
});
}

const multiplier = await getMultiplier(pog);
const baseReward = mod.userConfig.baseReward * streak * multiplier;
let bonusReward = 0;
let milestoneMessage = '';

// Check for milestones
for (const milestone of mod.userConfig.milestoneRewards) {
if (streak === milestone.days) {
bonusReward = milestone.reward;
milestoneMessage = `\n${milestone.message}`;
break;
}
}

// Award total rewards
const totalReward = baseReward + bonusReward;
await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, {
currency: totalReward,
});

const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;
await pog.pm(
`Daily reward claimed! You received ${totalReward} ${currencyName}\n` +
`Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x bonus!)` : ''}` +
milestoneMessage,
);
}

await main();
42 changes: 42 additions & 0 deletions packages/lib-modules/src/modules/dailyRewards/commands/streak.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { data, takaro } from '@takaro/helpers';
import { getPlayerStreak, getLastClaim, getMultiplier } from './utils.js';

async function main() {
const { pog, gameServerId, module: mod } = data;

const streak = await getPlayerStreak(gameServerId, pog.playerId, mod.moduleId);
const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);
const multiplier = await getMultiplier(pog);
const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;

if (!streak || !lastClaim) {
await pog.pm(`You haven't claimed any daily rewards yet! Use ${prefix}daily to get started.`);
return;
}

const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);
const now = new Date();
const canClaim = now >= nextClaimTime;

// Find next milestone
let nextMilestone = null;
for (const milestone of mod.userConfig.milestoneRewards) {
if (milestone.days > streak) {
nextMilestone = milestone;
break;
}
}

let message = `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x donor bonus!)` : ''}\n`;
message += canClaim
? `Your daily reward is available! Use ${prefix}daily to claim it!\n`
: `Next reward available at: ${nextClaimTime.toLocaleString()}\n`;

if (nextMilestone) {
message += `\n🎯 Next milestone: ${nextMilestone.days} days (${nextMilestone.days - streak} days to go!)`;
}

await pog.pm(message);
}

await main();
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { takaro, data } from '@takaro/helpers';
import { STREAK_KEY } from './utils.js';

async function main() {
const { pog, gameServerId, module: mod, arguments: args } = data;

// Limit count to reasonable number
const count = Math.min(Math.max(1, args.count), 50);

// Get all streaks
const streaksRes = await takaro.variable.variableControllerSearch({
filters: {
key: [STREAK_KEY],
gameServerId: [gameServerId],
moduleId: [mod.moduleId],
},
limit: 1000, // Get all possible streaks
});

if (streaksRes.data.data.length === 0) {
await pog.pm('No players have started their daily streak yet!');
return;
}

// Sort by streak value
const sortedStreaks = streaksRes.data.data
.map((record) => ({
playerId: record.playerId,
streak: JSON.parse(record.value),
}))
.sort((a, b) => b.streak - a.streak)
.slice(0, count);

// Get player names
const playerDetails = await Promise.all(
sortedStreaks.map(async (record) => {
const player = (await takaro.player.playerControllerGetOne(record.playerId)).data.data;
return {
name: player.name,
streak: record.streak,
};
}),
);

// Build message
let message = `Top ${count} Daily Streaks:\n\n`;
playerDetails.forEach((player, index) => {
message += `${index + 1}. ${player.name}: ${player.streak} days\n`;
});

await pog.pm(message);
}

await main();
36 changes: 36 additions & 0 deletions packages/lib-modules/src/modules/dailyRewards/functions/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { takaro, checkPermission } from '@takaro/helpers';

export const DAILY_KEY = 'daily_timestamp';
export const STREAK_KEY = 'daily_streak';

export async function getMultiplier(pog) {
const perm = checkPermission(pog, 'DAILY_REWARD_MULTIPLIER');
if (perm) return perm.count;
return 1;
}

export async function getPlayerStreak(gameServerId, playerId, moduleId) {
const streakRes = await takaro.variable.variableControllerSearch({
filters: {
key: [STREAK_KEY],
gameServerId: [gameServerId],
playerId: [playerId],
moduleId: [moduleId],
},
});

return streakRes.data.data.length ? parseInt(JSON.parse(streakRes.data.data[0].value)) : 0;
}

export async function getLastClaim(gameServerId, playerId, moduleId) {
const lastClaimRes = await takaro.variable.variableControllerSearch({
filters: {
key: [DAILY_KEY],
gameServerId: [gameServerId],
playerId: [playerId],
moduleId: [moduleId],
},
});

return lastClaimRes.data.data.length ? new Date(JSON.parse(lastClaimRes.data.data[0].value)) : null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { data, takaro } from '@takaro/helpers';
import { getLastClaim } from './utils.js';

async function main() {
const { pog, gameServerId, module: mod } = data;
const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;

const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);

// First time player
if (!lastClaim) {
await pog.pm(`Welcome! Use ${prefix}daily to claim your first daily reward and start your streak!`);
return;
}

const now = new Date();
const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);

if (now >= nextClaimTime) {
await pog.pm(`Your daily reward is ready! Use ${prefix}daily to claim it!`);
}
}

await main();
Loading

0 comments on commit efc6cbe

Please sign in to comment.