diff --git a/src/function/sync/get-server-data-status/campaign.ts b/src/function/sync/get-server-data-status/campaign.ts new file mode 100644 index 0000000..f32f508 --- /dev/null +++ b/src/function/sync/get-server-data-status/campaign.ts @@ -0,0 +1,64 @@ +/* + * server component for the TimeLimit App + * Copyright (C) 2019 - 2026 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { createHash } from 'crypto' + +const updateMessage = + 'Please update the TimeLimit App to the latest release. ' + + 'In your current version, synchronization and purchases may not work as expected.' + + '\n\n' + + 'Bitte die TimeLimit-App aktualisieren. ' + + 'Mit der momentan installierten Version kann es zu Einschränkungen bei der Synchronisation und bei Käufen kommen.' + +export function getCampaign({ familyId, isClient750OrNewer }: { + familyId: string + isClient750OrNewer: boolean +}): string | undefined { + const now = Date.now() + + const campaigns = [ + { + enable: !isClient750OrNewer, + seed: 'Q05NwM4nyUfiYg6K', // require('crypto').randomBytes(12).toString('base64') + startTime: 1784160000000, // new Date('2026-07-16').valueOf() + endTime: 1785369600000, // new Date('2026-07-30').valueOf() + startPercentage: 0, + endPercentage: 10, + message: updateMessage, + } + ] + + for (const campaign of campaigns) { + if (!campaign.enable) continue + if (now < campaign.startTime) continue + + const progress = Math.min((now - campaign.startTime) / (campaign.endTime - campaign.startTime), 1) + const chanceInPercent = campaign.startPercentage + progress * (campaign.endPercentage - campaign.startPercentage) + + const hasher = createHash('SHA3-224') + + hasher.update(campaign.seed, 'base64') + hasher.update(familyId) + + const hash = hasher.digest().readUInt32BE(0) + const match = hash < chanceInPercent * Math.pow(2, 32) / 100 + + if (match) return campaign.message + } + + return undefined +} diff --git a/src/function/sync/get-server-data-status/index.ts b/src/function/sync/get-server-data-status/index.ts index 6eb391e..7d54575 100644 --- a/src/function/sync/get-server-data-status/index.ts +++ b/src/function/sync/get-server-data-status/index.ts @@ -22,6 +22,7 @@ import { getStatusMessage } from '../../../function/statusmessage' import { ClientDataStatus } from '../../../object/clientdatastatus' import { ServerDataStatus } from '../../../object/serverdatastatus' import { EventHandler } from '../../../monitoring/eventhandler' +import { getCampaign } from './campaign' import { getCategoryAssignedApps, getCategoryBaseDatas, getCategoryDataToSync, getRules, getTasks, getUsedTimes @@ -54,12 +55,18 @@ export const generateServerDataStatus = async ({ const doesClientSupportDh = clientLevel >= 5 const doesClientSupportU2f = clientLevel >= 6 const doesClientSupportPing = clientLevel >= 7 + const isClient750OrNewer = clientLevel >= 8 // first release in the post gplay time + + const message: string | undefined = + await getStatusMessage({ database, transaction }) || + getCampaign({ familyId, isClient750OrNewer }) || + undefined const result: ServerDataStatus = { fullVersion: config.alwaysPro ? 1 : ( familyEntry.hasFullVersion ? parseInt(familyEntry.fullVersionUntil, 10) : 0 ), - message: await getStatusMessage({ database, transaction }) || undefined, + message, apiLevel: 9 }