From 9ce11a27bd6a67c2f98ce4b11ec560e2fa25b59f Mon Sep 17 00:00:00 2001 From: floele Date: Mon, 27 Jan 2025 17:16:24 +0100 Subject: [PATCH 1/2] Updated gold VIP settings for full switch to permanent requests --- server/src/controllers/settingsController.ts | 8 ++++---- server/src/services/rewardService.ts | 14 ++++++++++++-- server/src/services/userService.ts | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/server/src/controllers/settingsController.ts b/server/src/controllers/settingsController.ts index 5dc5cfae..125f7697 100644 --- a/server/src/controllers/settingsController.ts +++ b/server/src/controllers/settingsController.ts @@ -11,7 +11,7 @@ import BotSettingsService, { BotSettings } from "../services/botSettingsService" class SettingsController { private readonly SettingDescriptions = { [BotSettings.DonationPointsPerDollar]: { title: "Donations: Points added per USD", readonly: false }, - [BotSettings.GoldStatusDonationAmount]: { title: "VIP Gold: USD required per month", readonly: false }, + [BotSettings.GoldStatusDonationAmount]: { title: "VIP Gold: USD required for 4 weeks / requests", readonly: false }, [BotSettings.PointsPerBit]: { title: "Bits: Points added per Bit", readonly: false }, [BotSettings.PruneLogsAfterDays]: { title: "Amount of days to retain logs", readonly: false }, [BotSettings.RedeemCost]: { title: "Cost for !redeem command", readonly: false }, @@ -30,7 +30,7 @@ class SettingsController { [BotSettings.SongDonationLink]: { title: "URL for donations on song queue page", readonly: false }, [BotSettings.CardsRequiredForUpgrade]: { title: "Number of cards required for redeeming an upgrade", readonly: false }, [BotSettings.CommandCooldownInSeconds]: { title: "Cooldown for regular text commands (in seconds)", readonly: false }, - [BotSettings.GoldWeeksPerT3Sub]: { title: "Amount of VIP gold weeks per T3 sub", readonly: false }, + [BotSettings.GoldWeeksPerT3Sub]: { title: "VIP Gold: Amount of weeks / requests per T3 sub", readonly: false }, [BotSettings.ReadonlyMode]: { title: "Read-only mode for points", readonly: false }, [BotSettings.MaxSongRequestRedemptionsInQueue]: { title: "Maximum number of song requests through channel rewards in queue", readonly: false }, [BotSettings.SubNotificationProvider]: { title: "Provider for subscription notifications (Twitch|Streamlabs)", readonly: false }, @@ -48,8 +48,8 @@ class SettingsController { [BotSettings.TaxInspectorExemptUsers]: { title: "Users exempt from tax inspector", readonly: false }, [BotSettings.TaxEvasionPenaltyLeaderboardCount]: { title: "Number of top users from tax evasion leaderboard for penalty", readonly: false }, [BotSettings.TaxEvasionCooldown]: { title: "Number of minutes that tax evaders are safe after penalty", readonly: false }, - [BotSettings.GoldStatusRequestLimit]: { title: "Limit VIP gold requests by 1 per week or stream (Week|Stream)", readonly: false }, - [BotSettings.GoldStatusPermanentRequests]: { title: "Grant permanent VIP gold requests for donations", readonly: false }, + [BotSettings.GoldStatusRequestLimit]: { title: "VIP Gold: Limit requests to 1 per week or stream (Week|Stream)", readonly: false }, + [BotSettings.GoldStatusPermanentRequests]: { title: "VIP Gold: Use permanent requests instead of time periods", readonly: false }, }; constructor( diff --git a/server/src/services/rewardService.ts b/server/src/services/rewardService.ts index 41d36cda..930d047c 100644 --- a/server/src/services/rewardService.ts +++ b/server/src/services/rewardService.ts @@ -91,7 +91,12 @@ export default class RewardService { if (sub.sub_plan === SubscriptionPlan.Tier3) { const goldWeeksT3 = parseInt(await this.settings.getValue(BotSettings.GoldWeeksPerT3Sub), 10); - await this.userService.addVipGoldWeeks(user, goldWeeksT3, "T3 Resub"); + const usePermanentRequests = await this.settings.getBoolValue(BotSettings.GoldStatusPermanentRequests); + if (usePermanentRequests) { + await this.userService.addPermanentVip(user, goldWeeksT3, "T3 Resub"); + } else { + await this.userService.addVipGoldWeeks(user, goldWeeksT3, "T3 Resub"); + } } } @@ -104,7 +109,12 @@ export default class RewardService { // Both gifter and receiver gets half the amount of VIP gold. // We assume that the user on the receiving end will be covered by a streamlabs event. if (giftingUser) { - await this.userService.addVipGoldWeeks(giftingUser, giftedMonths, "Gifted T3 sub"); + const usePermanentRequests = await this.settings.getBoolValue(BotSettings.GoldStatusPermanentRequests); + if (usePermanentRequests) { + await this.userService.addPermanentVip(giftingUser, giftedMonths, "Gifted T3 sub"); + } else { + await this.userService.addVipGoldWeeks(giftingUser, giftedMonths, "Gifted T3 sub"); + } } } } diff --git a/server/src/services/userService.ts b/server/src/services/userService.ts index 394ba6a0..996a7ad5 100644 --- a/server/src/services/userService.ts +++ b/server/src/services/userService.ts @@ -9,6 +9,7 @@ import { Logger, LogType } from "../logger"; import PointLogsRepository from "../database/pointLogsRepository"; import EventAggregator from "./eventAggregator"; import WebsocketService from "../services/websocketService"; +import BotSettingsService, { BotSettings } from "../services/botSettingsService"; @injectable() export class UserService { @@ -17,7 +18,8 @@ export class UserService { @inject(EventLogService) private eventLog: EventLogService, @inject(EventAggregator) private eventAggregator: EventAggregator, @inject(WebsocketService) private websocketService: WebsocketService, - @inject(PointLogsRepository) private pointsLog: PointLogsRepository) { + @inject(PointLogsRepository) private pointsLog: PointLogsRepository, + @inject(BotSettingsService) private settings: BotSettingsService) { // Empty } @@ -179,7 +181,15 @@ export class UserService { */ public async addPermanentVip(user: IUser, amount: number, reason: string) { user.vipPermanentRequests = user.vipPermanentRequests ? (user.vipPermanentRequests + amount) : amount; - await this.addVipGoldWeeks(user, amount, reason); + + const usePermanentRequests = await this.settings.getBoolValue(BotSettings.GoldStatusPermanentRequests); + if (usePermanentRequests) { + // Not changing VIP expiry date here to limit available requests to the permanent ones + await this.users.updateVipExpiry(user); + await this.eventLog.addVipGoldAdded(user, { weeksAdded: 0, newExpiry: user.vipExpiry, permanentRequests: user.vipPermanentRequests, reason }); + } else { + await this.addVipGoldWeeks(user, amount, reason); + } } /** From 74e4ed044168bd523b175257c5cf16afab245480 Mon Sep 17 00:00:00 2001 From: floele Date: Sat, 8 Feb 2025 10:51:02 +0100 Subject: [PATCH 2/2] Updated chewiemelodies logo --- client/public/assets/chewie.jpg | Bin 4331 -> 5094 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/client/public/assets/chewie.jpg b/client/public/assets/chewie.jpg index c87e11973e68d6302469b43bc03ff0909685d5ac..4914861b2cb32cc60a0f2789c46bcb9eb9027397 100644 GIT binary patch literal 5094 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 literal 4331 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