const https = require("https"); const { parseStringPromise } = require("xml2js"); const config = require("../config"); // API Request Counter (resets daily) const stats = { requestsToday: 0, textsToday: 0, lastResetDate: new Date().toDateString(), }; /** * Prüft ob der Zähler zurückgesetzt werden muss (neuer Tag) */ function checkAndResetDailyStats() { const today = new Date().toDateString(); if (stats.lastResetDate !== today) { console.log(`[Scriptalizer] 📅 New day detected, resetting counters`); console.log(`[Scriptalizer] 📊 Yesterday's stats: ${stats.requestsToday} API calls, ${stats.textsToday} texts processed`); stats.requestsToday = 0; stats.textsToday = 0; stats.lastResetDate = today; } } /** * Gibt die aktuellen Stats zurück */ function getStats() { checkAndResetDailyStats(); return { ...stats }; } /** * Ruft die Scriptalizer API auf * @param {string} inputText - Text zum Scriptalisieren * @param {string} fontName - Font-Name (z.B. 'PremiumUltra79') * @param {number} errFrequency - Fehlerfrequenz * @returns {Promise<{status: string, outputText: string}>} */ async function callScriptalizer(inputText, fontName, errFrequency = 10) { // Prüfe ob neuer Tag checkAndResetDailyStats(); return new Promise((resolve, reject) => { if (!config.scriptalizer.licenseKey) { reject(new Error("Scriptalizer License Key fehlt in Konfiguration")); return; } // Use x-www-form-urlencoded format const params = new URLSearchParams(); params.append("LicenseKey", config.scriptalizer.licenseKey); params.append("FontName", fontName); params.append("ErrFrequency", String(errFrequency)); params.append("InputText", inputText); const body = params.toString(); // Check input size (48KB limit) if (Buffer.byteLength(body) > config.scriptalizer.maxInputSize) { reject( new Error( `Input size exceeds 48KB limit (${Buffer.byteLength(body)} bytes)` ) ); return; } const options = { hostname: "www.scriptalizer.co.uk", port: 443, path: "/QuantumScriptalize.asmx/Scriptalize", method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "Content-Length": Buffer.byteLength(body), }, timeout: 30000, // 30 second timeout }; const req = https.request(options, (res) => { let data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("end", async () => { try { const result = await parseStringPromise(data, { explicitArray: false, }); const response = result.ScriptalizerResponse; if (!response || response.Status !== "OK") { reject(new Error(response?.Status || "UNKNOWN_STATUS")); return; } // Zähler erhöhen bei erfolgreichem Request stats.requestsToday++; console.log(`[Scriptalizer] 📊 API Request #${stats.requestsToday} today completed`); resolve({ status: "OK", outputText: response.OutputText }); } catch (err) { reject( new Error(`Failed to parse Scriptalizer response: ${err.message}`) ); } }); }); req.on("error", (err) => { reject(new Error(`Scriptalizer request failed: ${err.message}`)); }); req.on("timeout", () => { req.destroy(); reject(new Error("Scriptalizer request timed out")); }); req.write(body); req.end(); }); } /** * Scriptalisiert mehrere Texte in 25er Batches * @param {string[]} texts - Array von Texten * @param {string} font - Font-Key (tilda, alva, ellie) * @param {number} errFrequency - Fehlerfrequenz * @returns {Promise} - Array von scriptalisierten Texten */ async function scriptalizeBatch(texts, font = "tilda", errFrequency = 10) { if (!Array.isArray(texts) || texts.length === 0) { return []; } const fontName = config.scriptalizer.fontMap[font.toLowerCase()] || config.scriptalizer.fontMap.tilda; const separator = config.scriptalizer.separator; const BATCH_SIZE = 18; // Zusätzlicher Deckel: max. 18 Texte pro Call (unabhängig von der Länge). // Primäre Grenze ist die ZEICHENLÄNGE: die Scriptalizer-API begrenzt den // InputText auf 32000 Zeichen pro Call ("Input length limit 32000 exceeded"). // Fixe 18er-Batches sprengen das bei langen (mehrseitigen) Texten – daher nach // kumulativer Länge batchen, mit Sicherheitsabstand. const MAX_BATCH_CHARS = 28000; const sepLen = separator.length; // Batches nach Länge (und max. Anzahl) bilden. const batches = []; { let cur = []; let curLen = 0; for (const t of texts) { const tLen = String(t || "").length; const projected = curLen + (cur.length ? sepLen : 0) + tLen; if (cur.length && (projected > MAX_BATCH_CHARS || cur.length >= BATCH_SIZE)) { batches.push(cur); cur = []; curLen = 0; } cur.push(t); curLen += (cur.length > 1 ? sepLen : 0) + tLen; } if (cur.length) batches.push(cur); } const totalBatches = batches.length; console.log( `[Scriptalizer] Processing ${texts.length} texts in ${totalBatches} batch(es) (max ${BATCH_SIZE} texts / ${MAX_BATCH_CHARS} chars each)` ); const allScriptalized = []; for (let b = 0; b < batches.length; b += 1) { const batch = batches[b]; const batchNumber = b + 1; const combinedText = batch.join(separator); console.log( `[Scriptalizer] Processing batch ${batchNumber}/${totalBatches} (${batch.length} texts, ${combinedText.length} chars)` ); try { const result = await callScriptalizer( combinedText, fontName, errFrequency ); // Trenne Ergebnis wieder const scriptalized = result.outputText.split(separator); if (scriptalized.length !== batch.length) { console.warn( `[Scriptalizer] Batch ${batchNumber}: returned ${scriptalized.length} parts, expected ${batch.length}` ); } allScriptalized.push(...scriptalized); console.log( `[Scriptalizer] Batch ${batchNumber}/${totalBatches} completed successfully` ); } catch (err) { console.error(`[Scriptalizer] Batch ${batchNumber} failed:`, err.message); throw err; } } // Textzähler erhöhen stats.textsToday += allScriptalized.length; console.log( `[Scriptalizer] All batches completed. Total scriptalized: ${allScriptalized.length}` ); console.log( `[Scriptalizer] 📊 Daily stats: ${stats.requestsToday} API calls, ${stats.textsToday} texts processed` ); return allScriptalized; } /** * Scriptalisiert einen einzelnen Text * @param {string} text - Text zum Scriptalisieren * @param {string} font - Font-Key (tilda, alva, ellie) * @param {number} errFrequency - Fehlerfrequenz * @returns {Promise} - Scriptalisierter Text */ async function scriptalizeSingle(text, font = "tilda", errFrequency = 10) { const fontName = config.scriptalizer.fontMap[font.toLowerCase()] || config.scriptalizer.fontMap.tilda; try { const result = await callScriptalizer(text, fontName, errFrequency); return result.outputText; } catch (err) { console.error("Scriptalizer single call failed:", err.message); throw err; } } module.exports = { scriptalizeBatch, scriptalizeSingle, getStats, };