mirror of
https://github.com/MaximilianGT500/testflight-watcher.git
synced 2025-01-10 15:08:34 +01:00
347 lines
9.9 KiB
JavaScript
347 lines
9.9 KiB
JavaScript
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 Sekunden (z.B. "300" für 5 Min.)',
|
||
defaultValue: "300",
|
||
},
|
||
{
|
||
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 (Fals 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.1 (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 welchen Abstand soll das Script nach einen 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;
|
||
}
|
||
|
||
const currentValue = defaultValue;
|
||
const answer = await prompt(
|
||
clc.cyan(`${question} [Standard: ${currentValue}]: `)
|
||
);
|
||
env[key] = answer || 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");
|
||
}
|