From be1102119a9c3fe65859ff396fb8010cc26fe265 Mon Sep 17 00:00:00 2001 From: devilreef <86633411+devilr33f@users.noreply.github.com> Date: Sat, 17 Jan 2026 00:41:21 +0600 Subject: [PATCH] feat: add hytale-presskit module for Creator Presskit tracking Adds new module to track CreatorPressKit.zip file size changes via HEAD requests. Alerts with old/new size and diff. Also updates sign-token.ts with correct account ID. Co-Authored-By: Claude --- scripts/sign-token.ts | 2 +- src/index.ts | 2 + src/modules/hytale-presskit/formatter.ts | 31 ++++++++++ src/modules/hytale-presskit/index.ts | 73 ++++++++++++++++++++++++ src/modules/hytale-presskit/tracker.ts | 46 +++++++++++++++ 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/modules/hytale-presskit/formatter.ts create mode 100644 src/modules/hytale-presskit/index.ts create mode 100644 src/modules/hytale-presskit/tracker.ts diff --git a/scripts/sign-token.ts b/scripts/sign-token.ts index fb26b28..93ae103 100644 --- a/scripts/sign-token.ts +++ b/scripts/sign-token.ts @@ -24,7 +24,7 @@ const jwtSecret = process.env.JWT_SECRET || 'your-secret-key' const payload = { scope: scopes, - sub: 'test-client', + sub: 'starkow', iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + (expirationHours * 3600), } diff --git a/src/index.ts b/src/index.ts index dccf3b5..3cf5449 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { discordForwarderFactory } from './modules/discord-forwarder/index.js' import { hytaleDownloaderFactory } from './modules/hytale-downloader/index.js' import { hytaleLauncherFactory } from './modules/hytale-launcher/index.js' import { hytalePatchesFactory } from './modules/hytale-patches/index.js' +import { hytalePresskitFactory } from './modules/hytale-presskit/index.js' import { hytaleServerFactory } from './modules/hytale-server/index.js' const MODULE_FACTORIES: Record Module> = { @@ -18,6 +19,7 @@ const MODULE_FACTORIES: Record📦 Hytale Creator Presskit Update + +New size: ${formatBytes(size)}` + + if (previousSize !== null) { + const diffStr = sizeDiff > 0 ? `+${formatBytes(sizeDiff)}` : formatBytes(sizeDiff) + const diffSign = sizeDiff > 0 ? '📈' : '📉' + message += ` +Previous size: ${formatBytes(previousSize)} +Difference: ${diffStr} ${diffSign}` + } + + message += ` +Date: ${date}` + + return message +} diff --git a/src/modules/hytale-presskit/index.ts b/src/modules/hytale-presskit/index.ts new file mode 100644 index 0000000..e6310e5 --- /dev/null +++ b/src/modules/hytale-presskit/index.ts @@ -0,0 +1,73 @@ +import type { Module, ModuleDependencies } from '../../core/module.js' +import type { HytaleTrackerConfig } from '../../types.js' +import { formatPresskitUpdate } from './formatter.js' +import { checkPresskitUpdate } from './tracker.js' + +export function hytalePresskitFactory( + moduleConfig: unknown, + deps: ModuleDependencies, +): Module { + const config = moduleConfig as HytaleTrackerConfig + + class HytalePresskitModule implements Module { + readonly name = 'hytale-presskit' + private dependencies: ModuleDependencies + private config: HytaleTrackerConfig + private running = false + private intervalId: NodeJS.Timeout | null = null + + constructor(cfg: HytaleTrackerConfig, deps: ModuleDependencies) { + this.config = cfg + this.dependencies = deps + } + + isRunning(): boolean { + return this.running + } + + async start(): Promise { + if (this.running) + return + + this.dependencies.logger(this.name, `Starting (poll interval: ${this.config.pollIntervalMinutes}min)`) + + // Initial check + await this.check() + + // Start polling + this.intervalId = setInterval(() => { + this.check().catch((err) => { + this.dependencies.logger(this.name, `Check failed: ${err}`) + }) + }, this.config.pollIntervalMinutes * 60 * 1000) + + this.running = true + } + + async stop(): Promise { + if (!this.running) + return + + if (this.intervalId) { + clearInterval(this.intervalId) + this.intervalId = null + } + this.running = false + } + + private async check(): Promise { + const update = await checkPresskitUpdate(this.dependencies.stateStore) + if (update) { + const message = formatPresskitUpdate(update) + await this.dependencies.telegram.sendMessage({ + text: message, + chatIds: this.config.chatIds, + disableLinkPreview: true, + }) + this.dependencies.logger(this.name, `Update detected: ${update.size} bytes`) + } + } + } + + return new HytalePresskitModule(config, deps) +} diff --git a/src/modules/hytale-presskit/tracker.ts b/src/modules/hytale-presskit/tracker.ts new file mode 100644 index 0000000..8d1f99b --- /dev/null +++ b/src/modules/hytale-presskit/tracker.ts @@ -0,0 +1,46 @@ +import type { StateStore } from '../../core/state-store.js' + +export interface PresskitUpdate { + size: number + previousSize: number | null + sizeDiff: number + date: string +} + +const ENDPOINT = 'https://cdn.hytale.com/CreatorPressKit.zip' +const STATE_KEY = 'hytale-presskit' + +export async function checkPresskitUpdate(stateStore: StateStore): Promise { + const response = await fetch(ENDPOINT, { method: 'HEAD' }) + if (!response.ok) { + throw new Error(`Presskit endpoint returned ${response.status}`) + } + + const contentLength = response.headers.get('content-length') + const date = response.headers.get('date') + + if (!contentLength) { + throw new Error('Content-Length header missing') + } + + const size = parseInt(contentLength, 10) + + const state = stateStore.get<{ lastSize?: number, lastDate?: string }>(STATE_KEY) + const lastSize = state?.lastSize ?? null + + if (lastSize === size) { + // No update, just update check time + stateStore.set(STATE_KEY, { lastSize: size, lastDate: date || new Date().toISOString() }) + return null + } + + // Update detected + stateStore.set(STATE_KEY, { lastSize: size, lastDate: date || new Date().toISOString() }) + + return { + size, + previousSize: lastSize, + sizeDiff: lastSize !== null ? size - lastSize : 0, + date: date || new Date().toISOString(), + } +}