@aituber-onair/kizuna
Version:
A sophisticated bond system (絆 - Kizuna) for managing relationships between users and AI characters in AITuber OnAir.
295 lines • 9.89 kB
JavaScript
/**
* PointCalculator - Point calculation class
*
* Manages platform-specific point rules and performs point calculations
*/
/**
* Point calculation class
*/
export class PointCalculator {
constructor(config) {
this.cooldowns = new Map();
this.config = config;
}
/**
* Main method for point calculation
*/
calculatePoints(context, user) {
const result = {
points: 0,
appliedRules: [],
breakdown: [],
};
// Calculate base points
const basePoints = this.calculateBasePoints(context, user);
if (basePoints > 0) {
result.points += basePoints;
result.breakdown.push({
ruleName: "base_points",
points: basePoints,
description: `Base points (${context.platform})`,
});
}
// Apply platform-specific rules
const platformRules = this.applyPlatformRules(context, user);
result.points += platformRules.points;
result.appliedRules.push(...platformRules.appliedRules);
result.breakdown.push(...platformRules.breakdown);
// Apply custom rules
const customRules = this.applyCustomRules(context, user);
result.points += customRules.points;
result.appliedRules.push(...customRules.appliedRules);
result.breakdown.push(...customRules.breakdown);
// Apply owner multiplier
if (user.type === "owner") {
const multiplier = this.config.owner.pointMultiplier;
const bonusPoints = Math.floor(result.points * (multiplier - 1));
if (bonusPoints > 0) {
result.points += bonusPoints;
result.breakdown.push({
ruleName: "owner_multiplier",
points: bonusPoints,
description: `Owner multiplier bonus (×${multiplier})`,
});
}
}
// Check daily bonus
const dailyBonus = this.checkDailyBonus(context, user);
if (dailyBonus > 0) {
result.points += dailyBonus;
result.breakdown.push({
ruleName: "daily_bonus",
points: dailyBonus,
description: "Daily bonus",
});
}
this.log(`Points calculated for ${user.id}: ${result.points} (${result.appliedRules.length} rules applied)`);
return result;
}
/**
* Check if a specific rule can be applied
*/
canApplyRule(rule, context, user) {
const cooldownKey = `${user.id}:${rule.id}`;
if (this.config.dev.debugMode) {
this.log(`[canApplyRule] Checking rule: ${rule.id} for emotion: ${context.emotion}`);
}
// Cooldown check
if (rule.cooldown && rule.cooldown > 0) {
const record = this.cooldowns.get(cooldownKey);
if (record) {
const timePassed = Date.now() - record.lastApplied;
if (timePassed < rule.cooldown) {
if (this.config.dev.debugMode) {
this.log(`[canApplyRule] Rule ${rule.id} blocked by cooldown (${timePassed}ms < ${rule.cooldown}ms)`);
}
return false;
}
}
}
// Daily limit check
if (rule.dailyLimit && rule.dailyLimit > 0) {
const record = this.getCooldownRecord(cooldownKey);
if (record.todayCount >= rule.dailyLimit) {
if (this.config.dev.debugMode) {
this.log(`[canApplyRule] Rule ${rule.id} blocked by daily limit (${record.todayCount} >= ${rule.dailyLimit})`);
}
return false;
}
}
// Condition check
try {
const conditionResult = rule.condition(context, user);
if (this.config.dev.debugMode) {
this.log(`[canApplyRule] Rule ${rule.id} condition result: ${conditionResult}`);
}
return conditionResult;
}
catch (error) {
this.log(`Error evaluating rule condition for ${rule.id}: ${error}`);
return false;
}
}
/**
* Record rule application (cooldown management)
*/
recordRuleApplication(rule, userId) {
const cooldownKey = `${userId}:${rule.id}`;
const record = this.getCooldownRecord(cooldownKey);
record.lastApplied = Date.now();
record.todayCount++;
this.cooldowns.set(cooldownKey, record);
}
/**
* Clear cooldown data
*/
clearCooldowns() {
this.cooldowns.clear();
}
/**
* Reset cooldowns for specific user
*/
resetUserCooldowns(userId) {
for (const [key] of this.cooldowns) {
if (key.startsWith(`${userId}:`)) {
this.cooldowns.delete(key);
}
}
}
// ============================================================================
// Private methods
// ============================================================================
/**
* Calculate base points
*/
calculateBasePoints(context, user) {
const platformConfig = this.getPlatformConfig(context.platform);
if (!platformConfig) {
return 1; // Default points
}
// Platform-specific base points
const actionType = this.getActionType(context);
return platformConfig.basePoints[actionType] || 1;
}
/**
* Apply platform-specific rules
*/
applyPlatformRules(context, user) {
const result = {
points: 0,
appliedRules: [],
breakdown: [],
};
const platformConfig = this.getPlatformConfig(context.platform);
if (!platformConfig?.customRules) {
return result;
}
for (const rule of platformConfig.customRules) {
if (this.canApplyRule(rule, context, user)) {
result.points += rule.points;
result.appliedRules.push(rule);
result.breakdown.push({
ruleName: rule.id,
points: rule.points,
description: rule.description || rule.name,
});
this.recordRuleApplication(rule, user.id);
}
}
// Platform-specific bonus calculation
if (platformConfig.bonusCalculator) {
try {
const bonus = platformConfig.bonusCalculator(context);
if (bonus > 0) {
result.points += bonus;
result.breakdown.push({
ruleName: "platform_bonus",
points: bonus,
description: `${context.platform} platform bonus`,
});
}
}
catch (error) {
this.log(`Error in platform bonus calculator: ${error}`);
}
}
return result;
}
/**
* Apply custom rules
*/
applyCustomRules(context, user) {
const result = {
points: 0,
appliedRules: [],
breakdown: [],
};
const customRules = this.config.customRules || [];
for (const rule of customRules) {
if (this.canApplyRule(rule, context, user)) {
result.points += rule.points;
result.appliedRules.push(rule);
result.breakdown.push({
ruleName: rule.id,
points: rule.points,
description: rule.description || rule.name,
});
this.recordRuleApplication(rule, user.id);
}
}
return result;
}
/**
* Check daily bonus
*/
checkDailyBonus(context, user) {
// No daily bonus for non-owners
if (user.type !== "owner") {
return 0;
}
const bonusKey = `${user.id}:daily_bonus`;
const record = this.getCooldownRecord(bonusKey);
// If bonus already received today
if (record.todayCount > 0) {
return 0;
}
// Award daily bonus
record.todayCount = 1;
record.lastApplied = Date.now();
this.cooldowns.set(bonusKey, record);
return this.config.owner.dailyBonus;
}
/**
* Get platform configuration
*/
getPlatformConfig(platform) {
return this.config.platforms[platform] || null;
}
/**
* Get action type
*/
getActionType(context) {
// Get action type from metadata
if (context.metadata?.actionType) {
return context.metadata.actionType;
}
// Platform-specific default actions
switch (context.platform) {
case "youtube":
return "comment";
case "twitch":
return "chat";
case "websocket":
return "message";
default:
return "message";
}
}
/**
* Get or create cooldown record
*/
getCooldownRecord(key) {
const today = new Date().toISOString().split("T")[0] || ""; // YYYY-MM-DD
const existing = this.cooldowns.get(key);
if (existing && existing.today === today) {
return existing;
}
// New day or first time
const newRecord = {
lastApplied: 0,
todayCount: 0,
today: today,
};
this.cooldowns.set(key, newRecord);
return newRecord;
}
/**
* Log output
*/
log(message) {
if (this.config.dev.debugMode) {
console.log(`[PointCalculator] ${message}`);
}
}
}
//# sourceMappingURL=PointCalculator.js.map