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 <noreply@anthropic.com>
This commit is contained in:
parent
e0ec995068
commit
be1102119a
5 changed files with 153 additions and 1 deletions
|
|
@ -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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, (config: unknown, deps: import('./core/module.js').ModuleDependencies) => Module> = {
|
||||
|
|
@ -18,6 +19,7 @@ const MODULE_FACTORIES: Record<string, (config: unknown, deps: import('./core/mo
|
|||
'hytale-launcher': hytaleLauncherFactory,
|
||||
'hytale-patches': hytalePatchesFactory,
|
||||
'hytale-downloader': hytaleDownloaderFactory,
|
||||
'hytale-presskit': hytalePresskitFactory,
|
||||
'hytale-server': hytaleServerFactory,
|
||||
}
|
||||
|
||||
|
|
|
|||
31
src/modules/hytale-presskit/formatter.ts
Normal file
31
src/modules/hytale-presskit/formatter.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import type { PresskitUpdate } from './tracker.js'
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes === 0)
|
||||
return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`
|
||||
}
|
||||
|
||||
export function formatPresskitUpdate(update: PresskitUpdate): string {
|
||||
const { size, previousSize, sizeDiff, date } = update
|
||||
|
||||
let message = `<b>📦 Hytale Creator Presskit Update</b>
|
||||
|
||||
<b>New size:</b> <code>${formatBytes(size)}</code>`
|
||||
|
||||
if (previousSize !== null) {
|
||||
const diffStr = sizeDiff > 0 ? `+${formatBytes(sizeDiff)}` : formatBytes(sizeDiff)
|
||||
const diffSign = sizeDiff > 0 ? '📈' : '📉'
|
||||
message += `
|
||||
<b>Previous size:</b> <code>${formatBytes(previousSize)}</code>
|
||||
<b>Difference:</b> <code>${diffStr}</code> ${diffSign}`
|
||||
}
|
||||
|
||||
message += `
|
||||
<b>Date:</b> <code>${date}</code>`
|
||||
|
||||
return message
|
||||
}
|
||||
73
src/modules/hytale-presskit/index.ts
Normal file
73
src/modules/hytale-presskit/index.ts
Normal file
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
if (!this.running)
|
||||
return
|
||||
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId)
|
||||
this.intervalId = null
|
||||
}
|
||||
this.running = false
|
||||
}
|
||||
|
||||
private async check(): Promise<void> {
|
||||
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)
|
||||
}
|
||||
46
src/modules/hytale-presskit/tracker.ts
Normal file
46
src/modules/hytale-presskit/tracker.ts
Normal file
|
|
@ -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<PresskitUpdate | null> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue