testflight-watcher/setup.js

355 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require("fs");
const readline = require("readline");
const axios = require("axios");
const PushoverAPI = require("./PushoverAPI");
const clc = require("cli-color");
require("dotenv").config();
const ENV_FILE = ".env";
const TESTFLIGHT_BASE_URL = "https://testflight.apple.com/join/";
function loadEnv() {
if (fs.existsSync(ENV_FILE)) {
const content = fs.readFileSync(ENV_FILE, "utf8");
return Object.fromEntries(
content
.split("\n")
.filter((line) => line && !line.startsWith("#"))
.map((line) => line.split("=").map((part) => part.trim()))
);
}
return {};
}
function saveEnv(env) {
const content = Object.entries(env)
.map(([key, value]) => `${key}=${value}`)
.join("\n");
fs.writeFileSync(ENV_FILE, content);
}
function saveURLs(urls) {
const env = loadEnv();
env.TESTFLIGHT_URLS = JSON.stringify(urls);
saveEnv(env);
}
function loadURLs() {
const env = loadEnv();
return env.TESTFLIGHT_URLS ? JSON.parse(env.TESTFLIGHT_URLS) : [];
}
async function prompt(question) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) =>
rl.question(question, (answer) => {
rl.close();
resolve(answer.trim());
})
);
}
async function verifyURL(url) {
try {
const response = await axios.get(url, { timeout: 5000 });
if (response.status === 200) {
console.log(
clc.greenBright("\n✅ Die URL ist gültig und die Beta existiert.")
);
return true;
}
} catch (error) {
const errorMessage =
error.response && error.response.status === 404
? "\n❌ Fehler: Die Beta existiert nicht (404)."
: "❌ Fehler: Konnte die Seite nicht überprüfen. Netzwerkproblem?";
console.log(clc.redBright(errorMessage));
}
return false;
}
function displayMessage(message, type = "info") {
const color =
type === "success"
? clc.green
: type === "error"
? clc.red
: clc.magentaBright;
console.log(color(message));
}
async function manageURLs(userKey, appToken) {
let urls = loadURLs();
while (true) {
clearConsole();
displayMessage("📜 Aktuelle TestFlight-URLs:", "info");
urls.forEach((app, index) => {
console.log(clc.yellow(`${index + 1}. ${app.name} - ${app.url}`));
});
console.log("\n🛠 Optionen:");
console.log("1. 🆕 Neue URL hinzufügen");
console.log("2. 🗑️ Existierende URL löschen");
console.log("3. ✅ Fertig");
const choice = await prompt(clc.cyan("Wähle eine Option (1/2/3): "));
if (choice === "1") {
const name = await prompt(clc.cyan("\nApp-Name: "));
let input = await prompt(clc.cyan("TestFlight-URL oder ID: "));
let url = input.startsWith(TESTFLIGHT_BASE_URL)
? input
: `${TESTFLIGHT_BASE_URL}${input}`;
if (urls.some((app) => app.url === url)) {
displayMessage("\n❌ Diese URL wurde bereits hinzugefügt.", "error");
displayMessage("🔙 Rückkehr zum Hauptmenü...", "info");
await pause(3000);
continue;
}
const isValid = await verifyURL(url);
if (isValid) {
urls.push({ name, url });
saveURLs(urls);
await sendPushoverNotification(
userKey,
appToken,
"🆕 TestFlight-URL hinzugefügt",
`Die TestFlight-Beta für ${name} ist jetzt verfügbar.\n\nURL: ${url}`
);
displayMessage("\n📲 Benachrichtigung gesendet.", "success");
displayMessage("✅ Neue URL hinzugefügt.", "success");
displayMessage("🔙 Rückkehr zum Hauptmenü...", "info");
await pause(3000);
} else {
displayMessage(
"❌ Die URL wurde nicht hinzugefügt, da sie ungültig ist.",
"error"
);
displayMessage("🔙 Rückkehr zum Hauptmenü...", "info");
await pause(3000);
}
} else if (choice === "2") {
const index =
parseInt(await prompt(clc.cyan("Nummer der zu löschenden URL: ")), 10) -
1;
if (index >= 0 && index < urls.length) {
displayMessage(
`\n🗑️ Lösche: ${urls[index].name} - ${urls[index].url}`,
"error"
);
await sendPushoverNotification(
userKey,
appToken,
"🗑️ TestFlight-URL gelöscht",
`Die TestFlight-Beta für ${urls[index].name} wurde gelöscht.\n\nURL: ${urls[index].url}`
);
displayMessage("\n📲 Benachrichtigung gesendet.", "success");
urls.splice(index, 1);
saveURLs(urls);
displayMessage("✅ URL gelöscht.", "success");
displayMessage("🔙 Rückkehr zum Hauptmenü...", "info");
await pause(3000);
} else {
displayMessage("❌ Ungültige Auswahl.", "error");
await pause(3000);
}
} else if (choice === "3") {
break;
} else {
displayMessage("❌ Ungültige Eingabe.", "error");
await pause(3000);
}
}
}
async function sendPushoverNotification(userKey, appToken, title, message) {
const pushover = new PushoverAPI(userKey, appToken);
try {
const response = await pushover.sendNotification(title, message, {
priority: 0,
});
if (!response || response.status !== 1) {
throw err;
}
return response;
} catch (err) {
throw err;
}
}
async function configureEnvironment() {
const env = loadEnv();
displayMessage("\n🎉 Willkommen zum Setup!\n", "info");
const questions = [
{
key: "OTP_SECRET",
prompt:
'Verschlüsselungsstring für das OTP (z.B. "768XuxTKWXKUPQ8fjfLxCtUQCVEKikq6")',
defaultValue: "AendereDiesenString",
},
{
key: "OTP_VALIDITY",
prompt: 'Gültigkeitsdauer für das OTP in Minuten (z.B. "5" für 5 Min.)',
defaultValue: "5 * 60 * 1000",
},
{
key: "SETUP_FILE_NAME",
prompt: "Name der Setup-Datei (Falls Du ihn angepasst hast)",
defaultValue: "setup.js",
},
{
key: "SERVER_FILE_NAME",
prompt: "Name der Server-Datei (Falls Du ihn angepasst hast)",
defaultValue: "index.js",
},
{
key: "PORT",
prompt: 'Port für den Server (z.B. "3000")',
defaultValue: "3000",
},
{
key: "HTTP_URL",
prompt:
"Auf welche Adresse ist der Webserver erreichbar? (z.B. http://localhost:3000)",
defaultValue: "http://localhost:3000",
},
{
key: "USER_AGENT",
prompt: "User-Agent für Anfragen",
defaultValue: "Testflight-Watcher/0.0.2 (Monitoring Script)",
},
{
key: "PUSHOVER_PRIORITY",
prompt:
"Priorität der Benachrichtigung bei freier Beta (Niedrigste = -2; Niedrige = -1; Normale = 0; Hohe = 1; Kritische = 2)",
defaultValue: "1",
},
{
key: "CHECK_INTERVAL",
prompt:
"In welchem Abstand soll das Script nach einem neuen Platz prüfen in Sekunden (z.B. 30)",
defaultValue: "30",
},
];
for (const { key, prompt: question, defaultValue } of questions) {
if (env[key]) {
displayMessage(
`⏩ Überspringe: ${key} (bereits gesetzt: ${env[key]})`,
"info"
);
continue;
}
let currentValue = defaultValue;
const answer = await prompt(
clc.cyan(`${question} [Standard: ${currentValue}]: `)
);
if (key === "OTP_VALIDITY") {
const minutes = parseFloat(answer || "5");
currentValue = `${minutes} * 60 * 1000`;
} else {
currentValue = answer || currentValue;
}
env[key] = currentValue;
saveEnv(env);
}
saveEnv(env);
displayMessage("\n📂 Standard-Konfiguration gespeichert.", "success");
}
(async () => {
const env = loadEnv();
await configureEnvironment();
if (!env.PUSHOVER_USER_KEY || !env.PUSHOVER_APP_TOKEN) {
env.PUSHOVER_USER_KEY = await prompt(
clc.cyan("\nPushover-Benutzer-Schlüssel: ")
);
env.PUSHOVER_APP_TOKEN = await prompt(clc.cyan("Pushover-API-Token: "));
let testApiSuccess = false;
while (!testApiSuccess) {
const testApi = await prompt(
clc.yellow("Möchtest Du die Pushover-Verbindung testen? (") +
clc.green("ja") +
clc.yellow("/") +
clc.red("nein") +
clc.yellow("): ")
);
if (testApi.toLowerCase() === "ja") {
try {
await sendPushoverNotification(
env.PUSHOVER_USER_KEY,
env.PUSHOVER_APP_TOKEN,
"Pushover-Verbindung",
"Die Pushover-API ist erfolgreich konfiguriert!"
);
testApiSuccess = true;
displayMessage(
"✅ Die Verbindung zu Pushover war erfolgreich!",
"success"
);
saveEnv(env);
displayMessage(
"\n\n📂 Pushover-Konfiguration gespeichert.",
"success"
);
await pause(3000);
} catch (error) {
const retry = await prompt(
clc.yellow("\nMöchtest Du die Daten korrigieren? (") +
clc.green("ja") +
clc.yellow("/") +
clc.red("nein") +
clc.yellow("): ")
);
if (retry.toLowerCase() === "ja") {
env.PUSHOVER_USER_KEY = await prompt(
clc.cyan("\nPushover-Benutzer-Schlüssel: ")
);
env.PUSHOVER_APP_TOKEN = await prompt(
clc.cyan("Pushover-API-Token: ")
);
saveEnv(env);
}
}
} else {
displayMessage("\nVerbindungstest übersprungen.", "info");
saveEnv(env);
displayMessage("📂 Pushover-Konfiguration gespeichert.", "success");
await pause(3000);
testApiSuccess = true;
}
}
} else {
displayMessage("✅ Pushover ist bereits konfiguriert.", "success");
await pause(3000);
}
displayMessage("\n🛠 Verwalte TestFlight-URLs:", "info");
await manageURLs(env.PUSHOVER_USER_KEY, env.PUSHOVER_APP_TOKEN);
displayMessage("\n✅ Setup abgeschlossen!", "success");
})();
async function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function clearConsole() {
process.stdout.write("\x1Bc");
}