require('dotenv').config(); const TelegramBot = require('node-telegram-bot-api'); const axios = require('axios'); const fs = require('fs'); const yaml = require('yamljs'); const path = require('path'); const dayjs = require('dayjs'); const dayOfYear = require('dayjs/plugin/dayOfYear'); const express = require('express'); const bodyParser = require('body-parser'); const NodeCache = require('node-cache'); const schedule = require('node-schedule'); const moment = require('moment'); const nodemailer = require('nodemailer'); const { scheduleJob } = require('node-schedule'); const { format } = require('date-fns'); const archiver = require('archiver'); const today = format(new Date(), 'yyyy-MM-dd'); console.log(today); // Sollte das aktuelle Datum im Format yyyy-MM-dd ausgeben const CacheDir = path.join(__dirname, 'Cache'); const cacheFilePath = path.join(CacheDir, 'cache.json'); // Setze PROJECT_ROOT auf das aktuelle Verzeichnis const PROJECT_ROOT = __dirname; // Konstanten aus .env-Datei const BOT_TOKEN = process.env.BOT_TOKEN; const PLEX_TOKEN = process.env.PLEX_TOKEN; const PLEX_DOMAIN = process.env.PLEX_DOMAIN; const PLEX_LIBRARY_URL = `${PLEX_DOMAIN}/library/sections/all?X-Plex-Token=${PLEX_TOKEN}`; const USER_YML_PATH = path.resolve(PROJECT_ROOT, process.env.USER_YML_PATH); const LOG_DIR = path.resolve(PROJECT_ROOT, process.env.LOG_DIR); const ERROR_LOG_PATH = path.resolve(LOG_DIR, process.env.ERROR_LOG_PATH); const PORT = process.env.PORT; const USER1_ID = process.env.USER1_ID; const USER2_ID = process.env.USER2_ID; const WEBHOOK_URL = process.env.WEBHOOK_URL; const AUTHORIZED_USER_ID = process.env.AUTHORIZED_USER_ID; const errorLogPath = process.env.ERROR_LOG_PATH; // Debug-Ausgaben für Pfade console.log('USER_YML_PATH:', USER_YML_PATH); console.log('LOG_DIR:', LOG_DIR); console.log('ERROR_LOG_PATH:', ERROR_LOG_PATH); // Sicherstellen, dass Verzeichnisse und Dateien existieren if (!fs.existsSync(LOG_DIR)) { fs.mkdirSync(LOG_DIR, { recursive: true }); } if (!fs.existsSync(USER_YML_PATH)) { fs.writeFileSync(USER_YML_PATH, yaml.stringify({}, 4)); } if (!fs.existsSync(ERROR_LOG_PATH)) { fs.writeFileSync(ERROR_LOG_PATH, ''); // Leere Datei erstellen } // Erstelle den Cache-Ordner, falls er nicht existiert if (!fs.existsSync(CacheDir)) { fs.mkdirSync(CacheDir); } // Initialisiere den Cache mit einer bestimmten Lebensdauer (TTL) von 1 Stunde const cache = new NodeCache({ stdTTL: 3600 }); // Funktion zum Speichern des Caches in eine Datei function saveCacheToFile() { const cacheData = cache.keys().reduce((acc, key) => { acc[key] = cache.get(key); return acc; }, {}); fs.writeFileSync(cacheFilePath, JSON.stringify(cacheData)); } // Funktion zum Laden des Caches aus einer Datei function loadCacheFromFile() { if (fs.existsSync(cacheFilePath)) { const cacheData = JSON.parse(fs.readFileSync(cacheFilePath)); for (const [key, value] of Object.entries(cacheData)) { cache.set(key, value); } } } // Funktion zum Abrufen von Plex-Daten async function fetchPlexData(url) { try { const response = await axios.get(url, { headers: { 'X-Plex-Token': PLEX_TOKEN } }); return response.data; } catch (error) { logError(`Error fetching Plex data: ${error.message}`); throw error; } } // Funktion zum Abrufen aller Filme async function fetchAllMovies() { try { const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); const sections = sectionsData.MediaContainer.Directory; let movies = []; for (const section of sections) { const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`; const sectionData = await fetchPlexData(sectionUrl); if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) { const metadata = sectionData.MediaContainer.Metadata; movies = movies.concat(metadata.filter(media => media.type === 'movie')); } } movies.sort((a, b) => (b.addedAt || 0) - (a.addedAt || 0)); return movies; } catch (error) { logError(`Error fetching all movies: ${error.message}`); throw error; } } // Funktion zum Abrufen der Filme mit Caching async function fetchMoviesWithCache() { const cacheKey = 'allMovies'; const cachedMovies = cache.get(cacheKey); if (cachedMovies) { logMessage('Movies fetched from cache'); return cachedMovies; } try { const movies = await fetchAllMovies(); cache.set(cacheKey, movies); logMessage('Movies fetched from API and cached'); return movies; } catch (error) { logError(`Error fetching movies: ${error.message}`); throw error; } } // Funktion zum Abrufen eines zufälligen Films mit Caching async function fetchRandomMovie() { try { const movies = await fetchMoviesWithCache(); if (movies.length === 0) return null; const randomIndex = Math.floor(Math.random() * movies.length); return movies[randomIndex]; } catch (error) { logError(`Error fetching random movie: ${error.message}`); throw error; } } // Funktion zum Durchführen der Filmsuche mit Caching async function searchMovies(query) { try { const movies = await fetchMoviesWithCache(); const results = movies.filter(movie => movie.title.toLowerCase().includes(query.toLowerCase()) ); return results; } catch (error) { logError(`Error searching movies: ${error.message}`); throw error; } } // Funktion zum Abrufen der gut bewerteten Filme mit Caching async function fetchTopRatedMovies() { try { const movies = await fetchMoviesWithCache(); const ratedMovies = movies.filter(movie => movie.rating && movie.rating > 0); ratedMovies.sort((a, b) => (b.rating || 0) - (a.rating || 0)); return ratedMovies; } catch (error) { logError(`Error fetching top-rated movies: ${error.message}`); throw error; } } // Funktion zum Abrufen des Films des Tages mit Caching async function fetchDailyRecommendation() { try { const ratedMovies = await fetchTopRatedMovies(); if (ratedMovies.length === 0) return null; dayjs.extend(dayOfYear); // Füge das Plugin hier hinzu const dayOfYear = dayjs().dayOfYear(); const todayIndex = dayOfYear % ratedMovies.length; return ratedMovies[todayIndex]; } catch (error) { logError(`Error fetching daily recommendation: ${error.message}`); throw error; } } // Funktion zum Abrufen der letzten 10 hinzugefügten Filme mit Caching async function fetchLatest10Movies() { try { const movies = await fetchMoviesWithCache(); const sortedMovies = movies .filter(movie => movie.addedAt) .sort((a, b) => b.addedAt - a.addedAt) .slice(0, 10); return sortedMovies; } catch (error) { logError(`Error fetching latest 10 movies: ${error.message}`); throw error; } } // Funktion zum automatischen Aktualisieren des Caches async function updateCache() { try { await fetchMoviesWithCache(); // Stellt sicher, dass der Cache aktualisiert wird logMessage('Cache wurde automatisch aktualisiert'); } catch (error) { logError(`Fehler beim automatischen Aktualisieren des Caches: ${error.message}`); } } // Lade den Cache beim Start (async function start() { try { await fetchMoviesWithCache(); // Initialisiert den Cache beim Start logMessage('Cache beim Start initialisiert'); // Speicher den Cache regelmäßig (z.B. jede Stunde) schedule.scheduleJob('0 * * * *', saveCacheToFile); // Plane die automatische Aktualisierung des Caches jede Stunde schedule.scheduleJob('0 * * * *', updateCache); // Beispiel für die Verwendung von node-schedule function checkForNewMovies() { // Hier könntest du eine Funktion zum Überprüfen neuer Filme einfügen console.log('Checking for new movies...'); } // Beispiel für geplante Aufgaben schedule.scheduleJob('*/1 * * * *', checkForNewMovies); } catch (error) { logError(`Fehler beim Start des Bots: ${error.message}`); } })(); // Telegram-Bot-Instanz erstellen const bot = new TelegramBot(BOT_TOKEN, { polling: true }); // Express-Server für Webhooks const app = express(); app.use(bodyParser.json()); // Funktion zum Protokollieren von allgemeinen Nachrichten function logMessage(message) { const today = dayjs().format('YYYY-MM-DD'); const logFilePath = path.join(LOG_DIR, `${today}.log`); fs.appendFileSync(logFilePath, `${dayjs().format('HH:mm:ss')} - ${message}\n`); } // Funktion zur Fehlerprotokollierung function logError(error) { const errorMessage = `${dayjs().format('HH:mm:ss')} - Error: ${error}\n`; fs.appendFileSync(ERROR_LOG_PATH, errorMessage); } const faqFilePath = path.join(__dirname, 'faq.json'); // Pfad zur faq.json im Hauptverzeichnis const authorizedUsers = [USER1_ID, USER2_ID]; // Funktion zum Laden der FAQs function loadFaqs() { if (!fs.existsSync(faqFilePath)) { fs.writeFileSync(faqFilePath, JSON.stringify([])); // Leere Datei erstellen, wenn sie nicht existiert } const faqs = JSON.parse(fs.readFileSync(faqFilePath)); return faqs; } // Funktion zum Speichern der FAQs function saveFaqs(faqs) { fs.writeFileSync(faqFilePath, JSON.stringify(faqs, null, 2)); } // Befehl zum Abrufen von Trailern bot.onText(/\/trailer/, (msg) => { const chatId = msg.chat.id; // Nach dem Filmtitel fragen bot.sendMessage(chatId, 'Bitte geben Sie den Titel des Films ein:'); // Auf die nächste Nachricht warten, die den Filmnamen enthält bot.once('message', async (msg) => { const filmTitle = msg.text; try { // YouTube API URL für die Suche const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=${encodeURIComponent(filmTitle + ' trailer')}&key=${process.env.YOUTUBE_API_KEY}`; const response = await axios.get(url); const videos = response.data.items; // Überprüfen, ob Videos gefunden wurden if (videos.length > 0) { const videoId = videos[0].id.videoId; // ID des ersten gefundenen Trailers const trailerUrl = `https://www.youtube.com/watch?v=${videoId}`; const reply = `Hier ist der Trailer für "${filmTitle}": ${trailerUrl}`; bot.sendMessage(chatId, reply); } else { bot.sendMessage(chatId, `Leider konnte ich keinen Trailer für "${filmTitle}" finden.`); } } catch (error) { console.error('Fehler beim Abrufen des Trailers:', error); bot.sendMessage(chatId, 'Es gab ein Problem beim Abrufen des Trailers. Bitte versuche es später erneut.'); } }); }); // Globale Variable zum Zählen der Passwortanforderungen let passwordRequestCount = 0; let passwordChangeRequired = false; // Sperrt Login, wenn Passwortänderung nötig ist let newPassword = ''; // Speichert das neue Passwort bot.onText(/\/passwd/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id.toString(); // Überprüfen, ob das Passwort geändert werden muss if (passwordChangeRequired) { const reply = `⚠️ Der Befehl /passwd ist zurzeit nicht verfügbar! Der Zugang wurde gesperrt bitte wenden dich an den Admin.`; bot.sendMessage(chatId, reply, { parse_mode: 'HTML' }); return; // Beende die Ausführung, wenn das Passwort geändert werden muss } if (authorizedUsers.includes(userId)) { // Zähler für Passwortanforderungen erhöhen passwordRequestCount++; // Wenn die Anzahl der Anfragen 10 erreicht, muss das Passwort geändert werden if (passwordRequestCount >= 10) { newPassword = generateRandomPassword(); // Zufälliges sicheres Passwort generieren updateEnvPassword(newPassword); // Neues Passwort in der .env speichern const changePwMessage = `⚠️ Du hast das Passwort zu oft angefordert. Der Zugang wurde gesperrt.`; bot.sendMessage(chatId, changePwMessage, { parse_mode: 'HTML', protect_content: true, }).then((sentMessage) => { setTimeout(() => { bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => { console.error('Fehler beim Löschen der Nachricht:', err); }); }, 30000); // 30 Sekunden }); // Zähler zurücksetzen und Sperre aktivieren passwordRequestCount = 0; passwordChangeRequired = true; // Passwort muss geändert werden return; // Beende die Ausführung, um das alte Passwort nicht mehr zu senden } // Passwort aus der .env-Datei holen const password = process.env.ADMIN_PW; const reply = `🔒 Das Passwort für den Adminbereich lautet:\n\n${password}\n\n‼️Hinweis:‼\n Diese Nachricht wird automatisch in 30 Sekunden gelöscht.`; bot.sendMessage(chatId, reply, { parse_mode: 'HTML', protect_content: true, // Inhalt schützen }).then((sentMessage) => { setTimeout(() => { bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => { console.error('Fehler beim Löschen der Antwortnachricht:', err); }); }, 30000); // 30 Sekunden }); setTimeout(() => { bot.deleteMessage(chatId, msg.message_id).catch((err) => { console.error('Fehler beim Löschen der ursprünglichen Nachricht:', err); }); }, 30000); // 30 Sekunden // Nachricht an den Dev senden const devMessage = `🔒 Das Passwort für den Adminbereich wurde angefordert von:\n\n👤 @${msg.from.username}\n\n🆔 ID: ${userId}\n\n📅 Datum: ${new Date().toLocaleDateString('de-DE')}\n\n🕒 Uhrzeit: ${new Date().toLocaleTimeString('de-DE')}`; bot.sendMessage(process.env.DEV_CHAT_ID, devMessage, { parse_mode: 'HTML' }).catch((err) => { console.error('Fehler beim Senden der Dev-Nachricht:', err); }); } else { const reply = `🚫 Zugriff verweigert!\nLeider hast du keine Berechtigung, diesen Befehl auszuführen.`; bot.sendMessage(chatId, reply, { parse_mode: 'HTML', protect_content: true, }).then((sentMessage) => { setTimeout(() => { bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => { console.error('Fehler beim Löschen der Antwortnachricht:', err); }); }, 30000); // 30 Sekunden }); setTimeout(() => { bot.deleteMessage(chatId, msg.message_id).catch((err) => { console.error('Fehler beim Löschen der ursprünglichen Nachricht:', err); }); }, 30000); // 30 Sekunden } }); // /key Befehl bot.onText(/\/key/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id.toString(); if (authorizedUsers.includes(userId)) { if (passwordChangeRequired) { const reply = `🔑 Neues Passwort:\n${newPassword}\n\nDiese Nachricht wird in 30 Sekunden gelöscht.`; bot.sendMessage(chatId, reply, { parse_mode: 'HTML' }).then((sentMessage) => { setTimeout(() => { bot.deleteMessage(chatId, sentMessage.message_id); }, 30000); }); setTimeout(() => { bot.deleteMessage(chatId, msg.message_id); }, 30000); passwordChangeRequired = false; // /passwd wieder aktivieren } else { bot.sendMessage(chatId, "⚠️ Das Passwort wurde nicht geändert. Du kannst den /passwd Befehl nutzen.", { parse_mode: 'HTML' }); } } else { bot.sendMessage(chatId, "🚫 Zugriff verweigert!", { parse_mode: 'HTML' }); } }); // Funktion zum Generieren eines sicheren zufälligen Passworts mit mindestens 12 Zeichen function generateRandomPassword() { const uppercaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const lowercaseChars = 'abcdefghijklmnopqrstuvwxyz'; const digits = '0123456789'; const specialChars = '@$!+-*&'; const allChars = uppercaseChars + lowercaseChars + digits + specialChars; let password = ''; // Stelle sicher, dass das Passwort mindestens je einen Großbuchstaben, Kleinbuchstaben, Zahl und Sonderzeichen enthält password += uppercaseChars.charAt(Math.floor(Math.random() * uppercaseChars.length)); password += lowercaseChars.charAt(Math.floor(Math.random() * lowercaseChars.length)); password += digits.charAt(Math.floor(Math.random() * digits.length)); password += specialChars.charAt(Math.floor(Math.random() * specialChars.length)); // Fülle das restliche Passwort mit zufälligen Zeichen auf, um 12 Zeichen zu erreichen for (let i = 4; i < 12; i++) { password += allChars.charAt(Math.floor(Math.random() * allChars.length)); } // Das Passwort zufällig durchmischen return shuffleString(password); } // Funktion zum Mischen eines Strings (optional, um die Zeichen zufällig anzuordnen) function shuffleString(string) { const array = string.split(''); for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array.join(''); } // Funktion zum Aktualisieren des Passworts in der .env-Datei function updateEnvPassword(newPassword) { const envFilePath = path.resolve(__dirname, '.env'); // Die aktuelle .env-Datei lesen let envContent = fs.readFileSync(envFilePath, 'utf-8'); // Das Passwort in der Datei ändern envContent = envContent.replace(/ADMIN_PW=.*/, `ADMIN_PW=${newPassword}`); // Die geänderte Datei speichern fs.writeFileSync(envFilePath, envContent); // Umgebungsvariable aktualisieren, ohne den Bot neu zu starten process.env.ADMIN_PW = newPassword; // Passwortänderung abgeschlossen passwordChangeRequired = false; // Sperre aufheben, Login wieder möglich } // /faq Befehl: Zeigt alle FAQs an bot.onText(/\/faq/, (msg) => { const chatId = msg.chat.id; const faqs = loadFaqs(); if (faqs.length === 0) { bot.sendMessage(chatId, 'Es gibt derzeit keine FAQs.'); } else { let response = 'Häufig gestellte Fragen:\n\n'; faqs.forEach((faq, index) => { response += `${index + 1}. *${faq.question}*\n${faq.answer}\n\n`; }); bot.sendMessage(chatId, response, { parse_mode: 'Markdown' }); } }); // /add_faq Befehl: Interaktives Hinzufügen einer neuen FAQ (nur für autorisierte Benutzer) bot.onText(/\/add_faq/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id.toString(); if (!authorizedUsers.includes(userId)) { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); return; } // Frage nach der FAQ-Frage bot.sendMessage(chatId, 'Bitte gib die FAQ-Frage ein:', { reply_markup: { force_reply: true } }).then(sentMessage => { bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => { const question = reply.text; // Frage nach der FAQ-Antwort bot.sendMessage(chatId, 'Bitte gib die Antwort auf die Frage ein:', { reply_markup: { force_reply: true } }).then(sentMessage => { bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => { const answer = reply.text; // FAQ speichern const faqs = loadFaqs(); faqs.push({ question, answer }); saveFaqs(faqs); bot.sendMessage(chatId, '✅ FAQ erfolgreich hinzugefügt.'); }); }); }); }); }); // /del_faq Befehl: Interaktives Entfernen einer FAQ (nur für autorisierte Benutzer) bot.onText(/\/del_faq/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id.toString(); if (!authorizedUsers.includes(userId)) { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); return; } const faqs = loadFaqs(); if (faqs.length === 0) { bot.sendMessage(chatId, 'Es gibt derzeit keine FAQs zum Löschen.'); return; } // Liste der FAQs anzeigen und um Eingabe der Nummer bitten let response = 'Welche FAQ möchtest du löschen?\n\n'; faqs.forEach((faq, index) => { response += `${index + 1}. *${faq.question}*\n${faq.answer}\n\n`; }); bot.sendMessage(chatId, response, { parse_mode: 'Markdown', reply_markup: { force_reply: true } }).then(sentMessage => { bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => { const faqIndex = parseInt(reply.text, 10) - 1; if (isNaN(faqIndex) || faqIndex < 0 || faqIndex >= faqs.length) { bot.sendMessage(chatId, '❌ Ungültige Auswahl.'); return; } // FAQ löschen faqs.splice(faqIndex, 1); saveFaqs(faqs); bot.sendMessage(chatId, '✅ FAQ erfolgreich gelöscht.'); }); }); }); // Pfad zur Abonnentendatei const subscribersFilePath = './subscribers.json'; const moviesApiUrl = `${process.env.PLEX_DOMAIN}/api/movies/latest`; // Beispiel-API-URL, anpassen // Erstelle die subscribers.json, wenn sie nicht existiert if (!fs.existsSync(subscribersFilePath)) { fs.writeFileSync(subscribersFilePath, JSON.stringify([])); } let subscribers = []; // Lade Abonnenten aus der subscribers.json function loadSubscribers() { try { const data = fs.readFileSync(subscribersFilePath); subscribers = JSON.parse(data); } catch (error) { console.error('Fehler beim Laden der Abonnenten:', error); } } // Sende den Newsletter async function sendNewsletter() { loadSubscribers(); // Abonnenten laden if (subscribers.length === 0) { console.log('Keine Abonnenten gefunden.'); return; } const movies = await fetchLatestMovies(); // Filme abrufen const htmlContent = createNewsletterContent(movies); // HTML-Inhalt erstellen const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, secure: false, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); subscribers.forEach(subscriber => { const mailOptions = { from: process.env.SMTP_USER, to: subscriber.email, subject: 'Wöchentlicher Film-Newsletter', html: htmlContent, }; transporter.sendMail(mailOptions, (error, info) => { if (error) { return console.log('Fehler beim Senden der E-Mail:', error); } console.log('Newsletter gesendet an:', subscriber.email); }); }); } // Sofortige Bestätigungs-E-Mail senden async function sendConfirmationEmail(email) { const latestMovies = await fetchLatestMovies(); // Den zuletzt hinzugefügten Film abrufen const latestMovie = latestMovies.length > 0 ? latestMovies[0] : null; const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, secure: false, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); const logoUrl = process.env.PLEX_LOGO_URL; // Füge hier die URL zu deinem Plex-Logo hinzu const latestMovieThumb = latestMovie ? `${process.env.PLEX_DOMAIN}${latestMovie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : ''; const latestMovieTitle = latestMovie ? latestMovie.title : 'Kein Film gefunden'; const latestMovieSummary = latestMovie ? latestMovie.summary : 'Keine Zusammenfassung verfügbar'; const mailOptions = { from: process.env.SMTP_USER, to: email, subject: '🎉 Bestätigung der Newsletter-Anmeldung 🎉', html: `

Willkommen zum Viper-Plex Newsletter!

Vielen Dank, dass Sie sich für unseren Newsletter angemeldet haben! 🎊

Ab sofort erhalten Sie jeden Sonntag die neuesten Informationen über spannende Filme.

Plex Logo

Zuletzt hinzugefügter Film:

${latestMovieTitle}

${latestMovieThumb ? `${latestMovieTitle} Poster` : ''}

Zusammenfassung: ${latestMovieSummary}

Wir freuen uns, Sie als Teil unserer Viper-Plex Familie zu haben!

`, }; transporter.sendMail(mailOptions, (error, info) => { if (error) { return console.log('Fehler beim Senden der Bestätigungs-E-Mail:', error); } console.log('Bestätigungs-E-Mail gesendet an:', email); }); } // Filme abrufen async function fetchLatestMovies() { try { const response = await axios.get(moviesApiUrl); // Filtere nur Filme aus und ignoriere Serien und Staffeln return response.data.movies.filter(movie => !movie.isSeries); } catch (error) { console.error('Fehler beim Abrufen der Filme:', error); return []; } } // Erstelle den HTML-Inhalt des Newsletters function createNewsletterContent(movies) { let html = `

Neueste Filme auf Viper-Plex

`; movies.forEach(movie => { const movieTitle = movie.title || 'Unbekannt'; const movieSummary = movie.summary || 'Keine Zusammenfassung verfügbar'; const addedAtDate = new Date((movie.addedAt || 0) * 1000).toLocaleString('de-DE'); // Konvertierung von Unix-Zeitstempel in lesbares Datum const movieThumb = movie.thumb ? `${process.env.PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : ''; html += `

${movieTitle}

${movieThumb ? `${movieTitle} Poster` : ''}

Zusammenfassung: ${movieSummary}

Hinzugefügt am: ${addedAtDate}

`; }); html += `
`; return html; } // Abmeldung vom Newsletter bot.on('callback_query', (query) => { const chatId = query.from.id; // Extrahiere die chatId aus dem Benutzer // Überprüfen, ob die Abmeldung angefordert wird if (query.data.startsWith('unsubscribe_')) { const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId); if (subscriberIndex !== -1) { const subscriber = subscribers[subscriberIndex]; const options = { reply_markup: { inline_keyboard: [ [ { text: 'Ja', callback_data: `unsubscribe_yes_${chatId}`, }, { text: 'Nein', callback_data: `unsubscribe_no_${chatId}`, }, ], ], }, }; bot.sendMessage(chatId, `😥 Möchten Sie sich wirklich von dem Newsletter abmelden, ${subscriber.username}?`, options); } else { bot.sendMessage(chatId, '❗️ Sie sind nicht für den Newsletter angemeldet.'); } } }); // Verarbeite die Callback-Daten für die Bestätigung bot.on('callback_query', (query) => { const chatId = query.from.id; // Hier verwenden wir query.from.id, um die chatId zu erhalten if (query.data.startsWith('unsubscribe_yes')) { const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId); if (subscriberIndex !== -1) { subscribers.splice(subscriberIndex, 1); // Abonnenten entfernen fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); bot.sendMessage(chatId, '✅ Sie wurden erfolgreich vom Newsletter abgemeldet.'); } else { bot.sendMessage(chatId, '❗️ Abonnent nicht gefunden.'); } } else if (query.data.startsWith('unsubscribe_no')) { bot.sendMessage(chatId, '❌ Abmeldung vom Newsletter abgebrochen.'); } }); // Abmeldebefehl (z.B. /unsubscribe) bot.onText(/\/unsubscribe/, (msg) => { const chatId = msg.chat.id; unsubscribeFromNewsletter(chatId); }); // Planen des Newsletter-Versands jeden Sonntag um 10:00 Uhr schedule.scheduleJob('0 10 * * 0', () => { console.log('Sende wöchentlichen Newsletter...'); sendNewsletter(); }); // Abonnieren bot.onText(/\/newsletter/, (msg) => { const chatId = msg.chat.id; const username = msg.from.username || 'Unbekannt'; // Überprüfen, ob der Benutzer bereits abonniert ist const subscriber = subscribers.find(subscriber => subscriber.chatId === chatId); if (!subscriber) { // Wenn nicht abonniert, frage nach der E-Mail-Adresse bot.sendMessage(chatId, 'Bitte geben Sie Ihre E-Mail-Adresse ein:'); bot.once('message', (msg) => { const email = msg.text; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (emailRegex.test(email)) { // Neuen Abonnenten hinzufügen subscribers.push({ chatId, email, username }); fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); sendConfirmationEmail(email); // Bestätigungs-E-Mail senden bot.sendMessage(chatId, '🎉 Sie haben sich erfolgreich für den Newsletter angemeldet!'); } else { bot.sendMessage(chatId, '❌ Ungültige E-Mail-Adresse. Bitte versuchen Sie es erneut.'); } }); } else { // Wenn bereits abonniert, zeige die Optionen an const options = { reply_markup: { inline_keyboard: [ [ { text: 'Abmelden', callback_data: `unsubscribe_${chatId}`, }, { text: 'Mailadresse ändern', callback_data: `change_email_${chatId}`, }, ], // Zusätzliche Optionen für Administratoren ...(isAdmin(chatId) ? [ [ { text: 'Send Newsletter', callback_data: 'send_newsletter', }, { text: 'Abonnenten', callback_data: 'list_subscribers', }, { text: 'Abonnenten Entfernen', callback_data: 'remove_subscriber', }, ] ] : []), ], }, }; bot.sendMessage(chatId, 'Sie sind bereits angemeldet. Was möchten Sie tun?', options); } }); // Funktion, um zu überprüfen, ob der Benutzer ein Administrator ist function isAdmin(chatId) { const adminIds = [process.env.USER1_ID, process.env.USER2_ID]; return adminIds.includes(chatId.toString()); } // Callback-Handler für die Buttons bot.on('callback_query', (query) => { const chatId = query.from.id; // chatId aus der Anfrage erhalten if (query.data.startsWith('change_email')) { bot.sendMessage(chatId, 'Bitte geben Sie Ihre neue E-Mail-Adresse ein:'); bot.once('message', (msg) => { const newEmail = msg.text; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (emailRegex.test(newEmail)) { const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId); if (subscriberIndex !== -1) { subscribers[subscriberIndex].email = newEmail; // E-Mail-Adresse aktualisieren fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); bot.sendMessage(chatId, '✅ Ihre E-Mail-Adresse wurde erfolgreich aktualisiert.'); } } else { bot.sendMessage(chatId, '❌ Ungültige E-Mail-Adresse. Bitte versuchen Sie es erneut.'); } }); } else if (query.data === 'send_newsletter') { sendNewsletter(); // Newsletter sofort senden bot.sendMessage(chatId, '📧 Der Newsletter wurde gesendet!'); } else if (query.data === 'list_subscribers') { // Hier wird die Abonnentenliste formatiert const subscriberList = subscribers.map(subscriber => `🔹 @${subscriber.username} - ${subscriber.email}`).join('\n') || 'Keine Abonnenten gefunden.'; bot.sendMessage(chatId, `📋 Abonnenten:\n\n${subscriberList}`); } else if (query.data === 'remove_subscriber') { bot.sendMessage(chatId, 'Bitte geben Sie die E-Mail-Adresse des Abonnenten ein, den Sie entfernen möchten:'); bot.once('message', (msg) => { const emailToRemove = msg.text; const subscriberIndex = subscribers.findIndex(subscriber => subscriber.email === emailToRemove); if (subscriberIndex !== -1) { subscribers.splice(subscriberIndex, 1); // Abonnenten entfernen fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); bot.sendMessage(chatId, `✅ Der Abonnent ${emailToRemove} wurde entfernt.`); } else { bot.sendMessage(chatId, '❌ Abonnent nicht gefunden.'); } }); } }); // Lade Abonnenten beim Start loadSubscribers(); // Profilbefehl bot.onText(/\/profil/, (msg) => { const chatId = msg.chat.id; const userFilePath = path.join(__dirname, 'user.yml'); const subscribersFilePath = path.join(__dirname, 'subscribers.json'); const wishesFilePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`); const feedbackFilePath = path.join(__dirname, 'feedback.log'); // Schritt 1: Benutzerinformationen aus user.yml lesen fs.readFile(userFilePath, 'utf8', (err, userData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${userFilePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Laden der Benutzerinformationen.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } const users = load(userData); const user = users[chatId] || {}; // Benutzerdaten für den aktuellen Benutzer // Initialisiere Benutzerinformationen const userName = escapeMarkdownV2(user.username || 'Unbekannt'); const userId = chatId; const firstUsedDate = escapeMarkdownV2(formatDate(user.firstUsed || new Date().toISOString())); // Aktuelles Datum verwenden, falls nicht vorhanden // Benutzerlevel initialisieren const commandCount = user.commandCount || 0; // Anzahl der Befehle aus den Benutzerdaten const wishesCount = user.wishesCount || 0; // Anzahl der Wünsche aus Benutzerdaten const userLevel = getUserLevel(commandCount, wishesCount); // Benutzerlevel ermitteln // Admin und Dev IDs aus .env auslesen const adminIds = [process.env.USER1_ID, process.env.USER2_ID]; const devId = process.env.DEV_CHAT_ID; // Bestimme die Rolle basierend auf der ID let roles = []; if (adminIds.includes(String(chatId))) { roles.push('Admin'); } if (String(chatId) === devId) { roles.push('DEV'); } const role = roles.length > 0 ? roles.join(', ') : 'Benutzer'; fs.readFile(subscribersFilePath, 'utf8', (err, subsData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${subscribersFilePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Laden des Newsletter-Status.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } let subscribers; try { subscribers = JSON.parse(subsData); // JSON sicher parsen } catch (parseErr) { console.error('Fehler beim Parsen der subscribers.json:', parseErr); bot.sendMessage(chatId, 'Fehler beim Verarbeiten der Abonnentendaten.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } // Status prüfen, ob der Benutzer abonniert ist const isSubscribed = subscribers.some(subscriber => subscriber.chatId === chatId); const newsletterStatus = isSubscribed ? 'Ja' : 'Nein'; // Schritt 3: Wünsche aus wishes_${chatId}.json lesen fs.readFile(wishesFilePath, 'utf8', (err, wishesData) => { let wishesCount = 0; // Initialisierung der Wünsche let notificationStatus = user.notifications ? 'Ja' : 'Nein'; if (!err) { try { const userWishes = JSON.parse(wishesData); wishesCount = userWishes.length; } catch (parseErr) { console.error('Fehler beim Parsen der wishes-Datei:', parseErr); } } // Schritt 4: Anzahl der Feedbacks zählen fs.stat(feedbackFilePath, (err) => { let feedbackCount = 0; // Standardwert für Feedbacks if (!err) { // Datei existiert fs.readFile(feedbackFilePath, 'utf8', (err, feedbackData) => { if (!err) { const feedbackLines = feedbackData.split('\n'); feedbackCount = feedbackLines.filter(line => line.includes(`chatId ${chatId}`)).length; // Zähle nur die Feedbacks des aktuellen Benutzers } // Benutzerlevel aktualisieren basierend auf den aktuellen Wünschen const updatedUserLevel = getUserLevel(commandCount, wishesCount); // Schritt 5: Nachricht formatieren und senden const favoriteGenres = user.favoriteGenres && user.favoriteGenres.length > 0 ? user.favoriteGenres.join(', ') // Genres als kommagetrennte Liste : 'Nicht festgelegt'; // Standardwert, wenn keine Genres ausgewählt sind // Nachtmodus-Anzeige let nightModeText; if (user.nightModes && user.nightModes.length > 0) { const nightMode = user.nightModes[0]; // Nimm den ersten Nachtmodus const startTime = nightMode.startTime; const endTime = nightMode.endTime; nightModeText = `🌙 Nachtmodus: ${startTime} \\- ${endTime}`; // Escape des Minuszeichens } else { nightModeText = '🌙 Nachtmodus: Nicht aktiv'; } const profileMessage = ` 📝 *Profil Informationen:*\n\n 👤 *Name:* @${userName}\n 🔑 *ID:* ${userId}\n 👤 *Nutzerrolle:* ${role}\n 🌟 *Benutzerlevel:* ${updatedUserLevel}\n 📅 *Registrierung:* ${firstUsedDate}\n 📰 *Newsletter:* ${newsletterStatus}\n 📋 *Anzahl der Wünsche:* ${wishesCount}\n 📬 *Anzahl der Feedbacks:* ${feedbackCount}\n 🔔 *Benachrichtigung:* ${notificationStatus}\n ${nightModeText}\n 🎭 *Lieblingsgenre:* ${favoriteGenres}\n `.trim(); // Whitespace entfernen // Sende Profilinformationen und zeige Button an bot.sendMessage(chatId, profileMessage, { parse_mode: 'MarkdownV2', reply_markup: { inline_keyboard: [ [{ text: 'Profil Bearbeiten', callback_data: 'edit_profile' }] ] } }).catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); }); } else { // Datei existiert nicht, einfach die Nachricht senden const favoriteGenres = user.favoriteGenres && user.favoriteGenres.length > 0 ? user.favoriteGenres.join(', ') // Genres als kommagetrennte Liste : 'Nicht festgelegt'; // Standardwert, wenn keine Genres ausgewählt sind const nightModeText = user.nightModes && user.nightModes.length > 0 ? `🌙 Nachtmodus: ${user.nightModes[0].startTime} \\- ${user.nightModes[0].endTime}` : '🌙 Nachtmodus: Nicht aktiv'; const profileMessage = ` 📝 *Profil Informationen:*\n\n 👤 *Name:* @${userName}\n 🔑 *ID:* ${userId}\n 👤 *Nutzerrolle:* ${role}\n 🌟 *Benutzerlevel:* ${updatedUserLevel}\n 📅 *Registrierung:* ${firstUsedDate}\n 📰 *Newsletter:* ${newsletterStatus}\n 📋 *Anzahl der Wünsche:* ${wishesCount}\n 📬 *Anzahl der Feedbacks:* 0\n 🔔 *Benachrichtigung:* ${notificationStatus}\n ${nightModeText}\n 🎭 *Lieblingsgenre:* ${favoriteGenres}\n `.trim(); // Whitespace entfernen bot.sendMessage(chatId, profileMessage, { parse_mode: 'MarkdownV2', reply_markup: { inline_keyboard: [ [{ text: 'Profil Bearbeiten', callback_data: 'edit_profile' }] ] } }).catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); }); }); }); }); // Callback query handler for profile editing bot.on('callback_query', (callbackQuery) => { const action = callbackQuery.data; const chatId = callbackQuery.message.chat.id; if (action === 'edit_profile') { // Zeige Bearbeitungsoptionen an, wenn der Benutzer "Profil Bearbeiten" drückt bot.sendMessage(chatId, '🔍 Was möchten Sie tun? Wählen Sie eine der folgenden Optionen:', { reply_markup: { inline_keyboard: [ [ { text: 'Genre bearbeiten', callback_data: 'edit_genres' }, { text: 'Punkte löschen', callback_data: 'delete_points' }, { text: 'Profil zurücksetzen', callback_data: 'reset_profile' } ], [ { text: 'Profil löschen', callback_data: 'delete_profile' } ] ] } }); } else if (action === 'edit_genres') { // Genre-Auswahl anzeigen bot.sendMessage(chatId, '🎬 Wählen Sie Ihre Lieblingsgenres aus:', { reply_markup: { inline_keyboard: [ [ { text: 'Action', callback_data: 'select_genre_Action' }, { text: 'Drama', callback_data: 'select_genre_Drama' }, { text: 'Komödie', callback_data: 'select_genre_Comedy' } ], [ { text: 'Syfy', callback_data: 'select_genre_Syfy' }, { text: 'Romantik', callback_data: 'select_genre_Romance' }, { text: 'Thriller', callback_data: 'select_genre_Thriller' } ], [ { text: 'Fantasy', callback_data: 'select_genre_Fantasy' }, { text: 'Family', callback_data: 'select_genre_Family' }, { text: 'Zeichentrick', callback_data: 'select_genre_Animation' } ], [ { text: 'Anime', callback_data: 'select_genre_Anime' }, { text: 'Horror', callback_data: 'select_genre_Horror' }, { text: 'Katastrophen', callback_data: 'select_genre_Katastrophen' } ], [ { text: 'Krimi', callback_data: 'select_genre_Krimi' }, { text: 'Mystery', callback_data: 'select_genre_Mystery' }, { text: 'Western', callback_data: 'select_genre_Western' } ], [ { text: 'Abenteuer', callback_data: 'select_genre_Abenteuer' }, { text: 'Dokumentation', callback_data: 'select_genre_Dokumentation' } ] ] } }); } else if (action.startsWith('select_genre_')) { // Logik zum Hinzufügen/Entfernen des Genres vom Benutzerprofil const selectedGenre = action.split('_')[2]; // Hier wird die Logik zum Speichern des Genres ins Profil hinzugefügt fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } const users = load(userData); // Überprüfen, ob der Benutzer existiert if (users[chatId]) { // Genre zur Liste der Lieblingsgenres hinzufügen oder entfernen if (!users[chatId].favoriteGenres) { users[chatId].favoriteGenres = []; } const genreIndex = users[chatId].favoriteGenres.indexOf(selectedGenre); if (genreIndex === -1) { users[chatId].favoriteGenres.push(selectedGenre); bot.sendMessage(chatId, `✅ ${selectedGenre} wurde zu Ihren Lieblingsgenres hinzugefügt.`); } else { users[chatId].favoriteGenres.splice(genreIndex, 1); bot.sendMessage(chatId, `❌ ${selectedGenre} wurde aus Ihren Lieblingsgenres entfernt.`); } // Schreibe die aktualisierten Benutzerinformationen zurück in die Datei fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } else { bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } else if (action === 'reset_profile') { // Profil zurücksetzen fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Zurücksetzen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } const users = load(userData); // Überprüfen, ob der Benutzer existiert if (users[chatId]) { // Setze die Standardwerte zurück users[chatId] = { userId: chatId, username: users[chatId].username, // Behalte den Benutzernamen bei firstUsed: users[chatId].firstUsed, // Behalte das erste Nutzungsdatum bei notifications: true, // Standardwert für Benachrichtigungen commandCount: 0, // Punkte zurücksetzen userLevel: 'Neuling', // Benutzerlevel zurücksetzen favoriteGenre: 'Nicht festgelegt' // Setze das Lieblingsgenre auf den Standardwert }; // Schreibe die aktualisierten Benutzerinformationen zurück in die Datei fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Zurücksetzen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { bot.sendMessage(chatId, '✅ Ihr Profil wurde erfolgreich zurückgesetzt.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } else { bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } else if (action === 'delete_profile') { // Profil löschen fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } const users = load(userData); // Überprüfen, ob der Benutzer existiert if (users[chatId]) { // Benutzer aus user.yml entfernen delete users[chatId]; // Schreibe die aktualisierten Benutzerinformationen zurück in die Datei fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } // Lösche zugehörige Einträge in w_offen.json const wOffenFilePath = path.join(__dirname, 'w_offen.json'); // Pfad zur w_offen.json-Datei fs.readFile(wOffenFilePath, 'utf8', (err, wOffenData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${wOffenFilePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } let wOffen; try { wOffen = JSON.parse(wOffenData); // Sicheres Parsen der JSON-Daten } catch (parseErr) { console.error(`Fehler beim Parsen der w_offen.json: ${parseErr}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } // Entferne alle Wünsche des Benutzers wOffen = wOffen.filter(wish => wish.userId !== chatId); // Schreibe die aktualisierten Wünsche zurück in die w_offen.json fs.writeFile(wOffenFilePath, JSON.stringify(wOffen, null, 2), 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben in die Datei ${wOffenFilePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); return; } // Lösche die Datei im Wunsch-Ordner const wunschFolderPath = path.join(__dirname, 'wunsch'); const userFilePath = path.join(wunschFolderPath, `wishes_${chatId}.json`); // Stelle sicher, dass der Dateiname korrekt ist fs.unlink(userFilePath, (err) => { if (err) { console.error(`Fehler beim Löschen der Datei ${userFilePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { bot.sendMessage(chatId, '✅ Ihr Profil wurde erfolgreich gelöscht.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); }); }); }); } else { bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } }); const { load, dump } = require('js-yaml'); // Stelle sicher, dass js-yaml installiert ist // Funktion zum Escapen von Markdown V2-Sonderzeichen function escapeMarkdownV2(text) { return text.replace(/([_*[\]()~>#+\-=.])/g, '\\$1'); // Escape-Zeichen } // Funktion zum Formatieren des Datums in DD-MM-YYYY function formatDate(dateString) { const [year, month, day] = dateString.split('-'); // Datum in Jahr, Monat, Tag zerlegen return `${day}-${month}-${year}`; // DD-MM-YYYY Format zurückgeben } // Funktion zum Bestimmen des Benutzerlevels function getUserLevel(commandCount) { let level = 'Neuling'; // Kriterien für die Vergabe des Benutzerlevels if (commandCount >= 6000) { level = 'Legendärer Benutzer'; } else if (commandCount >= 3000) { level = 'Meister Benutzer'; } else if (commandCount >= 1600) { level = 'Fortgeschrittener Benutzer'; } else if (commandCount >= 800) { level = 'Erfahrener Benutzer'; } else if (commandCount >= 400) { level = 'VIP Benutzer'; } else if (commandCount >= 200) { level = 'Aktiver Benutzer'; } else if (commandCount >= 100) { level = 'Gelegentlicher Benutzer'; } else if (commandCount >= 30) { level = 'Neuling'; } return level; } // Funktion zum Aktualisieren des Benutzerlevels function updateUserLevel(chatId) { const userFilePath = path.join(__dirname, 'user.yml'); // Benutzerinformationen aus user.yml lesen fs.readFile(userFilePath, 'utf8', (err, userData) => { if (err) { console.error(`Fehler beim Lesen der Datei ${userFilePath}: ${err}`); return; } const users = load(userData); const user = users[chatId]; if (user) { // Benutzerlevel bestimmen const commandCount = user.commandCount || 0; const wishCount = user.wishCount || 0; user.userLevel = getUserLevel(commandCount, wishCount); // Benutzerlevel aktualisieren // Benutzerinformationen zurück in die Datei schreiben const updatedUserData = dump(users); fs.writeFile(userFilePath, updatedUserData, 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben der Datei ${userFilePath}: ${err}`); } }); } }); } // Befehl zum Aktualisieren des Benutzerlevels bei jeder Nachricht bot.on('message', (msg) => { const chatId = msg.chat.id; const userFilePath = path.join(__dirname, 'user.yml'); // Hier kannst du die Anzahl der Befehle erhöhen fs.readFile(userFilePath, 'utf8', (err, userData) => { if (!err) { const users = load(userData); if (users[chatId]) { users[chatId].commandCount = (users[chatId].commandCount || 0) + 1; const updatedUserData = dump(users); fs.writeFile(userFilePath, updatedUserData, 'utf8', (err) => { if (err) { console.error(`Fehler beim Schreiben der Datei ${userFilePath}: ${err}`); } else { // Benutzerlevel aktualisieren, nachdem die Anzahl der Befehle erhöht wurde updateUserLevel(chatId); } }); } } }); }); // Befehl zum Sichern der Dateien bot.onText(/\/backup/, (msg) => { const chatId = msg.chat.id; // Überprüfen, ob die Nachricht vom Dev kommt if (msg.from.id.toString() === process.env.DEV_CHAT_ID) { const filesToBackup = [ 'user.yml', 'faq.json', 'subscribers.json', 'w_offen.json', 'feedback.log', 'command_history.json', 'dev_reports.json' ]; const backupFolder = path.join(__dirname, 'wunsch'); // Pfad zum Wunsch-Ordner const zipFilePath = path.join(__dirname, 'backup.zip'); // Speicherort für die ZIP-Datei // Erstelle einen ZIP-Stream const output = fs.createWriteStream(zipFilePath); const archive = archiver('zip'); output.on('close', () => { console.log(`Backup abgeschlossen, ${archive.pointer()} total bytes.`); bot.sendDocument(chatId, zipFilePath, { caption: '📦 Hier ist dein Backup!' }) // Sende die ZIP-Datei an den Developer .then(() => { fs.unlinkSync(zipFilePath); // Lösche die ZIP-Datei nach dem Senden }) .catch(err => { console.error(`Fehler beim Senden der Backup-Datei: ${err.message}`); bot.sendMessage(chatId, `❌ Fehler beim Senden der Backup-Datei: ${err.message}`); }); }); archive.on('error', (err) => { console.error(`Fehler beim Erstellen des Backups: ${err}`); bot.sendMessage(chatId, `❌ Fehler beim Erstellen des Backups: ${err.message}`); }); archive.pipe(output); // Füge die Dateien hinzu filesToBackup.forEach(file => { const filePath = path.join(__dirname, file); if (fs.existsSync(filePath)) { archive.file(filePath, { name: file }); } }); // Füge den Wunsch-Ordner hinzu, wenn er existiert if (fs.existsSync(backupFolder)) { archive.directory(backupFolder + '/', 'wunsch/'); // Füge den Inhalt des Wunsch-Ordners hinzu } archive.finalize(); // Beende die Archivierung } else { bot.sendMessage(chatId, '🚫 Dieser Befehl ist nur für den Developer verfügbar.'); } }); let debugMode = false; bot.onText(/\/setdebug/, (msg) => { const chatId = msg.chat.id; if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) { return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich."); } debugMode = !debugMode; const status = debugMode ? "aktiviert" : "deaktiviert"; bot.sendMessage(chatId, `🐞 Debug-Modus wurde ${status}.`); }); const os = require('os'); bot.onText(/\/serverinfo/, (msg) => { const chatId = msg.chat.id; if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) { return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich."); } const totalMemory = os.totalmem(); const freeMemory = os.freemem(); // Umrechnung in Gigabyte const totalMemoryGB = (totalMemory / (1024 ** 3)).toFixed(2); // Umrechnen in GB const freeMemoryGB = (freeMemory / (1024 ** 3)).toFixed(2); // Umrechnen in GB const info = `🖥️ *Server-Info:*\n\n\n` + `🔹 Plattform: ${os.platform()}\n\n` + `🔹 Architektur: ${os.arch()}\n\n` + `🔹 Gesamter Speicher: ${totalMemoryGB} GB\n\n` + `🔹 Freier Speicher: ${freeMemoryGB} GB`; bot.sendMessage(chatId, info); }); bot.onText(/\/healthcheck/, async (msg) => { const chatId = msg.chat.id; if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) { return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich."); } let responseMessages = []; responseMessages.push("🖥️ *Bot-Status:*\n\n"); // 1. Überprüfung, ob der Bot online ist responseMessages.push("✅ *Bot ist online und funktionsfähig.*\n"); // 2. Überprüfung der Verbindung zur Plex API try { const plexResponse = await axios.get(`${process.env.PLEX_DOMAIN}/status`, { headers: { 'X-Plex-Token': process.env.PLEX_TOKEN } }); responseMessages.push("✅ *Verbindung zur Plex API ist erfolgreich.*\n\n\n"); } catch (error) { responseMessages.push("❌ *Verbindung zur Plex API fehlgeschlagen.*\n\n\n"); } // 3. Überprüfung, ob wichtige Dateien vorhanden sind responseMessages.push("📂 *Dateiüberprüfung:*\n\n"); const requiredFiles = ['user.yml', 'faq.json', 'subscribers.json', 'dev_reports.json', 'w_offen.json', 'feedback.log', 'command_history.json', 'error.log', 'Cache/cache-series.json', 'Cache/cache.json', 'Log/message.log', 'wunsch', 'backups']; for (const file of requiredFiles) { if (fs.existsSync(file)) { responseMessages.push(`✅ *Datei ${file} ist vorhanden.*\n`); } else { responseMessages.push(`❌ *Datei ${file} fehlt.*\n`); } } // Sende die gesammelten Antworten als Nachricht bot.sendMessage(chatId, responseMessages.join(''), { parse_mode: 'Markdown' }); }); const commandHistoryFilePath = './command_history.json'; // Pfad zur Datei let commandHistory = []; // Lade die Historie aus der Datei, falls vorhanden if (fs.existsSync(commandHistoryFilePath)) { try { const data = fs.readFileSync(commandHistoryFilePath, 'utf8'); // Stelle sicher, dass die Datei als UTF-8 gelesen wird commandHistory = JSON.parse(data); } catch (error) { console.error("Fehler beim Laden der Kommando-Historie:", error.message); commandHistory = []; // Setze die Historie zurück, wenn ein Fehler auftritt } } // Funktion zum Protokollieren der Befehle function logCommand(command, username) { const timestamp = new Date(); // Aktuelles Datum und Uhrzeit const formattedDate = timestamp.toLocaleString(); // Formatierung des Datums // Füge den Befehl zur Historie hinzu commandHistory.push(`${formattedDate} - @${username} - ${command}`); if (commandHistory.length > 30) { commandHistory.shift(); // Behalte nur die letzten 10 Befehle } // Speichere die Historie in der Datei fs.writeFileSync(commandHistoryFilePath, JSON.stringify(commandHistory, null, 2)); } // Funktion zum Formatieren der Historie function formatCommandHistory(history) { return history.map(entry => { const [date, time, username, command] = entry.split(' - '); return `${date} ${time} | ${username} | ${command}`; }).join('\n'); // Jeder Eintrag wird in eine neue Zeile geschrieben } bot.onText(/\/command_history/, (msg) => { const chatId = msg.chat.id; if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) { return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich."); } if (commandHistory.length === 0) { return bot.sendMessage(chatId, "📜 Keine Befehle in der Historie gefunden."); } const historyMessage = `🗃️ Kommando-Historie:\n\n` + `Datum - Uhrzeit | Benutzername | Befehl\n` + `-----------------------------------------\n` + formatCommandHistory(commandHistory).replace(/,/g, ''); // Entferne Kommas und füge neue Zeilen hinzu bot.sendMessage(chatId, historyMessage); }); // Beispiel für andere Befehle bot.onText(/\/start/, (msg) => { logCommand('/update', msg.from.username); // Logik für den Update-Befehl... }); bot.onText(/\/notification_on/, (msg) => { logCommand('/notification_on', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/notification_off/, (msg) => { logCommand('/notification_off', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/serien/, (msg) => { logCommand('/serien', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/latestmovie/, (msg) => { logCommand('/latestmovie', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/latest10movies/, (msg) => { logCommand('/latest10movies', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/top_rated/, (msg) => { logCommand('/top_rated', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/wunsch/, (msg) => { logCommand('/wunsch', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/trailer/, (msg) => { logCommand('/trailer', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/empfehlung/, (msg) => { logCommand('/empfehlung', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/newsletter/, (msg) => { logCommand('/newsletter', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/help/, (msg) => { logCommand('/help', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/profil/, (msg) => { logCommand('/profil', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/w_list/, (msg) => { logCommand('/w_list', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/dev/, (msg) => { logCommand('/dev', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/feedback/, (msg) => { logCommand('/feedback', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/faq/, (msg) => { logCommand('/faq', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/info/, (msg) => { logCommand('/info', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/bot/, (msg) => { logCommand('/bot', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/admin/, (msg) => { logCommand('/admin', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/open_wishes/, (msg) => { logCommand('/open_wishes', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/user/, (msg) => { logCommand('/user', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/update/, (msg) => { logCommand('/update', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/logs/, (msg) => { logCommand('/logs', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/logs_delete/, (msg) => { logCommand('/logs_delete', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/f_log/, (msg) => { logCommand('/f_log', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/add_faq/, (msg) => { logCommand('/add_faq', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/del_faq/, (msg) => { logCommand('/del_faq', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/command_history/, (msg) => { logCommand('/command_history', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/backup/, (msg) => { logCommand('/backup', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/serverinfo/, (msg) => { logCommand('/serverinfo', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/healthcheck/, (msg) => { logCommand('/healthcheck', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/setdebug/, (msg) => { logCommand('/setdebug', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/support/, (msg) => { logCommand('/support', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/night/, (msg) => { logCommand('/night', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/passwd/, (msg) => { logCommand('/passwd', msg.from.username); // Logik für den Befehl... }); bot.onText(/\/key/, (msg) => { logCommand('/key', msg.from.username); // Logik für den Befehl... }); const userId1 = Number(process.env.USER1_ID); // USER1_ID aus .env laden und in Zahl umwandeln const userId2 = Number(process.env.USER2_ID); // USER2_ID aus .env laden und in Zahl umwandeln bot.onText(/\/support/, (msg) => { const chatId = msg.chat.id; // Direkt die Telegram-ID verwenden const adminId = 5507179337; // Überprüfen, ob die Benutzer-ID in den autorisierten IDs enthalten ist if (msg.from.id !== userId1 && msg.from.id !== userId2) { return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für autorisierte Benutzer zugänglich."); } bot.sendMessage(chatId, "💬 Bitte gib zusätzliche Informationen für den Support an:"); // Setze einen Listener für die nächste Nachricht des Benutzers bot.once('message', async (reply) => { const additionalText = reply.text || "Keine zusätzlichen Informationen bereitgestellt."; const filesToZip = [ 'error.log', 'command_history.json', 'user.yml', 'subscribers.json', ]; const logFolder = 'Log'; const zipPath = 'support.zip'; const output = fs.createWriteStream(zipPath); const archive = archiver('zip'); output.on('close', async () => { const botName = process.env.BOT_NAME || "Unbekannter Bot"; // Bot-Namen aus der .env const adminNames = `${userId1}, ${userId2}`; // Namen der Administratoren const supportMessage = `🛠️ *Externe Support-Anfrage* \n\n\n` + `🔧 Bot-Name: @${botName}\n\n` + `👨‍💻 Administratoren:\n ${adminNames}\n\n\n` + `💬 Zusätzliche Informationen:\n\n ${additionalText}`; await bot.sendMessage(adminId, supportMessage, { parse_mode: 'Markdown' }); await bot.sendDocument(adminId, zipPath); fs.unlinkSync(zipPath); // Löscht die ZIP-Datei nach dem Senden }); archive.on('error', (err) => { throw err; }); archive.pipe(output); // Füge die Dateien zum ZIP-Archiv hinzu filesToZip.forEach((file) => { if (fs.existsSync(file)) { archive.file(file, { name: file }); } else { console.warn(`Datei ${file} nicht gefunden.`); } }); // Füge den Log-Ordner hinzu if (fs.existsSync(logFolder)) { archive.directory(logFolder + '/', logFolder + '/'); } await archive.finalize(); // Warte, bis das Archiv abgeschlossen ist }); }); // Handler für den /admin-Befehl bot.onText(/\/admin/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; // Prüfe, ob der Benutzer autorisiert ist if (userId.toString() === USER1_ID || userId.toString() === USER2_ID) { bot.sendMessage(chatId, 'Bitte gib die Nachricht ein, die du an alle Benutzer senden möchtest:', { reply_markup: { force_reply: true } }).then(() => { bot.once('message', async (msg) => { if (msg.chat.id === chatId && msg.text) { const messageText = msg.text; // Sende die Nachricht an alle Benutzer const users = yaml.load(USER_YML_PATH); const sendMessages = Object.keys(users).map(userChatId => { return bot.sendMessage(userChatId, `❗️Systemnachricht\n\n"${messageText}"`).catch(error => { logError(`Fehler beim Senden der Systemnachricht an chatId ${userChatId}: ${error.message}`); }); }).filter(promise => promise !== undefined); await Promise.all(sendMessages); bot.sendMessage(chatId, 'Nachricht wurde an alle Benutzer gesendet.').catch(error => { logError(`Fehler beim Senden der Bestätigung an chatId ${chatId}: ${error.message}`); }); } }); }).catch(error => { logError(`Fehler beim Senden der Nachrichteneingabeaufforderung an chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); } }); // Pfad zur Cache-Datei const CACHE_FILE_PATH = path.join('Cache', 'cache-series.json'); // Funktion zum Speichern des Caches in eine Datei function saveSeriesCache(series) { fs.writeFileSync(CACHE_FILE_PATH, JSON.stringify(series, null, 2)); } // Funktion zum Laden des Caches aus einer Datei function loadSeriesCache() { if (fs.existsSync(CACHE_FILE_PATH)) { return JSON.parse(fs.readFileSync(CACHE_FILE_PATH)); } return null; } // Funktion zum Abrufen aller Serien async function fetchAllSeries() { try { const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); const sections = sectionsData.MediaContainer.Directory; let series = []; for (const section of sections) { const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`; const sectionData = await fetchPlexData(sectionUrl); if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) { const metadata = sectionData.MediaContainer.Metadata; series = series.concat(metadata.filter(media => media.type === 'show')); } } series.sort((a, b) => (b.addedAt || 0) - (a.addedAt || 0)); return series; } catch (error) { logError(`Error fetching all series: ${error.message}`); throw error; } } // Funktion zum Abrufen der Serien mit Caching async function fetchSeriesWithCache() { const cachedSeries = loadSeriesCache(); if (cachedSeries) { logMessage('Series fetched from cache'); return cachedSeries; } try { const series = await fetchAllSeries(); saveSeriesCache(series); logMessage('Series fetched from API and cached'); return series; } catch (error) { logError(`Error fetching series: ${error.message}`); throw error; } } // Automatische Cache-Aktualisierung jede Stunde schedule.scheduleJob('0 * * * *', async () => { try { const series = await fetchAllSeries(); saveSeriesCache(series); logMessage('Series cache updated automatically'); } catch (error) { logError(`Error updating series cache: ${error.message}`); } }); // Handler für den /serien-Befehl bot.onText(/\/serien/, async (msg) => { const chatId = msg.chat.id; try { const series = await fetchSeriesWithCache(); const seriesList = series.map((s, index) => `${index + 1}. ${s.title}`).join('\n'); bot.sendMessage(chatId, `Hier sind die Serien in deiner Plex-Mediathek:\n\n${seriesList}`, { reply_markup: { inline_keyboard: [ [{ text: 'Weitere Informationen', callback_data: 'get_series_info' }] ] } }); } catch (error) { bot.sendMessage(chatId, 'Fehler beim Abrufen der Serien. Bitte versuche es später erneut.'); logError(`Error handling /serien command: ${error.message}`); } }); // Handler für die Callback-Abfragen von Inline-Buttons bot.on('callback_query', async (query) => { const chatId = query.message.chat.id; if (query.data.startsWith('get_series_info')) { try { const series = await fetchSeriesWithCache(); const responseMessage = `Bitte gib die Nummer der Serie ein, um weitere Informationen zu erhalten.`; bot.sendMessage(chatId, responseMessage, { reply_markup: { force_reply: true } }).then(() => { bot.once('message', async (msg) => { if (msg.chat.id === chatId && msg.text) { const seriesNumber = parseInt(msg.text, 10); if (!isNaN(seriesNumber) && seriesNumber > 0 && seriesNumber <= series.length) { const seriesInfo = series[seriesNumber - 1]; const { title, summary, thumb, addedAt } = seriesInfo; const imageUrl = `${PLEX_DOMAIN}${thumb}?X-Plex-Token=${PLEX_TOKEN}`; // Beispiel-URL anpassen // Debugging-Ausgabe console.log(`Image URL: ${imageUrl}`); // Formatieren des Hinzufügungsdatums const addedDate = addedAt ? dayjs(addedAt * 1000).format('DD.MM.YYYY') : 'Unbekannt'; const caption = `📺 *Titel:* ${title}\n\n` + `📝 *Beschreibung:* \n${summary}\n\n` + `📅 *Hinzugefügt am:* ${addedDate}`; // Bild herunterladen und lokal speichern const imagePath = path.join(__dirname, 'temp_image.jpg'); const writer = fs.createWriteStream(imagePath); const response = await axios({ url: imageUrl, method: 'GET', responseType: 'stream' }); response.data.pipe(writer); writer.on('finish', async () => { // Senden des Bildes try { await bot.sendPhoto(chatId, imagePath, { caption, parse_mode: 'Markdown' }); // Optional: Nach dem Senden das Bild löschen fs.unlinkSync(imagePath); } catch (sendPhotoError) { logError(`Fehler beim Senden des Fotos: ${sendPhotoError.message}`); bot.sendMessage(chatId, 'Fehler beim Senden des Bildes. Bitte versuche es später erneut.'); } }); writer.on('error', (error) => { logError(`Fehler beim Schreiben der Bilddatei: ${error.message}`); bot.sendMessage(chatId, 'Fehler beim Abrufen des Bildes. Bitte versuche es später erneut.'); }); } else { bot.sendMessage(chatId, 'Ungültige Nummer. Bitte gib eine gültige Nummer ein.'); } } }); }).catch(error => { logError(`Fehler beim Senden der Eingabeaufforderung: ${error.message}`); }); } catch (error) { bot.sendMessage(chatId, 'Fehler beim Abrufen der Serieninformationen. Bitte versuche es später erneut.'); logError(`Error handling callback query: ${error.message}`); } } }); // Log-Error-Funktion (Optional) function logError(message) { const timestamp = new Date().toISOString(); fs.appendFileSync('error.log', `${timestamp} - ${message}\n`); } // Umgebungsvariable für die Chat-ID der Entwickler const DEV_CHAT_ID = parseInt(process.env.DEV_CHAT_ID, 10); // Der Pfad zur Datei, in der die Dev Reports gespeichert werden const DEV_REPORTS_FILE_PATH = path.join(__dirname, 'dev_reports.json'); // Funktion zum Erstellen der Datei, wenn sie nicht vorhanden ist function createDevReportsFile() { if (!fs.existsSync(DEV_REPORTS_FILE_PATH)) { fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify([])); // Leeres Array initialisieren console.log('Dev Reports Datei erstellt.'); } } // Funktion zum Erstellen des Inline-Keyboards function getDevOptionsKeyboard() { return { reply_markup: { inline_keyboard: [ [{ text: '💡 Funktionswunsch', callback_data: 'dev_request' }], [{ text: '🐞 Bug melden', callback_data: 'dev_bug' }], [{ text: '🎬 Film Report', callback_data: 'film_report' }] ] } }; } // Handler für den /dev-Befehl bot.onText(/\/dev/, (msg) => { const chatId = msg.chat.id; const message = '🔧 *Dev-Feedback* - Bitte wählen Sie eine der folgenden Optionen, um Ihr Feedback zu übermitteln:'; bot.sendMessage(chatId, message, { parse_mode: 'Markdown', ...getDevOptionsKeyboard() }); }); // Handler für Callback-Queries im /dev-Befehl bot.on('callback_query', (query) => { console.log('Callback-Query-Daten:', query.data); // Debugging-Ausgabe const chatId = query.message.chat.id; const data = query.data; let responseText = ''; let replyMarkup; switch (data) { case 'dev_request': responseText = '✏️ *Bitte geben Sie Ihren Funktionswunsch ein:*'; replyMarkup = { reply_markup: { force_reply: true } }; break; case 'dev_bug': responseText = '✏️ *Bitte beschreiben Sie den Bug, den Sie melden möchten:*'; replyMarkup = { reply_markup: { force_reply: true } }; break; case 'film_report': responseText = '🎬 *Bitte geben Sie den Film Report ein:* \n\nTitel & Fehlerbeschreibung:'; replyMarkup = { reply_markup: { force_reply: true } }; break; default: // Kein Popup oder Nachricht senden, wenn die Auswahl unbekannt ist return; } bot.sendMessage(chatId, responseText, { parse_mode: 'Markdown', ...replyMarkup }); }); // Handler für die Antworten auf die Feedback-Anfrage bot.on('message', async (msg) => { if (msg.reply_to_message && (msg.reply_to_message.text.includes('Bitte geben Sie Ihren Funktionswunsch ein:') || msg.reply_to_message.text.includes('Bitte beschreiben Sie den Bug, den Sie melden möchten:') || msg.reply_to_message.text.includes('Bitte geben Sie den Film Report ein:'))) { const chatId = msg.chat.id; const text = msg.text; const userName = msg.from.first_name + (msg.from.last_name ? ` ${msg.from.last_name}` : ''); const userId = msg.from.id; let messageType; let report; if (msg.reply_to_message.text.includes('Funktionswunsch')) { messageType = 'Funktionswunsch'; report = { type: messageType, user: { name: userName, id: userId }, message: text }; } else if (msg.reply_to_message.text.includes('Bug')) { messageType = 'Bug'; report = { type: messageType, user: { name: userName, id: userId }, message: text }; } else if (msg.reply_to_message.text.includes('Film Report')) { // Hier wird der gesamte Text für Titel und Fehlerbeschreibung verwendet const userMessage = text.trim(); // Benutzertext messageType = 'Film Report'; report = { type: messageType, user: { name: userName, id: userId }, message: userMessage // Der gesamte Benutzertext wird als Nachricht verwendet }; } // Dev Report in die Datei schreiben try { // Nur die DEV_CHAT_ID für Bug und Funktionswunsch if (messageType === 'Bug' || messageType === 'Funktionswunsch') { console.log('Sende Nachricht an Entwickler-Chat-ID:', DEV_CHAT_ID); // Debugging-Ausgabe await bot.sendMessage(DEV_CHAT_ID, formatDevMessage(report), { parse_mode: 'Markdown' }); } // DEV_CHAT_ID und USER2_ID für Film Reports else if (messageType === 'Film Report') { console.log('Sende Nachricht an Entwickler-Chat-ID und USER2_ID:', DEV_CHAT_ID, USER2_ID); // Debugging-Ausgabe await bot.sendMessage(DEV_CHAT_ID, formatDevMessage(report), { parse_mode: 'Markdown' }); await bot.sendMessage(USER2_ID, formatDevMessage(report), { parse_mode: 'Markdown' }); } console.log('Nachricht erfolgreich gesendet.'); // Dev Report in die JSON-Datei speichern saveDevReport(report); bot.sendMessage(chatId, '✅ Ihre Nachricht wurde erfolgreich gesendet! Vielen Dank.'); } catch (error) { console.error('Fehler beim Senden der Nachricht:', error); bot.sendMessage(chatId, '🚫 Etwas ist schiefgelaufen. Ihre Nachricht konnte nicht gesendet werden.'); } } }); // Funktion zur Formatierung der Dev-Nachricht function formatDevMessage(report) { return `📩 *${report.type}*\n\n` + `von: ${report.user.name} (${report.user.id})\n\n` + `"${report.message}"`; } // Funktion zum Speichern des Dev Reports in die JSON-Datei function saveDevReport(report) { const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH)); report.id = reports.length; // ID basierend auf der aktuellen Länge des Arrays zuweisen reports.push(report); fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(reports, null, 2)); // Schön formatieren } // Starte den Bot und erstelle die Datei createDevReportsFile(); // Handler für den /bot-Befehl bot.onText(/\/bot/, (msg) => { const chatId = msg.chat.id; try { // Bot-Version dynamisch aus der .env-Datei const botVersion = process.env.BOT_VERSION || "1.7.0"; // Laufzeit des Prozesses in Sekunden const uptime = process.uptime(); const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); const seconds = Math.floor(uptime % 60); const runtime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; // Benutzeranzahl aus user.yml const users = yaml.load(USER_YML_PATH); const userCount = Object.keys(users).length; // Abonnentenanzahl aus subscribers.json const subscribers = JSON.parse(fs.readFileSync('subscribers.json', 'utf8')); const subscriberCount = subscribers.length; // Letzter Neustart des Bots const lastRestart = dayjs().format('YYYY-MM-DD HH:mm:ss'); // Speicherbelegung const memoryUsage = process.memoryUsage(); const memoryStats = `Heap Total: ${Math.round(memoryUsage.heapTotal / 1024 / 1024)} MB, Heap Used: ${Math.round(memoryUsage.heapUsed / 1024 / 1024)} MB`; // Cache-Status const cacheKeys = cache.keys().length; const cacheStats = cache.getStats(); // Hole die vollständigen Cache-Stats const cacheTTL = cacheStats.stdTTL || 0; // Setze Default-Wert auf 0, falls nicht definiert // Fehlerprotokoll-Status const errorLogCount = fs.existsSync(ERROR_LOG_PATH) ? fs.readFileSync(ERROR_LOG_PATH, 'utf8').split('\n').length - 1 : 0; // Aktuelle Aufgaben const currentTasks = ` - Cache wird jede Stunde aktualisiert \n - Geplante Überprüfungen neuer Filme alle 1 Minute \n - Newsletter Versand jeden Sonntag \n `; // Bot Token und Webhook URL (falls vorhanden) const botToken = BOT_TOKEN; const webhookStatus = WEBHOOK_URL ? "Aktiv" : "Inaktiv"; // Nachricht erstellen const infoMessage = ` 📊 *Bot Informationen* \n\n 🆙 *Version:* ${botVersion} \n ⏱️ *Laufzeit:* ${runtime} \n 👥 *Benutzeranzahl:* ${userCount} \n 📰 *Abonnentenanzahl:* ${subscriberCount} \n 🔄 *Letzter Neustart:* ${lastRestart} \n 💾 *Speicherbelegung:* ${memoryStats} \n 🔑 *Bot Token:* ${botToken.slice(0, 0)}... (Ausgeblendet für Sicherheit) \n 🌐 *Webhook URL:* ${webhookStatus} \n 🔑 *Cache Keys:* ${cacheKeys} \n ⏳ *Cache TTL:* ${cacheTTL} Sekunden \n 📝 *Fehlerprotokoll-Anzahl:* ${errorLogCount} \n\n 🛠️ *Aktuelle Aufgaben:* \n ${currentTasks.trim()} `; // Inline-Button erstellen const options = { reply_markup: { inline_keyboard: [ [ { text: "Kontakt", url: process.env.CONTACT_LINK // Link aus der .env-Datei } ] ] }, parse_mode: 'Markdown' }; // Nachricht senden bot.sendMessage(chatId, infoMessage, options).catch(error => { logError(`Fehler beim Senden der Bot-Informationen an chatId ${chatId}: ${error.message}`); }); } catch (error) { // Fehlerprotokollierung für unerwartete Fehler logError(`Fehler beim Abrufen von Bot-Informationen: ${error.message}`); bot.sendMessage(chatId, 'Fehler beim Abrufen der Bot-Informationen.').catch(err => { logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${err.message}`); }); } }); // Handler für den /logs-Befehl bot.onText(/\/logs(?: (\d+))?/, (msg, match) => { const chatId = msg.chat.id; const userId = msg.from.id; if (userId.toString() === USER1_ID || userId.toString() === USER2_ID) { const count = match[1] ? parseInt(match[1], 10) : 10; const recentErrors = getRecentErrors(count).join('\n'); const message = recentErrors.length > 0 ? `Fehlermeldungen:\n${recentErrors}` : 'Keine Fehlermeldungen vorhanden.'; bot.sendMessage(chatId, message).catch(error => { logError(`Fehler beim Senden der Logs an chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); } }); // Definiere den Pfad zur feedback.yml const FEEDBACK_FILE_PATH = path.resolve(__dirname, 'Log', 'feedback.yml'); // Handler für den /log_delete-Befehl bot.onText(/\/log_delete/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; if (userId.toString() === USER1_ID || userId.toString() === USER2_ID) { const inlineKeyboard = [ [{ text: 'Error Log Löschen', callback_data: 'delete_error_log' }], [{ text: 'User Log Löschen', callback_data: 'delete_user_log' }], [{ text: 'Feedback Log Löschen', callback_data: 'delete_feedback_log' }] // Neuer Button ]; bot.sendMessage(chatId, 'Wähle, welches Log du löschen möchtest:', { reply_markup: { inline_keyboard: inlineKeyboard } }).catch(error => { logError(`Fehler beim Senden der Log-Lösch-Nachricht an chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); } }); // Handler für Inline-Button-Callbacks bot.on('callback_query', async (callbackQuery) => { const chatId = callbackQuery.message.chat.id; const userId = callbackQuery.from.id; const data = callbackQuery.data; if (data === 'delete_error_log') { // Lösche das gesamte Error Log if (fs.existsSync(ERROR_LOG_PATH)) { fs.unlinkSync(ERROR_LOG_PATH); // Lösche die Error Log Datei komplett bot.answerCallbackQuery(callbackQuery.id, { text: 'Error Log wurde gelöscht.' }); bot.sendMessage(chatId, 'Das Error Log wurde erfolgreich gelöscht.').catch(error => { logError(`Fehler beim Senden der Bestätigung für das Löschen des Error Logs an chatId ${chatId}: ${error.message}`); }); } else { bot.answerCallbackQuery(callbackQuery.id, { text: 'Error Log existiert nicht.' }); } } else if (data === 'delete_user_log') { // Lösche alle User Logs im LOG_DIR try { const files = fs.readdirSync(LOG_DIR); const userLogFiles = files.filter(file => /^\d{4}-\d{2}-\d{2}\.log$/.test(file)); if (userLogFiles.length > 0) { userLogFiles.forEach(file => fs.unlinkSync(path.join(LOG_DIR, file))); // Lösche jede User Log Datei bot.answerCallbackQuery(callbackQuery.id, { text: 'User Logs wurden gelöscht.' }); bot.sendMessage(chatId, 'Alle User Logs wurden erfolgreich gelöscht.').catch(error => { logError(`Fehler beim Senden der Bestätigung für das Löschen der User Logs an chatId ${chatId}: ${error.message}`); }); } else { bot.answerCallbackQuery(callbackQuery.id, { text: 'Keine User Logs zum Löschen gefunden.' }); } } catch (error) { bot.answerCallbackQuery(callbackQuery.id, { text: 'Fehler beim Löschen der User Logs.' }); logError(`Fehler beim Löschen der User Logs: ${error.message}`); } } else if (data === 'delete_feedback_log') { // Lösche die Feedback-Datei if (fs.existsSync(FEEDBACK_FILE_PATH)) { fs.unlinkSync(FEEDBACK_FILE_PATH); // Lösche die Feedback-Datei komplett bot.answerCallbackQuery(callbackQuery.id, { text: 'Feedback Log wurde gelöscht.' }); bot.sendMessage(chatId, 'Das Feedback Log wurde erfolgreich gelöscht.').catch(error => { logError(`Fehler beim Senden der Bestätigung für das Löschen des Feedback Logs an chatId ${chatId}: ${error.message}`); }); } else { bot.answerCallbackQuery(callbackQuery.id, { text: 'Feedback Log existiert nicht.' }); } } else { bot.answerCallbackQuery(callbackQuery.id, { text: 'Unbekannte Auswahl.' }); } }); // Handler für den /user-Befehl bot.onText(/\/user/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; // Überprüfen, ob der Benutzer autorisiert ist if (userId.toString() === USER1_ID || userId.toString() === USER2_ID) { try { // Lade die Benutzer aus der YAML-Datei const users = yaml.load(USER_YML_PATH); let responseMessage = "Benutzerinformationen:\n\n"; // Gehe durch die Benutzer und baue die Antwortnachricht auf for (const [id, user] of Object.entries(users)) { const name = user.username || 'Unbekannt'; const notificationsStatus = user.notifications ? 'Aktiv' : 'Inaktiv'; responseMessage += `Name: ${name}\nID: ${id}\nBenachrichtigung Status: ${notificationsStatus}\n\n`; // Zwei Leerzeilen für Abstand } // Sende die Antwortnachricht bot.sendMessage(chatId, responseMessage.trim()).catch(error => { logError(`Fehler beim Senden der Benutzerinformationen an chatId ${chatId}: ${error.message}`); }); } catch (error) { // Fehlerprotokollierung für unerwartete Fehler logError(`Fehler beim Abrufen der Benutzerinformationen: ${error.message}`); bot.sendMessage(chatId, 'Fehler beim Abrufen der Benutzerinformationen.').catch(err => { logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${err.message}`); }); } } else { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); } }); // Maximale Länge einer Telegram-Nachricht in Zeichen const MAX_MESSAGE_LENGTH = 4096; // Hilfsfunktion zum Aufteilen einer Nachricht in kleinere Teile function splitMessage(message) { const messages = []; while (message.length > MAX_MESSAGE_LENGTH) { let splitIndex = message.lastIndexOf('\n', MAX_MESSAGE_LENGTH); if (splitIndex === -1) { splitIndex = MAX_MESSAGE_LENGTH; // Wenn kein neuer Zeilenumbruch gefunden wird, einfach am Limit aufteilen } messages.push(message.substring(0, splitIndex)); message = message.substring(splitIndex); } if (message.length > 0) { messages.push(message); } return messages; } // Handler für den /top_rated-Befehl bot.onText(/\/top_rated/, async (msg) => { const chatId = msg.chat.id; try { const movies = await fetchTopRatedMovies(); if (movies.length > 0) { // Begrenze die Anzahl der angezeigten Filme auf 20 const topMovies = movies.slice(0, 15); let message = '🌟 *Top 15 Am besten bewertete Filme:*\n\n'; topMovies.forEach((movie, index) => { message += `🎬 *${index + 1}. ${movie.title}* \n` + `⭐ Bewertung: ${movie.rating.toFixed(1)} \n\n`; }); // Teile die Nachricht in kleinere Teile auf, wenn sie zu lang ist const messageParts = splitMessage(message); for (const part of messageParts) { await bot.sendMessage(chatId, part, { parse_mode: 'Markdown' }); } } else { await bot.sendMessage(chatId, '🚫 Keine gut bewerteten Filme gefunden.'); } } catch (error) { logError(`Fehler beim Abrufen der besten Filme für chatId ${chatId}: ${error.message}`); await bot.sendMessage(chatId, 'Beim Abrufen der besten Filme ist ein Fehler aufgetreten.'); } }); // Handler für Inline-Button-Callbacks bot.on('callback_query', (callbackQuery) => { const chatId = callbackQuery.message.chat.id; const userId = callbackQuery.from.id; const data = callbackQuery.data; if (data.startsWith('delete_log_')) { const index = parseInt(data.split('_')[2], 10); const recentErrors = getRecentErrors(); if (index >= 0 && index < recentErrors.length) { recentErrors.splice(index, 1); // Lösche den ausgewählten Eintrag fs.writeFileSync(ERROR_LOG_PATH, recentErrors.join('\n'), 'utf8'); bot.answerCallbackQuery(callbackQuery.id, { text: 'Fehlermeldung gelöscht.' }); bot.sendMessage(chatId, 'Die Fehlermeldung wurde gelöscht.').catch(error => { logError(`Fehler beim Senden der Bestätigungsnachricht über das Löschen der Fehlermeldung an chatId ${chatId}: ${error.message}`); }); } else { bot.answerCallbackQuery(callbackQuery.id, { text: 'Ungültiger Index.' }); } } }); // Funktion zum Abrufen der letzten Fehlermeldungen function getRecentErrors(count = 10) { if (!fs.existsSync(ERROR_LOG_PATH)) return []; const logLines = fs.readFileSync(ERROR_LOG_PATH, 'utf8').trim().split('\n'); return logLines.slice(-count); } // Funktion zum Protokollieren von Fehlern function logError(error) { const errorMessage = `${dayjs().format('HH:mm:ss')} - Error: ${error}\n`; fs.appendFileSync(ERROR_LOG_PATH, errorMessage); } // /start-Befehl verarbeiten bot.onText(/\/start/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; const username = msg.from.username || 'Unbekannt'; // Benutzername, falls vorhanden, sonst 'Unbekannt' // Aktuelles Datum im ISO-Format const firstUsedDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD // Benutzerdaten in user.yml speichern let users = yaml.load(USER_YML_PATH); users[chatId] = { userId: userId, username: username, notifications: true, // Standardmäßig Benachrichtigungen aktiviert firstUsed: firstUsedDate, // Datum des ersten Gebrauchs favoriteGenre: "Nicht festgelegt" // Standardwert für das Lieblingsgenre }; fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); // Bot-Start-Nachricht const welcomeMessage = `👋 Willkommen ${username}! Dein Zugang zum Bot wurde erfolgreich eingerichtet. ✅ Um die verfügbaren Befehle anzuzeigen, tippe 👉 /help. 🔔 Hinweis: Benachrichtigungen über neue Filme sind standardmäßig aktiviert. Um sie zu deaktivieren, tippe 👉 /notification_off. 👤 Möchtest du dein Profil sehen? Tippe 👉 /profil.`; // Inline-Button zu einer Webadresse, basierend auf der Umgebungsvariable const options = { reply_markup: { inline_keyboard: [ [ { text: 'zur Web Oberfläche', url: process.env.PANEL_LINK // Verwendung der PANEL_LINK-Umgebungsvariable } ] ] } }; bot.sendMessage(chatId, welcomeMessage, options); // /start-Befehl protokollieren logMessage(`Received /start command from chatId ${chatId} (userId ${userId}, username ${username})`); }); // /notification-on-Befehl verarbeiten bot.onText(/\/notification_on/, (msg) => { const chatId = msg.chat.id; // Benutzerdaten in user.yml laden let users = yaml.load(USER_YML_PATH); if (users[chatId]) { users[chatId].notifications = true; fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); bot.sendMessage(chatId, 'Benachrichtigungen wurden aktiviert.'); } else { bot.sendMessage(chatId, 'Du musst den Bot zuerst mit /start aktivieren.'); } }); // /notification-off-Befehl verarbeiten bot.onText(/\/notification_off/, (msg) => { const chatId = msg.chat.id; // Benutzerdaten in user.yml laden let users = yaml.load(USER_YML_PATH); if (users[chatId]) { users[chatId].notifications = false; fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); bot.sendMessage(chatId, 'Benachrichtigungen wurden deaktiviert.'); } else { bot.sendMessage(chatId, 'Du musst den Bot zuerst mit /start aktivieren.'); } }); // /update Befehl bot.onText(/\/update/, async (msg) => { const chatId = msg.chat.id; // Überprüfen, ob der Benutzer berechtigt ist if (chatId.toString() !== process.env.DEV_CHAT_ID) { bot.sendMessage(chatId, '❌ Du hast keine Berechtigung, diesen Befehl auszuführen.'); return; } try { // Benutzer.yml laden const userYmlData = yaml.load(USER_YML_PATH); // Aktuelles Datum im Format 'YYYY-MM-DD' const currentDate = dayjs().format('YYYY-MM-DD'); // Durchlaufe alle Benutzer und aktualisiere das Datum, falls es fehlt for (const userId in userYmlData) { if (!userYmlData[userId].firstUsed) { userYmlData[userId].firstUsed = currentDate; // Setze das aktuelle Datum } // Überprüfen, ob das Feld favoriteGenre existiert if (!userYmlData[userId].favoriteGenre) { userYmlData[userId].favoriteGenre = "Nicht festgelegt"; // Setze Standardwert, wenn das Genre fehlt } } // Benutzer.yml speichern fs.writeFileSync(USER_YML_PATH, yaml.stringify(userYmlData, 4)); bot.sendMessage(chatId, '✅ Die user.yml wurde erfolgreich aktualisiert.'); } catch (error) { logError(`Fehler beim Aktualisieren der user.yml: ${error.message}`); bot.sendMessage(chatId, `❌ Fehler beim Aktualisieren der user.yml: ${error.message}`); } }); let lastAddedMovieTime = null; // Variable zum Speichern des Zeitpunkts des letzten Films // Funktion zum Abrufen der letzten hinzugefügten Filme async function fetchLatestMovies() { try { const response = await axios.get(`${PLEX_DOMAIN}/library/recentlyAdded?X-Plex-Token=${PLEX_TOKEN}`); const movies = response.data.MediaContainer.Metadata; if (movies && movies.length > 0) { return movies; } return []; } catch (error) { console.error(`Error fetching latest movies: ${error.message}`); return []; } } // Funktion zum Überprüfen und Benachrichtigen über neue Filme async function checkForNewMovies() { try { const movies = await fetchLatest10Movies(); // Verwende fetchLatest10Movies, um die letzten 10 Filme zu erhalten if (movies.length > 0) { const latestMovie = movies[0]; if (!lastAddedMovieTime || dayjs.unix(latestMovie.addedAt).isAfter(lastAddedMovieTime)) { // Neuer Film wurde hinzugefügt und ist neuer als der zuletzt gesendete Film lastAddedMovieTime = dayjs.unix(latestMovie.addedAt); // Update the last added movie time const movieTitle = latestMovie.title || 'Unbekannt'; const movieSummary = latestMovie.summary || 'Keine Zusammenfassung verfügbar'; const movieThumb = latestMovie.thumb ? `${PLEX_DOMAIN}${latestMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : ''; // Kürze die Zusammenfassung, wenn sie zu lang ist const maxSummaryLength = 200; // Maximale Länge der Zusammenfassung const truncatedSummary = movieSummary.length > maxSummaryLength ? `${movieSummary.substring(0, maxSummaryLength)}...` : movieSummary; const message = `Ein neuer Film wurde hinzugefügt:\n\nTitel: ${movieTitle}\n\nZusammenfassung:\n${truncatedSummary}`; const users = yaml.load(USER_YML_PATH); const sendMessages = Object.keys(users).map(chatId => { if (users[chatId].notifications) { if (movieThumb) { // Wenn ein Bild vorhanden ist, sende es mit der Nachricht return bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { console.error(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { // Wenn kein Bild vorhanden ist, sende nur die Nachricht return bot.sendMessage(chatId, message).catch(error => { console.error(`Error sending message to chatId ${chatId}: ${error.message}`); }); } } }).filter(promise => promise !== undefined); await Promise.all(sendMessages); console.log(`Sent new movie message to all users`); // Speichern der letzten gesendeten Zeit in einer Datei, um Wiederholungen zu vermeiden fs.writeFileSync('lastAddedMovieTime.json', JSON.stringify({ time: lastAddedMovieTime.unix() })); } } } catch (error) { logError(`Error checking for new movies: ${error.message}`); } } // Lade den letzten gesendeten Film-Zeitstempel beim Start des Bots if (fs.existsSync('lastAddedMovieTime.json')) { const lastAddedMovieData = JSON.parse(fs.readFileSync('lastAddedMovieTime.json')); lastAddedMovieTime = dayjs.unix(lastAddedMovieData.time); } // Plane die kontinuierliche Überprüfung alle 1 Minute schedule.scheduleJob('*/1 * * * *', checkForNewMovies); // Initiale Überprüfung beim Start checkForNewMovies(); // /latestmovie-Befehl verarbeiten bot.onText(/\/latestmovie/, async (msg) => { const chatId = msg.chat.id; try { const movies = await fetchAllMovies(); const sortedMovies = movies .filter(movie => movie.addedAt) .sort((a, b) => b.addedAt - a.addedAt); const latestMovie = sortedMovies[0]; if (latestMovie) { const movieTitle = latestMovie.title || 'Unbekannt'; const movieSummary = latestMovie.summary || 'Keine Zusammenfassung verfügbar'; const addedAtDate = new Date((latestMovie.addedAt || 0) * 1000).toLocaleString(); // Konvertierung von Unix-Zeitstempel in lesbares Datum const movieThumb = latestMovie.thumb ? `${PLEX_DOMAIN}${latestMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : ''; // Trailer-URL abrufen const trailerUrl = await fetchTrailerUrl(latestMovie); // Funktion zum Abrufen des Trailer-Links const message = `Der zuletzt hinzugefügte Film ist:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}\n\nHinzugefügt am: ${addedAtDate}`; // Erstelle den Inline-Button für den Trailer const replyMarkup = { inline_keyboard: [ [{ text: "Trailer ansehen", url: trailerUrl || '#' }] // Fallback-URL falls kein Trailer vorhanden ] }; // Bild anzeigen, wenn vorhanden if (movieThumb) { bot.sendPhoto(chatId, movieThumb, { caption: message, reply_markup: replyMarkup }).catch(error => { logError(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, message, { reply_markup: replyMarkup }).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); } logMessage(`Sent latest movie info to chatId ${chatId}`); } else { bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => { logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`); }); logMessage(`No movies found for chatId ${chatId}`); } } catch (error) { if (error.response) { bot.sendMessage(chatId, `Fehler beim Abrufen der neuesten Filme. Statuscode: ${error.response.status}`).catch(err => { logError(`Error sending error message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching latest movie: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { bot.sendMessage(chatId, 'Fehler beim Abrufen der neuesten Filme. Keine Antwort vom Server.').catch(err => { logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching latest movie: No response from server`); } else { logError(`Error fetching latest movie: ${error.message}`); } } }); // /info-Befehl verarbeiten bot.onText(/\/info/, async (msg) => { const chatId = msg.chat.id; const messageId = msg.message_id; const plexDomain = PLEX_DOMAIN; try { // Überprüfe den Serverstatus const serverStatus = await checkServerStatus(); const { movieCount, showCount, episodeCount, seasonCount, topGenre, totalSize, oldestMovie, newestMovie } = await fetchAllMedia(); // Serverstatus Text const serverStatusText = serverStatus ? '🟢 Server Status: Online' : '🔴 Server Status: Offline'; const message = `${serverStatusText}\n\n` + `*In der Bibliothek befinden sich derzeit:*\n\n` + `📽️ Filme: ${movieCount}\n\n` + `📺 Serien: ${showCount}\n\n` + `🎞️ Episoden: ${episodeCount}\n\n` + `📚 Staffeln: ${seasonCount}\n\n\n` + `📊 Top-Genre: ${topGenre}\n\n` + `💾 Gesamtgröße-Filme: ${totalSize}\n\n` + `💾 Gesamtgröße-Serien: 1.70TB\n\n\n` + `⏳ Ältester Film: ${oldestMovie.title} (${oldestMovie.year})\n\n` + `🆕 Neuester Film: ${newestMovie.title} (${newestMovie.year})\n\n\n` + `© 2024 M_Viper`; const options = { reply_markup: JSON.stringify({ inline_keyboard: [ [{ text: 'Zu Plex gehen', url: plexDomain }] ] }) }; await bot.sendMessage(chatId, message, options).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); // Ursprüngliche Nachricht löschen (den /info-Befehl) await bot.deleteMessage(chatId, messageId).catch(error => { logError(`Error deleting message from chatId ${chatId}: ${error.message}`); }); logMessage(`Sent detailed media info, copyright, and Plex button to chatId ${chatId}`); } catch (error) { logError(`Error fetching media info: ${error.message}`); await bot.sendMessage(chatId, 'Fehler beim Abrufen der Medieninformationen.').catch(err => { logError(`Error sending media info error message to chatId ${chatId}: ${err.message}`); }); } }); // Funktion zum Überprüfen des Serverstatus async function checkServerStatus() { try { const response = await axios.get(`${PLEX_DOMAIN}/status`, { headers: { 'X-Plex-Token': PLEX_TOKEN } }); return response.status === 200; // Server ist online, wenn Status 200 zurückgegeben wird } catch (error) { console.error(`Server is offline or unreachable: ${error.message}`); return false; // Server ist offline oder nicht erreichbar } } // Funktion zum Abrufen von Plex-Daten async function fetchPlexData(url) { try { const response = await axios.get(url, { headers: { 'X-Plex-Token': PLEX_TOKEN } }); return response.data; } catch (error) { logError(`Error fetching Plex data from ${url}: ${error.message}`); throw error; } } // Funktion zum Abrufen der erweiterten Medieninformationen async function fetchAllMedia() { try { const movies = await fetchAllMovies(); const shows = await fetchAllShows(); const episodeCount = shows.reduce((sum, show) => sum + (show.leafCount || 0), 0); const seasonCount = shows.reduce((sum, show) => sum + (show.childCount || 0), 0); const topGenre = findTopGenre(movies.concat(shows)); const totalSize = await calculateTotalSize(movies.concat(shows)); const oldestMovie = findOldestMedia(movies); const newestMovie = findNewestMedia(movies); return { movieCount: movies.length, showCount: shows.length, episodeCount: episodeCount, seasonCount: seasonCount, topGenre: topGenre, totalSize: totalSize, oldestMovie: oldestMovie, newestMovie: newestMovie }; } catch (error) { logError(`Error fetching all media: ${error.message}`); throw error; } } // Funktion zur Ermittlung des am häufigsten vorkommenden Genres function findTopGenre(mediaArray) { const genreCount = {}; mediaArray.forEach(media => { if (media.Genre) { media.Genre.forEach(genre => { genreCount[genre.tag] = (genreCount[genre.tag] || 0) + 1; }); } }); return Object.keys(genreCount).reduce((a, b) => genreCount[a] > genreCount[b] ? a : b, ''); } // Funktion zur Berechnung der Gesamtgröße der Mediendateien async function calculateTotalSize(mediaArray) { let totalSizeBytes = 0; for (const media of mediaArray) { if (media.Media && media.Media.length > 0) { media.Media.forEach(mediaItem => { if (mediaItem.Part && mediaItem.Part.length > 0) { mediaItem.Part.forEach(part => { if (part.size) { const sizeInBytes = parseInt(part.size, 10); totalSizeBytes += sizeInBytes; } }); } }); } } // Log total size in bytes for debugging console.log(`Total size in bytes: ${totalSizeBytes}`); // Convert bytes to terabytes (TB) and gigabytes (GB) const totalSizeTB = totalSizeBytes / (1024 * 1024 * 1024 * 1024); const totalSizeGB = totalSizeBytes / (1024 * 1024 * 1024); // Log sizes in GB and TB console.log(`Total size in TB: ${totalSizeTB}`); console.log(`Total size in GB: ${totalSizeGB}`); // Determine the appropriate size unit to display if (totalSizeTB >= 1) { return `${totalSizeTB.toFixed(2)} TB`; } else { return `${totalSizeGB.toFixed(2)} GB`; } } // Funktion zum Finden des ältesten Mediums function findOldestMedia(mediaArray) { return mediaArray.reduce((oldest, media) => { if (!oldest || (media.year && media.year < oldest.year)) { return media; } return oldest; }, null); } // Funktion zum Finden des neuesten Mediums function findNewestMedia(mediaArray) { return mediaArray.reduce((newest, media) => { if (!newest || (media.year && media.year > newest.year)) { return media; } return newest; }, null); } // Funktion zum Abrufen aller Filme async function fetchAllMovies() { try { const sectionsData = await fetchPlexData(`${PLEX_DOMAIN}/library/sections?X-Plex-Token=${PLEX_TOKEN}`); const sections = sectionsData.MediaContainer.Directory; let movies = []; for (const section of sections) { const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`; const sectionData = await fetchPlexData(sectionUrl); if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) { const metadata = sectionData.MediaContainer.Metadata; movies = movies.concat(metadata.filter(media => media.type === 'movie')); } } return movies; } catch (error) { logError(`Error fetching all movies: ${error.message}`); throw error; } } // Funktion zum Abrufen aller Serien async function fetchAllShows() { try { const sectionsData = await fetchPlexData(`${PLEX_DOMAIN}/library/sections?X-Plex-Token=${PLEX_TOKEN}`); const sections = sectionsData.MediaContainer.Directory; let shows = []; for (const section of sections) { const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`; const sectionData = await fetchPlexData(sectionUrl); if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) { const metadata = sectionData.MediaContainer.Metadata; shows = shows.concat(metadata.filter(media => media.type === 'show')); } } return shows; } catch (error) { logError(`Error fetching all shows: ${error.message}`); throw error; } } // Fehlerprotokollierung function logError(message) { fs.appendFile(path.join(__dirname, 'Log', 'error.log'), `${new Date().toISOString()} - ${message}\n`, err => { if (err) { console.error(`Failed to log error: ${err.message}`); } }); } // Erfolgsprotokollierung function logMessage(message) { fs.appendFile(path.join(__dirname, 'Log', 'message.log'), `${new Date().toISOString()} - ${message}\n`, err => { if (err) { console.error(`Failed to log message: ${err.message}`); } }); } // Hilfsfunktion zum Abrufen von Plex-Daten async function fetchPlexData(url) { try { const response = await axios.get(url); return response.data; } catch (error) { logError(`Error fetching Plex data: ${error.message}`); throw error; } } const usersNightMode = {}; // Temporärer Speicher für Nachtmodus // Funktion zum Laden der Benutzerdaten aus der user.yml function loadUserData() { if (!fs.existsSync(USER_YML_PATH)) { fs.writeFileSync(USER_YML_PATH, yaml.stringify({})); } return yaml.load(USER_YML_PATH); } // Funktion zum Speichern der Benutzerdaten in die user.yml function saveUserData(userData) { fs.writeFileSync(USER_YML_PATH, yaml.stringify(userData, 4)); } // Funktion zum Anheften der Nachtmodus-Nachricht function pinNightModeMessage(chatId, messageId) { bot.pinChatMessage(chatId, messageId).catch(err => console.error('Fehler beim Anheften der Nachricht:', err)); } // Funktion zum Entfernen der angehefteten Nachricht function unpinNightModeMessage(chatId) { bot.unpinChatMessage(chatId).catch(err => console.error('Fehler beim Entfernen der angehefteten Nachricht:', err)); } // /night Befehl bot.onText(/\/night/, (msg) => { const chatId = msg.chat.id; const userData = loadUserData(); // Lade die Benutzerdaten const userId = chatId.toString(); // Standard-Buttons const buttons = []; // Überprüfen, ob der Benutzer bereits Nachtmodi hat if (!userData[userId] || !userData[userId].nightModes || userData[userId].nightModes.length === 0) { buttons.push([{ text: '🌙 Nachtmodus eingeben', callback_data: 'input_night_mode' }]); } else { buttons.push( [ { text: '🛠️ Nachtmodus bearbeiten', callback_data: 'edit_night_mode' }, { text: '➕ Weiteren Nachtmodus eingeben', callback_data: 'add_night_mode' } ], [{ text: '🗑️ Nachtmodus löschen', callback_data: 'delete_night_mode' }] ); } // Zeige die Inline-Buttons an bot.sendMessage(chatId, 'Wählen Sie eine Option:', { reply_markup: { inline_keyboard: buttons, }, }); }); // Callback-Query-Handler für die Inline-Buttons bot.on('callback_query', (query) => { const chatId = query.message.chat.id; const userId = chatId.toString(); const userData = loadUserData(); // Lade die Benutzerdaten if (query.data === 'input_night_mode') { // Eingabe eines neuen Nachtmodus bot.sendMessage(chatId, 'Bitte geben Sie die Startzeit des Nachtmodus im Format HH:mm ein (z.B. 22:00):'); bot.once('message', (msg) => { const startTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!startTime || !/^\d{2}:\d{2}$/.test(startTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } bot.sendMessage(chatId, 'Bitte geben Sie die Endzeit des Nachtmodus im Format HH:mm ein (z.B. 06:00):'); bot.once('message', (msg) => { const endTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!endTime || !/^\d{2}:\d{2}$/.test(endTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } // Speichere die Nachtmodus-Daten userData[userId] = userData[userId] || {}; userData[userId].nightModes = userData[userId].nightModes || []; userData[userId].nightModes.push({ startTime, endTime }); saveUserData(userData); // Speichere die Daten in die yml-Datei bot.sendMessage(chatId, `🌓 Nachtmodus geplant von ${startTime} bis ${endTime}.`); }); }); } else if (query.data === 'edit_night_mode') { // Bearbeiten eines bestehenden Nachtmodus if (userData[userId] && userData[userId].nightModes && userData[userId].nightModes.length > 0) { const nightModes = userData[userId].nightModes; let nightModesText = 'Aktuelle Nachtmodi:\n'; nightModes.forEach((mode, index) => { nightModesText += `${index + 1}: ${mode.startTime} bis ${mode.endTime}\n`; }); nightModesText += 'Geben Sie die Nummer des Nachtmodus ein, den Sie bearbeiten möchten:'; bot.sendMessage(chatId, nightModesText); bot.once('message', (msg) => { const modeIndex = parseInt(msg.text) - 1; if (isNaN(modeIndex) || modeIndex < 0 || modeIndex >= nightModes.length) { return bot.sendMessage(chatId, 'Ungültige Auswahl.'); } bot.sendMessage(chatId, 'Bitte geben Sie die neue Startzeit im Format HH:mm ein (z.B. 22:00):'); bot.once('message', (msg) => { const newStartTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!newStartTime || !/^\d{2}:\d{2}$/.test(newStartTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } bot.sendMessage(chatId, 'Bitte geben Sie die neue Endzeit im Format HH:mm ein (z.B. 06:00):'); bot.once('message', (msg) => { const newEndTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!newEndTime || !/^\d{2}:\d{2}$/.test(newEndTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } // Aktualisiere den Nachtmodus userData[userId].nightModes[modeIndex] = { startTime: newStartTime, endTime: newEndTime }; saveUserData(userData); // Speichere die Änderungen bot.sendMessage(chatId, `🌓 Nachtmodus aktualisiert auf ${newStartTime} bis ${newEndTime}.`); }); }); }); } else { bot.sendMessage(chatId, 'Es sind keine Nachtmodi zum Bearbeiten vorhanden.'); } } else if (query.data === 'add_night_mode') { // Eingabe eines weiteren Nachtmodus bot.sendMessage(chatId, 'Bitte geben Sie die Startzeit des Nachtmodus im Format HH:mm ein (z.B. 22:00):'); bot.once('message', (msg) => { const startTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!startTime || !/^\d{2}:\d{2}$/.test(startTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } bot.sendMessage(chatId, 'Bitte geben Sie die Endzeit des Nachtmodus im Format HH:mm ein (z.B. 06:00):'); bot.once('message', (msg) => { const endTime = msg.text; // Sicherstellen, dass msg.text existiert und gültig ist if (!endTime || !/^\d{2}:\d{2}$/.test(endTime)) { return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.'); } // Speichere den neuen Nachtmodus userData[userId].nightModes.push({ startTime, endTime }); saveUserData(userData); // Speichere die Daten in die yml-Datei bot.sendMessage(chatId, `🌓 Weiterer Nachtmodus geplant von ${startTime} bis ${endTime}.`); }); }); } else if (query.data === 'delete_night_mode') { // Zeige Liste der Nachtmodi zum Löschen if (userData[userId] && userData[userId].nightModes && userData[userId].nightModes.length > 0) { const nightModes = userData[userId].nightModes; let nightModesText = '🔄 **Bitte wählen Sie einen Nachtmodus zum Löschen:**\n\n'; nightModes.forEach((mode, index) => { nightModesText += `🕒 **${index + 1}:** ${mode.startTime} bis ${mode.endTime}\n`; }); nightModesText += '\n🗑️ *Geben Sie die Nummer des Nachtmodus ein, den Sie löschen möchten:*'; bot.sendMessage(chatId, nightModesText); bot.once('message', (msg) => { const modeIndex = parseInt(msg.text) - 1; if (isNaN(modeIndex) || modeIndex < 0 || modeIndex >= nightModes.length) { return bot.sendMessage(chatId, 'Ungültige Auswahl.'); } // Lösche den ausgewählten Nachtmodus userData[userId].nightModes.splice(modeIndex, 1); // Lösche den Nachtmodus an der gegebenen Indexposition saveUserData(userData); // Speichere die Änderungen bot.sendMessage(chatId, '🗑️ Der ausgewählte Nachtmodus wurde gelöscht.'); }); } else { bot.sendMessage(chatId, 'Es gibt keinen Nachtmodus, der gelöscht werden kann.'); } } }); // Funktion zur Überprüfung, ob der Benutzer im Nachtmodus ist function isUserInNightMode(chatId) { const userData = loadUserData(); const userId = chatId.toString(); const userNightModes = userData[userId] && userData[userId].nightModes; if (!userNightModes || userNightModes.length === 0) return false; const now = moment(); return userNightModes.some(userNightMode => { const start = moment(userNightMode.startTime, 'HH:mm'); const end = moment(userNightMode.endTime, 'HH:mm'); if (end.isBefore(start)) { return now.isAfter(start) || now.isBefore(end); // Nachtmodus über Mitternacht } else { return now.isBetween(start, end); // Normaler Nachtmodus } }); } // Funktion zur automatischen Aktivierung des Nachtmodus function activateNightMode() { const userData = loadUserData(); for (const userId in userData) { const userNightModes = userData[userId] && userData[userId].nightModes; if (!userNightModes) continue; const now = moment(); userNightModes.forEach(userNightMode => { const start = moment(userNightMode.startTime, 'HH:mm'); const end = moment(userNightMode.endTime, 'HH:mm'); // Nachtmodus über Mitternacht if (end.isBefore(start)) { if (now.isAfter(start) || now.isBefore(end)) { handleNightModeActivation(userId, userData); } else { handleNightModeDeactivation(userId, userData); } } // Normaler Nachtmodus else { if (now.isBetween(start, end)) { handleNightModeActivation(userId, userData); } else { handleNightModeDeactivation(userId, userData); } } }); } } // Funktion zur Aktivierung des Nachtmodus function handleNightModeActivation(userId, userData) { if (userData[userId].notifications !== false) { userData[userId].originalNotifications = userData[userId].notifications; userData[userId].notifications = false; saveUserData(userData); bot.sendMessage(userId, `🌓 Der Nachtmodus hat begonnen. Deine Benachrichtigungen wurden auf Stumm geschaltet.`) .then((msg) => { // Pin die Nachricht pinNightModeMessage(userId, msg.message_id); }); console.log(`Nachtmodus für Benutzer ${userId} aktiviert.`); } } // Funktion zur Deaktivierung des Nachtmodus function handleNightModeDeactivation(userId, userData) { if (userData[userId].notifications === false) { userData[userId].notifications = userData[userId].originalNotifications; delete userData[userId].originalNotifications; saveUserData(userData); bot.sendMessage(userId, `🌓 Der Nachtmodus endet, die Benachrichtigungen sind jetzt wieder aktiv.`) .then(() => { // Unpin die Nachricht unpinNightModeMessage(userId); }); console.log(`Nachtmodus für Benutzer ${userId} deaktiviert.`); } } // Automatische Nachtmodus-Aktivierung überwachen setInterval(() => { activateNightMode(); }, 60 * 1000); // Überprüfung alle 60 Sekunden // Funktion zum Erstellen der Datei 'w_offen.json' im Hauptverzeichnis, falls sie noch nicht existiert function ensureWOffenFileExists() { const filePath = path.join(__dirname, 'w_offen.json'); // Hauptverzeichnis if (!fs.existsSync(filePath)) { // Datei erstellen und leeres Array als Inhalt speichern fs.writeFileSync(filePath, JSON.stringify([], null, 2), (err) => { if (err) { console.error(`Fehler beim Erstellen der Datei 'w_offen.json': ${err}`); } }); console.log(`Die Datei 'w_offen.json' wurde im Hauptverzeichnis erstellt.`); } else { console.log(`Die Datei 'w_offen.json' existiert bereits.`); } } // Funktion zum Erstellen des Ordners 'wunsch', falls dieser noch nicht existiert function ensureWunschFolderExists() { const folderPath = path.join(__dirname, 'wunsch'); if (!fs.existsSync(folderPath)) { fs.mkdirSync(folderPath, { recursive: true }); console.log(`Ordner 'wunsch' wurde erstellt.`); } } // Funktion zum Speichern eines Wunsches in der Datei 'wishes_.json' function saveWish(chatId, wish, type, fulfilled = false) { ensureWunschFolderExists(); // Ordner sicherstellen const filePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`); const wishData = { type, wish, fulfilled }; fs.readFile(filePath, (err, data) => { let wishes = []; if (!err) { wishes = JSON.parse(data); // Vorhandene Wünsche lesen } wishes.push(wishData); // Neuen Wunsch hinzufügen fs.writeFile(filePath, JSON.stringify(wishes, null, 2), (err) => { if (err) { console.error(`Fehler beim Speichern des Wunsches: ${err}`); } else { console.log(`Wunsch von ${chatId} erfolgreich gespeichert.`); } }); }); } // Funktion zum Erstellen des Inline-Keyboards für die Auswahl von Film oder Serie function getTypeKeyboard() { return { reply_markup: JSON.stringify({ inline_keyboard: [ [{ text: '🎬 Film', callback_data: 'type_film' }], [{ text: '📺 Serie', callback_data: 'type_serie' }] ] }) }; } // Funktion zum Senden des Wunsches an zwei Benutzer mit Inline-Buttons für 'Erfüllt' und 'Nicht erfüllt' async function sendWish(wish, type, chatId) { const message = `✨ Achtung! ✨\n\nEin neuer Wunsch ist eingegangen:\n\n🔹 Typ: ${type}\n\n🔹 Titel:\n${wish}`; // Inline-Keyboard mit den zwei Buttons const inlineKeyboard = { reply_markup: JSON.stringify({ inline_keyboard: [ [ { text: '✅ Wunsch erfüllt', callback_data: `wish_fulfilled_${chatId}` }, { text: '❌ Wunsch nicht erfüllt', callback_data: `wish_not_fulfilled_${chatId}` } ] ] }) }; try { const [msg1, msg2] = await Promise.all([ bot.sendMessage(USER1_ID, message, inlineKeyboard), bot.sendMessage(USER2_ID, message, inlineKeyboard), ]); console.log(`Wunsch von Typ ${type} wurde an ${USER1_ID} und ${USER2_ID} gesendet.`); return { messageId1: msg1.message_id, messageId2: msg2.message_id }; // Rückgabe der Nachricht IDs } catch (error) { console.error(`Fehler beim Senden des Wunsches: ${error.message}`); } // Speichern des Wunsches in der Datei saveWish(chatId, wish, type); } // Verarbeite Callback Queries (für die Inline-Buttons) bot.on('callback_query', async (query) => { const chatId = query.message.chat.id; const data = query.data; if (data.startsWith('type_')) { // Benutzer hat den Typ ausgewählt (Film oder Serie) const type = data === 'type_film' ? 'Film' : 'Serie'; await bot.sendMessage(chatId, `Du hast ${type} ausgewählt. Bitte gib den Titel des ${type} ein.`) .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); // Lösche die Nachricht der Kategorieauswahl await bot.deleteMessage(chatId, query.message.message_id).catch(error => console.error(`Fehler beim Löschen der Nachricht: ${error.message}`)); userStates[chatId] = { type, waitingForWish: true }; // Setze den Status auf "wartend auf Wunsch" } if (data.startsWith('wish_fulfilled_')) { const userId = data.split('_')[2]; // Der Ersteller des Wunsches const messageText = query.message.text; // Der Text des Wunsches const wishTitle = messageText.split('Titel:\n')[1].trim(); // Titel korrekt extrahieren await bot.sendMessage(userId, '🎉 Dein Wunsch wurde erfüllt!') .catch(error => console.error(`Fehler beim Senden der Nachricht: ${error.message}`)); // Wunsch in der Datei 'wishes_.json' als erfüllt markieren const filePath = path.join(__dirname, 'wunsch', `wishes_${userId}.json`); fs.readFile(filePath, (err, data) => { if (!err) { let wishes = JSON.parse(data); // Suche den spezifischen Wunsch und markiere ihn als erfüllt wishes = wishes.map(wish => { if (wish.wish === wishTitle) { return { ...wish, fulfilled: true }; // Nur den spezifischen Wunsch als erfüllt markieren } return wish; // Alle anderen Wünsche unverändert lassen }); fs.writeFile(filePath, JSON.stringify(wishes, null, 2), (err) => { if (err) { console.error(`Fehler beim Aktualisieren des Wunsches: ${err}`); } }); } }); // Lösche die Wunschnachricht await bot.deleteMessage(chatId, query.message.message_id); } if (data.startsWith('wish_not_fulfilled_')) { const userId = query.message.chat.id; // Nutze die Chat-ID des Nachrichtenautors await bot.sendMessage(userId, '😢 Dein Wunsch wurde leider nicht erfüllt.') .catch(error => console.error(`Fehler beim Senden der Nachricht: ${error.message}`)); // Wunsch in der Datei 'w_offen.json' speichern const filePath = path.join(__dirname, 'w_offen.json'); const wishDetails = { userId: userId, // Chat-ID als userId speichern message: query.message.text, // Nachricht als Wunsch speichern }; fs.readFile(filePath, (err, data) => { let openWishes = []; if (!err && data.length > 0) { openWishes = JSON.parse(data); // Vorhandene offene Wünsche lesen } openWishes.push(wishDetails); // Neuen offenen Wunsch hinzufügen fs.writeFile(filePath, JSON.stringify(openWishes, null, 2), (err) => { if (err) { console.error(`Fehler beim Speichern des offenen Wunsches: ${err}`); } else { console.log('Der nicht erfüllte Wunsch wurde in der Datei "w_offen.json" gespeichert.'); } }); }); // Lösche die Wunschnachricht await bot.deleteMessage(chatId, query.message.message_id); } bot.answerCallbackQuery(query.id).catch(error => { console.error(`Fehler bei der Callback-Abfrage: ${error.message}`); }); }); // Verarbeite eingehende Nachrichten bot.on('message', async (msg) => { const chatId = msg.chat.id; const text = msg.text || ''; // Sicherstellen, dass text definiert ist if (userStates[chatId] && userStates[chatId].waitingForWish) { const wish = text.trim(); if (wish) { const type = userStates[chatId].type; await sendWish(wish, type, chatId); await bot.sendMessage(chatId, `✅ Dein ${type}-Wunsch wurde übermittelt.`) .catch(error => console.error(`Fehler bei der Bestätigungsnachricht: ${error.message}`)); userStates[chatId].waitingForWish = false; } else { await bot.sendMessage(chatId, `✍️ Bitte gib den Titel des ${userStates[chatId].type} ein.`) .catch(error => console.error(`Fehler bei der Wunsch-Nachricht: ${error.message}`)); } return; } if (text.startsWith('/wunsch')) { await bot.sendMessage(chatId, '🎬 Möchtest du einen Film oder eine Serie wünschen? Wähle bitte eine Option:', getTypeKeyboard()) .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); userStates[chatId] = { waitingForType: true }; } if (text.startsWith('/w_list')) { // Liste der Wünsche für den Benutzer anzeigen const filePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`); fs.readFile(filePath, (err, data) => { if (err) { bot.sendMessage(chatId, 'Es wurden keine Wünsche gefunden.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { const wishes = JSON.parse(data); let wishList = '📜 Deine Wünsche:\n\n'; wishes.forEach((wish, index) => { const statusEmoji = wish.fulfilled ? '🟢' : '🔴'; wishList += `${index + 1}. ${statusEmoji} ${wish.type}: ${wish.wish} \n\n`; }); bot.sendMessage(chatId, wishList) .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } }); // Objekt zur Verfolgung der Benutzer, die auf eine Eingabe warten let waitingForWishIndex = {}; // API-Endpunkt für offene Wünsche app.get('/api/wishes', (req, res) => { fs.readFile('w_offen.json', 'utf8', (err, data) => { if (err) { return res.status(500).json({ error: 'Fehler beim Lesen der Wünsche' }); } res.json(JSON.parse(data)); }); }); // Verarbeite die Auswahl des Inline-Buttons zum Markieren eines Wunsches als erfüllt bot.on('callback_query', (query) => { const chatId = query.message.chat.id; const data = query.data; if (data === 'mark_wish_fulfilled') { bot.sendMessage(chatId, 'Bitte gib die Nummer des Wunsches ein, den du als erfüllt markieren möchtest:') .then(() => { bot.once('message', (msg) => { const wishIndex = parseInt(msg.text.trim()) - 1; // Wunschindex basierend auf der eingegebenen Zahl // Lese die Datei 'w_offen.json' aus const filePath = path.join(__dirname, 'w_offen.json'); fs.readFile(filePath, (err, data) => { if (!err) { let openWishes = JSON.parse(data); if (wishIndex >= 0 && wishIndex < openWishes.length) { const fulfilledWish = openWishes[wishIndex]; // Log zur Überprüfung console.log(`Markiere Wunsch: ${fulfilledWish.message} von User ID: ${fulfilledWish.userId}`); // Wunsch als erfüllt markieren in 'wishes_.json' const userWishFile = path.join(__dirname, 'wunsch', `wishes_${fulfilledWish.userId}.json`); fs.readFile(userWishFile, (err, wishData) => { if (!err) { let userWishes = JSON.parse(wishData); // Suche den spezifischen Wunsch und markiere ihn als erfüllt let wishFound = false; // Extrahiere den Titel aus der Wunschnachricht const wishMatch = fulfilledWish.message.match(/🔹 Titel:\s*(.*)/); const extractedTitle = wishMatch ? wishMatch[1].trim() : ''; userWishes.forEach(wish => { // Entferne Leerzeichen und Zeilenumbrüche vor dem Vergleich const normalizedFileWishText = wish.wish.trim().toLowerCase(); console.log(`Wunschtext aus der Datei: "${normalizedFileWishText}"`); console.log(`Wunschtext aus der Eingabe: "${extractedTitle.toLowerCase()}"`); if (normalizedFileWishText === extractedTitle.toLowerCase()) { wishFound = true; wish.fulfilled = true; // Setze fulfilled auf true console.log(`Wunsch "${wish.wish}" wurde erfolgreich auf 'fulfilled: true' gesetzt.`); } }); if (!wishFound) { console.log(`Wunsch "${fulfilledWish.message}" nicht gefunden.`); bot.sendMessage(chatId, 'Wunsch konnte nicht gefunden werden.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { // Schreibe die aktualisierte Wunschliste zurück in die Datei fs.writeFile(userWishFile, JSON.stringify(userWishes, null, 2), (err) => { if (err) { console.error(`Fehler beim Aktualisieren des Wunsches: ${err}`); bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Wunsches.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { // Entferne den Wunsch aus 'w_offen.json' openWishes.splice(wishIndex, 1); fs.writeFile(filePath, JSON.stringify(openWishes, null, 2), (err) => { if (err) { console.error(`Fehler beim Aktualisieren von 'w_offen.json': ${err}`); bot.sendMessage(chatId, 'Fehler beim Aktualisieren der offenen Wünsche.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } else { bot.sendMessage(chatId, `Der Wunsch von User ID ${fulfilledWish.userId} wurde als erfüllt markiert.`) .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } }); } } else { console.error(`Fehler beim Lesen der Datei ${userWishFile}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Lesen der Benutzerdaten.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); } else { bot.sendMessage(chatId, 'Ungültige Wunschnummer.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } } else { console.error(`Fehler beim Lesen der Datei ${filePath}: ${err}`); bot.sendMessage(chatId, 'Fehler beim Lesen der offenen Wünsche.') .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } }); }); }) .catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`)); } bot.answerCallbackQuery(query.id).catch(error => { console.error(`Fehler bei der Callback-Abfrage: ${error.message}`); }); }); // /zufall-Befehl verarbeiten bot.onText(/\/zufall/, async (msg) => { const chatId = msg.chat.id; try { const randomMovie = await fetchRandomMovie(); if (randomMovie) { const movieTitle = randomMovie.title || 'Unbekannt'; const movieSummary = randomMovie.summary || 'Keine Zusammenfassung verfügbar'; const movieThumb = randomMovie.thumb ? `${PLEX_DOMAIN}${randomMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : ''; const message = `Hier ist ein zufälliger Film:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`; // YouTube Trailer Link erstellen const youtubeLink = `https://www.youtube.com/results?search_query=${encodeURIComponent(movieTitle + ' trailer')}`; // Inline-Button für den Trailer hinzufügen const options = { reply_markup: { inline_keyboard: [ [ { text: "Trailer ansehen", url: youtubeLink, } ] ] } }; // Bild anzeigen, wenn vorhanden if (movieThumb) { bot.sendPhoto(chatId, movieThumb, { caption: message, reply_markup: options.reply_markup }).catch(error => { logError(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, message, options).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); } logMessage(`Sent random movie info to chatId ${chatId}`); } else { bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => { logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`); }); logMessage(`No movies found for chatId ${chatId}`); } } catch (error) { if (error.response) { bot.sendMessage(chatId, `Fehler beim Abrufen eines zufälligen Films. Statuscode: ${error.response.status}`).catch(err => { logError(`Error sending error message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching random movie: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { bot.sendMessage(chatId, 'Fehler beim Abrufen eines zufälligen Films. Keine Antwort vom Server.').catch(err => { logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching random movie: No response from server`); } else { bot.sendMessage(chatId, 'Fehler beim Abrufen eines zufälligen Films. Unbekannter Fehler.').catch(err => { logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching random movie: ${error.message}`); } } }); // Speichern des Status der Benutzerinteraktionen const userStates = {}; // Einfache In-Memory-Datenstruktur // /search-Befehl verarbeiten bot.onText(/\/search/, (msg) => { const chatId = msg.chat.id; // Setze den Status auf "wartet auf Suchbegriff" userStates[chatId] = { waitingForQuery: true }; const message = 'Bitte gib den Suchbegriff für die Film-Suche ein.'; bot.sendMessage(chatId, message).catch(error => { logError(`Error sending search prompt to chatId ${chatId}: ${error.message}`); }); logMessage(`Prompted for search query from chatId ${chatId}`); }); // Eingehende Nachrichten verarbeiten bot.on('message', async (msg) => { const chatId = msg.chat.id; const text = msg.text; // Überprüfen, ob der Benutzer auf eine Suchabfrage wartet if (userStates[chatId] && userStates[chatId].waitingForQuery) { const query = text; // Suchbegriff erhalten try { const results = await searchMovies(query); if (results.length === 0) { await bot.sendMessage(chatId, 'Keine Filme gefunden, die deiner Suche entsprechen.').catch(error => { logError(`Error sending no results message to chatId ${chatId}: ${error.message}`); }); logMessage(`No search results found for chatId ${chatId} with query "${query}"`); } else { // Erstelle Nachrichten für jedes Ergebnis const messages = results.map(async (movie) => { const { title, summary, thumb } = movie; const movieThumbUrl = thumb ? `${process.env.PLEX_DOMAIN}${thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : ''; const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`; try { if (movieThumbUrl) { await bot.sendPhoto(chatId, movieThumbUrl, { caption: message }); logMessage(`Sent photo for movie "${title}" to chatId ${chatId}`); } else { await bot.sendMessage(chatId, message); logMessage(`Sent message for movie "${title}" to chatId ${chatId}`); } } catch (error) { logError(`Error sending message or photo to chatId ${chatId}: ${error.message}`); // Optional: Sende nur die Textnachricht, wenn das Bild nicht gesendet werden konnte await bot.sendMessage(chatId, message).catch(err => { logError(`Error sending fallback message to chatId ${chatId}: ${err.message}`); }); } }); // Führe alle Nachrichten-Operationen aus await Promise.all(messages); logMessage(`Sent search results for query "${query}" to chatId ${chatId}`); } } catch (error) { let errorMessage = 'Fehler beim Durchführen der Suche.'; if (error.response) { errorMessage += ` Statuscode: ${error.response.status}`; logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { errorMessage += ' Keine Antwort vom Server.'; logError(`Error searching movies: No response from server`); } else { errorMessage += ` Unbekannter Fehler: ${error.message}`; logError(`Error searching movies: ${error.message}`); } await bot.sendMessage(chatId, errorMessage).catch(err => { logError(`Error sending search error message to chatId ${chatId}: ${err.message}`); }); } // Benutzerstatus zurücksetzen userStates[chatId].waitingForQuery = false; } }); // Funktion zum Abrufen der Filme basierend auf der Suche async function searchMovies(query) { try { // Placeholder für die tatsächliche Implementierung // Diese Funktion sollte Filme basierend auf dem Suchbegriff abfragen und zurückgeben const movies = await fetchMoviesFromAPI(query); // Ersetze dies durch die echte Implementierung return movies; } catch (error) { logError(`Error searching movies: ${error.message}`); throw error; } } // Funktion zum Abrufen der Filme aus der API (placeholder) async function fetchMoviesFromAPI(query) { try { const url = `${process.env.PLEX_DOMAIN}/search?query=${encodeURIComponent(query)}&X-Plex-Token=${process.env.PLEX_TOKEN}`; const response = await axios.get(url); return response.data.MediaContainer.Metadata; // Oder wie auch immer die API antwortet } catch (error) { logError(`Error fetching movies from API: ${error.message}`); throw error; } } // Array, um empfohlene Filme zu speichern const recommendedMovies = []; let dailyMovieCache = {}; // Cache für den Film des Tages // Funktion zum Abrufen des täglichen Films basierend auf dem Datum async function fetchDailyRecommendation() { try { // Berechne das heutige Datum const today = moment().format('YYYY-MM-DD'); // Überprüfen, ob wir bereits einen Film für heute gespeichert haben if (dailyMovieCache[today]) { return dailyMovieCache[today]; } // Anfrage zur Mediathek, um alle Filme abzurufen const url = `${process.env.PLEX_DOMAIN}/library/sections/1/all?X-Plex-Token=${process.env.PLEX_TOKEN}`; const response = await axios.get(url); const data = response.data; if (data && data.MediaContainer && Array.isArray(data.MediaContainer.Metadata) && data.MediaContainer.Metadata.length > 0) { // Wähle einen zufälligen Film aus der Liste der Filme aus const movies = data.MediaContainer.Metadata; const randomIndex = Math.floor(Math.random() * movies.length); const selectedMovie = movies[randomIndex]; // Speichern des Films für heute im Cache dailyMovieCache[today] = selectedMovie; return selectedMovie; } else { // Protokolliere, wenn keine Filme gefunden wurden console.log('No movies found in API response or unexpected response format'); return null; } } catch (error) { logError(`Error fetching daily recommendation from API: ${error.message}`); throw error; } } // Funktion zum Abrufen des Trailers für einen bestimmten Film async function fetchTrailerUrl(filmTitle) { try { // YouTube API URL für die Suche const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=${encodeURIComponent(filmTitle + ' trailer')}&key=${process.env.YOUTUBE_API_KEY}`; const response = await axios.get(url); const videos = response.data.items; // Überprüfen, ob Videos gefunden wurden if (videos.length > 0) { const videoId = videos[0].id.videoId; // ID des ersten gefundenen Trailers return `https://www.youtube.com/watch?v=${videoId}`; // URL des Trailers } else { return null; // Kein Trailer gefunden } } catch (error) { logError(`Error fetching trailer URL: ${error.message}`); return null; // Fehler beim Abrufen des Trailers } } // Funktion zum Kürzen der Zusammenfassung function truncateSummary(summary, maxLength) { if (summary.length > maxLength) { return summary.slice(0, maxLength) + '...'; // Kürzen und "..." hinzufügen } return summary; } // Funktion zum Erstellen der Bildunterschrift function createCaption(title, summary) { // Initiale Bildunterschrift ohne Kürzung let caption = ` Hier ist der empfohlene Film des Tages: 🎬 Titel: ${title || 'Unbekannt'} 📝 Zusammenfassung: ${summary || 'Keine Zusammenfassung verfügbar'} `; // Überprüfen, ob die Bildunterschrift zu lang ist if (caption.length > MAX_CAPTION_LENGTH) { // Berechnen der maximalen Länge für die Zusammenfassung const maxSummaryLength = MAX_CAPTION_LENGTH - (caption.length - summary.length); // Kürzen der Zusammenfassung auf die berechnete Länge const truncatedSummary = truncateSummary(summary, maxSummaryLength); // Neu zusammenstellen der Bildunterschrift mit der gekürzten Zusammenfassung caption = ` Hier ist der empfohlene Film des Tages: 🎬 Titel: ${title || 'Unbekannt'} 📝 Zusammenfassung: ${truncatedSummary} `; } return caption; } // /empfehlung-Befehl verarbeiten bot.onText(/\/empfehlung/, async (msg) => { const chatId = msg.chat.id; try { const dailyMovie = await fetchDailyRecommendation(); if (dailyMovie) { // Film empfehlen const movieTitle = dailyMovie.title || 'Unbekannt'; const movieSummary = dailyMovie.summary || 'Keine Zusammenfassung verfügbar'; const movieThumb = dailyMovie.thumb ? `${process.env.PLEX_DOMAIN}${dailyMovie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : ''; // Erstellen der Bildunterschrift und Kürzen, falls nötig const message = createCaption(movieTitle, movieSummary); // Trailer URL abrufen const trailerUrl = await fetchTrailerUrl(movieTitle); // Bild anzeigen, wenn vorhanden if (movieThumb) { const options = { caption: message, parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: "Trailer ansehen", url: trailerUrl || "https://www.youtube.com", // Fallback-URL, falls kein Trailer gefunden wird } ] ] } }; await bot.sendPhoto(chatId, movieThumb, options).catch(error => { logError(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { const options = { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: "Trailer ansehen", url: trailerUrl || "https://www.youtube.com", // Fallback-URL, falls kein Trailer gefunden wird } ] ] } }; await bot.sendMessage(chatId, message, options).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); } logMessage(`Sent daily recommendation to chatId ${chatId}`); } else { await bot.sendMessage(chatId, 'Keine Empfehlungen verfügbar.').catch(error => { logError(`Error sending no recommendation message to chatId ${chatId}: ${error.message}`); }); logMessage(`No daily recommendation found for chatId ${chatId}`); } } catch (error) { let errorMessage = 'Fehler beim Abrufen der Empfehlung.'; if (error.response) { errorMessage += ` Statuscode: ${error.response.status}`; logError(`Error fetching daily recommendation: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { errorMessage += ' Keine Antwort vom Server.'; logError(`Error fetching daily recommendation: No response from server`); } else { errorMessage += ` Unbekannter Fehler: ${error.message}`; logError(`Error fetching daily recommendation: ${error.message}`); } await bot.sendMessage(chatId, errorMessage).catch(err => { logError(`Error sending daily recommendation error message to chatId ${chatId}: ${err.message}`); }); } }); // Session-Management für Feedback const feedbackSessions = {}; // Fehlerprotokollierungsfunktion function logError(error) { const errorMessage = `${dayjs().format('HH:mm:ss')} - Error: ${error}\n`; fs.appendFileSync(ERROR_LOG_PATH, errorMessage); } // Speichert Feedback in der Datei function saveFeedbackToFile(feedbackData) { // Wenn die Datei nicht existiert, erstelle sie mit dem Header if (!fs.existsSync(feedbackFilePath)) { fs.writeFileSync(feedbackFilePath, 'timestamp - chatId: feedback\n'); } const feedback = `${feedbackData.timestamp} - chatId ${feedbackData.chatId}: ${feedbackData.feedback}\n`; fs.appendFileSync(feedbackFilePath, feedback); } // Sendet Feedback an Administratoren function sendFeedbackToAdmins(userId, feedback) { const adminChatIds = [USER1_ID, USER2_ID]; // Hier sollten die IDs der Administratoren festgelegt werden const message = `📢 Neues Feedback:\n\n Von userId: "${userId}"\n\n"${feedback}"`; adminChatIds.forEach(adminChatId => { bot.sendMessage(adminChatId, message).catch(error => { logError(`Fehler beim Senden von Feedback an Admin chatId ${adminChatId}: ${error.message}`); }); }); } const feedbackFilePath = path.join(__dirname, 'feedback.log'); // Überprüfe, ob dieser Pfad korrekt ist // Fehlerprotokollierungsfunktion function logError(error) { const errorMessage = `${new Date().toISOString()} - Error: ${error.message || error}\n`; try { fs.appendFileSync(errorLogPath, errorMessage); } catch (err) { console.error('Fehler beim Schreiben in die Fehlerprotokolldatei:', err.message); } } // Funktion, die überprüft, ob ein Benutzer autorisiert ist function isUserAuthorized(userId) { const authorizedUsers = [process.env.USER1_ID, process.env.USER2_ID]; return authorizedUsers.includes(userId.toString()); } // Funktion, die überprüft, ob ein Benutzer autorisiert ist function isUserAuthorized(userId) { const authorizedUsers = [process.env.USER1_ID, process.env.USER2_ID]; return authorizedUsers.includes(userId.toString()); } // Speichert Feedback in der Datei function saveFeedbackToFile({ chatId, feedback, timestamp }) { const feedbackEntry = `${timestamp} - chatId ${chatId}: ${feedback}\n`; try { if (!fs.existsSync(feedbackFilePath)) { fs.writeFileSync(feedbackFilePath, 'timestamp - chatId: feedback\n'); } fs.appendFileSync(feedbackFilePath, feedbackEntry); } catch (err) { logError(`Fehler beim Speichern des Feedbacks: ${err.message}`); } } // Sendet Feedback an Administratoren function sendFeedbackToAdmins(userId, feedback) { const adminChatIds = [process.env.USER1_ID, process.env.USER2_ID]; const message = ` ✨ *Neues Feedback* ✨ 🆔 *User ID:* ${userId} ════════════════════════════════════ 📌 *Zusammenfassung:* 📌 ${feedback} `; adminChatIds.forEach(adminChatId => { bot.sendMessage(adminChatId, message) .catch(error => { logError(`Fehler beim Senden von Feedback an Admin chatId ${adminChatId}: ${error.message}`); }); }); } // Handler für den /feedback Befehl bot.onText(/\/feedback/, (msg) => { const chatId = msg.chat.id; // Startet eine Feedback-Sitzung feedbackSessions[chatId] = { waitingForFeedback: true }; bot.sendMessage(chatId, '✍️ Bitte gib dein Feedback ein. Du kannst den Befehl `/cancel` verwenden, um das Feedback zu abbrechen.', { parse_mode: 'Markdown' }) .catch(error => { logError(`Fehler beim Senden der Feedback-Aufforderung an chatId ${chatId}: ${error.message}`); }); }); // Handler für den /cancel Befehl bot.onText(/\/cancel/, (msg) => { const chatId = msg.chat.id; if (feedbackSessions[chatId]) { delete feedbackSessions[chatId]; bot.sendMessage(chatId, 'Feedback wurde abgebrochen.', { parse_mode: 'Markdown' }) .catch(error => { logError(`Fehler beim Senden der Abbruch-Nachricht an chatId ${chatId}: ${error.message}`); }); } }); // Handler für Nachrichten bot.on('message', (msg) => { const chatId = msg.chat.id; if (feedbackSessions[chatId] && msg.text && msg.text !== '/cancel') { const feedback = msg.text; const userId = msg.from.id; // Die userId des Feedbackers saveFeedbackToFile({ chatId, feedback, timestamp: dayjs().format('YYYY-MM-DD HH:mm:ss') }); sendFeedbackToAdmins(userId, feedback); bot.sendMessage(chatId, '👍 Danke für dein Feedback!', { parse_mode: 'Markdown' }) .catch(error => { logError(`Fehler beim Senden der Bestätigung an chatId ${chatId}: ${error.message}`); }); delete feedbackSessions[chatId]; } }); // Beispiel zur erweiterten Fehlerbehandlung im Bot bot.on('polling_error', (error) => { logError(`Polling Error: ${error.code} - ${error.message}`); }); // Handler für den /f_log Befehl bot.onText(/\/f_log/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; if (isUserAuthorized(userId)) { try { if (fs.existsSync(feedbackFilePath)) { const tempFilePath = path.join(__dirname, 'feedback_log.txt'); const feedbackData = fs.readFileSync(feedbackFilePath, 'utf8'); fs.writeFileSync(tempFilePath, feedbackData); bot.sendDocument(chatId, tempFilePath) .then(() => { fs.unlinkSync(tempFilePath); console.log('Feedback-Log-Datei erfolgreich gesendet und gelöscht.'); }) .catch(error => { logError(`Fehler beim Senden der feedback_log.txt an chatId ${chatId}: ${error.message}`); bot.sendMessage(chatId, '❌ Fehler beim Senden der Feedback-Log-Datei.') .catch(err => { logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${err.message}`); }); }); } else { const errMsg = `Keine Feedback-Datei gefunden unter ${feedbackFilePath}.`; console.log(errMsg); bot.sendMessage(chatId, `❌ ${errMsg}`) .catch(error => { logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${error.message}`); }); } } catch (error) { logError(`Fehler beim Senden der Feedback-Log-Datei: ${error.message}`); bot.sendMessage(chatId, '❌ Fehler beim Senden der Feedback-Log-Datei.') .catch(err => { logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${err.message}`); }); } } else { const errMsg = `Unberechtigter Zugriff auf /f_log von userId ${userId}.`; console.log(errMsg); bot.sendMessage(chatId, `❌ ${errMsg}`) .catch(error => { logError(`Unberechtigter Zugriff auf /f_log von userId ${userId}: ${error.message}`); }); } }); // Funktion zum Erstellen der allgemeinen Hilfennachricht function createHelpMessage() { return `📜 *Hier ist eine Liste der verfügbaren Befehle:*\n\n` + `👋 /start - Registriert deinen Zugang.\n\n` + `🔔 /notification\\_on - Aktiviert Benachrichtigungen für neue Filme.\n\n` + `🔕 /notification\\_off - Deaktiviert Benachrichtigungen für neue Filme.\n\n` + `📺 /serien - Zeigt eine Liste aller Serien an.\n\n` + `🎬 /latestmovie - Zeigt den zuletzt hinzugefügten Film an.\n\n` + `📅 /latest10movies - Zeigt die letzten 10 hinzugefügten Filme an.\n\n` + `⭐ /top\\_rated - Zeigt die am besten bewerteten Filme an.\n\n` + `💭 /wunsch - Nutze diesen Befehl, um einen Filmwunsch zu äußern.\n\n` + `🎬 /trailer - Fordere einen Trailer für einen bestimmten Film an. \n\n` + `🔝 /empfehlung - Film Empfehlung des Tages.\n\n` + `📰 /newsletter - zeigt die Newsletter Funktion an\n\n` + `🌙 /night - Schaltet den Nachtmodus ein oder aus.\n\n` + // Hinzugefügter Befehl `❓ /help - Zeigt diese Hilfennachricht an.\n\n`; } // Funktion zum Erstellen der weiteren Hilfennachricht function createMoreHelpMessage() { return `📜 *weitere Hilfe:*\n\n` + `📝 /profil - Zeigt dein Profil an\n\n` + `✨ /w\\_list - Zeigt dir deine Wünsche an.\n\n` + `🔧 /dev - Funktionswunsch oder Bug melden.\n\n` + `💬 /feedback - Gib Feedback zum Bot.\n\n` + `❓ /faq - Häufig gestellte Fragen.\n\n` + `ℹ️ /info - Anzahl Filme und Serien.\n\n` + `🤖 /bot - Bot-Informationen.\n\n`; } // Funktion zum Erstellen der Admin-Hilfennachricht function createAdminHelpMessage() { return `*👨‍💻 Admin Befehle* \n\n` + `🛠️ /admin - sendet eine Nachricht an alle Nutzer.\n\n` + `🔒 /passwd - gibt dir das Aktuelle Passwort vom Frontend\n\n` + `✨ /open\\_wishes - Zeigt alle offenen Wünsche an\n\n` + `👤 /user - Zeigt Benutzerinformationen an.\n\n` + `📰 /newsletter - Zeigt die Newsletter Funktion an\n\n` + `📝 /logs - Zeigt die letzten Fehlermeldungen an.\n\n` + `🗑️ /log\\_delete - Löscht Logs.\n\n` + `📝 /f\\_log - Sendet das Feedback als .txt-Datei.\n\n` + `❓ /add\\_faq - Fügt eine neue Frage zur FAQ hinzu.\n\n` + `🗑️ /del\\_faq - Löscht eine FAQ.\n\n\n`+ `*👨‍💻 Dev Befehle* \n\n` + `🔄 /update - Aktuallisiert die user.yml\n\n` + `🗃️ /command\\_history - Zeigt eine Liste der zuletzt verwendeten Befehle an.\n\n` + `💾 /backup - erstellt ein Backup und sendet es als zip\n\n` + `🪧 /serverinfo - Zeigt Informationen über den Server\n\n` + `🔍 /healthcheck - Überprüft den Bot\n\n` + `🔄 /setdebug - Aktiviert oder deaktiviert den Debug-Modus\n\n` + `🛠️ /support - Erstellt ein Support-Ticket an den Bot-Ersteller.\n\n`; } // /help-Befehl verarbeiten bot.onText(/\/help/, (msg) => { const chatId = msg.chat.id; // Prüfen, ob der Benutzer ein Admin ist const isAdmin = chatId.toString() === process.env.USER1_ID || chatId.toString() === process.env.USER2_ID; if (!isAdmin) { // Normale Benutzer: Hilfennachricht und "Mehr"-Button anzeigen const helpMessage = createHelpMessage(); const options = { reply_markup: { inline_keyboard: [ [ { text: "Mehr", callback_data: "more_help", }, { text: "Kontakt", url: process.env.CONTACT_LINK, } ] ] }, parse_mode: 'Markdown' }; bot.sendMessage(chatId, helpMessage, options).catch(error => { console.log(`Error sending help message to chatId ${chatId}: ${error.message}`); }); } else { // Admin-Benutzer: Buttons "User Hilfe" und "Admin Hilfe" anzeigen const options = { reply_markup: { inline_keyboard: [ [ { text: "User Hilfe", callback_data: "user_help" }, { text: "Admin Hilfe", callback_data: "admin_help", } ] ] }, parse_mode: 'Markdown' }; bot.sendMessage(chatId, "Bitte wähle eine Option:", options).catch(error => { console.log(`Error sending admin help buttons to chatId ${chatId}: ${error.message}`); }); } }); // Callback für die Inline-Buttons verarbeiten bot.on('callback_query', (callbackQuery) => { const chatId = callbackQuery.message.chat.id; const data = callbackQuery.data; if (data === "user_help") { const helpMessage = createHelpMessage(); // Inline-Button für "Mehr" und Kontakt hinzufügen const options = { reply_markup: { inline_keyboard: [ [ { text: "Mehr", callback_data: "more_help", }, { text: "Kontakt", url: process.env.CONTACT_LINK, } ] ] }, parse_mode: 'Markdown' }; bot.sendMessage(chatId, helpMessage, options).catch(error => { console.log(`Error sending user help message to chatId ${chatId}: ${error.message}`); }); } else if (data === "more_help") { const moreHelpMessage = createMoreHelpMessage(); // Kontakt-Button hinzufügen const options = { reply_markup: { inline_keyboard: [ [ { text: "Kontakt", url: process.env.CONTACT_LINK, } ] ] }, parse_mode: 'Markdown' }; bot.sendMessage(chatId, moreHelpMessage, options).catch(error => { console.log(`Error sending more help message to chatId ${chatId}: ${error.message}`); }); } else if (data === "admin_help") { // Überprüfung, ob der Benutzer berechtigt ist if (chatId.toString() === process.env.USER1_ID || chatId.toString() === process.env.USER2_ID) { const adminHelpMessage = createAdminHelpMessage(); const options = { reply_markup: { inline_keyboard: [ [ { text: "Kontakt", url: process.env.CONTACT_LINK, } ] ] }, parse_mode: 'Markdown' }; bot.sendMessage(chatId, adminHelpMessage, options).catch(error => { console.log(`Error sending admin help message to chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, "Du hast keine Berechtigung, diesen Befehl zu verwenden.", { parse_mode: 'Markdown' }).catch(error => { console.log(`Error sending unauthorized message to chatId ${chatId}: ${error.message}`); }); } } }); // Funktion zum Abrufen der letzten 10 hinzugefügten Filme async function fetchLatest10Movies() { try { const movies = await fetchAllMovies(); const sortedMovies = movies .filter(movie => movie.addedAt) .sort((a, b) => b.addedAt - a.addedAt) .slice(0, 10); // Nimm nur die neuesten 10 Filme return sortedMovies; } catch (error) { logError(`Error fetching latest 10 movies: ${error.message}`); throw error; } } // Funktion zum Abrufen der letzten 10 hinzugefügten Filme async function fetchLatest10Movies() { try { const movies = await fetchAllMovies(); const sortedMovies = movies .filter(movie => movie.addedAt) .sort((a, b) => b.addedAt - a.addedAt) .slice(0, 10); // Nimm nur die neuesten 10 Filme return sortedMovies; } catch (error) { logError(`Error fetching latest 10 movies: ${error.message}`); throw error; } } // Funktion zum Abrufen der letzten 10 hinzugefügten Filme async function fetchLatest10Movies() { try { const movies = await fetchAllMovies(); const sortedMovies = movies .filter(movie => movie.addedAt) .sort((a, b) => b.addedAt - a.addedAt) .slice(0, 10); // Nimm nur die neuesten 10 Filme return sortedMovies; } catch (error) { logError(`Error fetching latest 10 movies: ${error.message}`); throw error; } } // Maximal zulässige Länge der Bildunterschrift (in Zeichen) const MAX_CAPTION_LENGTH = 1024; // Telegrams Beschränkung für Bildunterschriften // Funktion zum Kürzen der Zusammenfassung function truncateSummary(summary, maxLength) { if (summary.length > maxLength) { return summary.slice(0, maxLength) + '...'; // Kürzen und "..." hinzufügen } return summary; } // Funktion zum Erstellen der Bildunterschrift function createCaption(title, summary, addedAt) { // Initiale Bildunterschrift ohne Kürzung let caption = ` 🎬 Titel: ${title || 'Unbekannt'} 📝 Zusammenfassung: ${summary || 'Keine Zusammenfassung verfügbar.'} 📅 Hinzugefügt am: ${dayjs(addedAt * 1000).format('DD.MM.YYYY')} `; // Überprüfen, ob die Bildunterschrift zu lang ist if (caption.length > MAX_CAPTION_LENGTH) { // Berechnen der maximalen Länge für die Zusammenfassung const maxSummaryLength = MAX_CAPTION_LENGTH - (caption.length - summary.length); // Kürzen der Zusammenfassung auf die berechnete Länge const truncatedSummary = truncateSummary(summary, maxSummaryLength); // Neu zusammenstellen der Bildunterschrift mit der gekürzten Zusammenfassung caption = ` 🎬 Titel: ${title || 'Unbekannt'} 📝 Zusammenfassung: ${truncatedSummary} 📅 Hinzugefügt am: ${dayjs(addedAt * 1000).format('DD.MM.YYYY')} `; } return caption; } // /latest10movies-Befehl verarbeiten bot.onText(/\/latest10movies/, async (msg) => { const chatId = msg.chat.id; try { const latestMovies = await fetchLatest10Movies(); if (latestMovies.length > 0) { const numberEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']; const inlineKeyboard = [[], []]; // Zwei Zeilen für das Inline-Keyboard let message = 'Letzten 10 hinzugefügten Filme:\n\n'; latestMovies.forEach((movie, index) => { const numberEmoji = numberEmojis[index] || ''; message += `${numberEmoji} - ${movie.title || 'Unbekannt'}\n\n`; // Ordne die Schaltflächen in zwei Zeilen an (5 pro Zeile) const rowIndex = index < 5 ? 0 : 1; inlineKeyboard[rowIndex].push({ text: numberEmoji, callback_data: `movie_${index}` }); }); // Füge die Anweisung unter den Filmnamen hinzu message += '\nKlicke auf die Zahl, um nähere Informationen zu bekommen.'; bot.sendMessage(chatId, message, { reply_markup: { inline_keyboard: inlineKeyboard } }).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); logMessage(`Sent latest 10 movies info to chatId ${chatId}`); } else { bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => { logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`); }); logMessage(`No movies found for chatId ${chatId}`); } } catch (error) { handleError(chatId, error); } }); app.use(express.json()); //Anfang für Frontend // schnittstelle für Kontakt.html app.get('/api/contact-info', (req, res) => { res.json({ email: process.env.SMTP_USER, telegram: process.env.CONTACT_LINK }); }); // Middleware app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); // Route für Umgebungsvariablen app.get('/api/env', (req, res) => { res.json({ botAlias: process.env.BOT_ALIAS, telegramLink: process.env.TELEGRAM_LINK, PW_FRONT: process.env.PW_FRONT, //Frontend Passwort Abfrage }); }); app.use(express.static('public')); // Middleware app.use(bodyParser.json()); app.use(express.static('public')); // Funktion zum Laden der Abonnenten function loadSubscribers() { if (fs.existsSync(subscribersFilePath)) { const data = fs.readFileSync(subscribersFilePath); subscribers = JSON.parse(data); } } // Abonnieren app.post('/subscribe', (req, res) => { const { email, chatId, username } = req.body; if (!subscribers.find(subscriber => subscriber.chatId === chatId)) { subscribers.push({ chatId, email, username }); fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); sendConfirmationEmail(email); // Bestätigungs-E-Mail senden res.status(200).send('Erfolgreich angemeldet!'); } else { res.status(400).send('Bereits angemeldet!'); } }); // Abmelden app.post('/unsubscribe', (req, res) => { const { chatId } = req.body; subscribers = subscribers.filter(subscriber => subscriber.chatId !== chatId); fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2)); res.status(200).send('Erfolgreich abgemeldet!'); }); // Lade Abonnenten beim Start loadSubscribers(); // API-Route für die neuesten Filme app.get('/api/latest-movies', async (req, res) => { try { const response = await axios.get(`${process.env.PLEX_DOMAIN}/library/recentlyAdded?X-Plex-Token=${process.env.PLEX_TOKEN}`); const movies = response.data.MediaContainer.Metadata.slice(0, 10).map(movie => ({ title: movie.title, coverImage: `${process.env.PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}`, // Coverbild-URL mit Token summary: movie.summary // Hier fügst du die Zusammenfassung hinzu })); console.log(movies); // Überprüfung der Daten res.json(movies); } catch (error) { console.error('Fehler beim Abrufen der neuesten Filme:', error); res.status(500).json({ error: 'Interner Serverfehler' }); } }); app.get('/api/telegram-link', (req, res) => { res.json({ link: process.env.TELEGRAM_LINK }); // Stelle den Link aus der .env bereit }); app.get('/api/bot-version', (req, res) => { res.json({ version: process.env.BOT_VERSION }); }); // API-Route, die den Wert von WEB_NAME bereitstellt app.get('/api/web-name', (req, res) => { res.json({ name: process.env.WEB_NAME }); }); // Inline-Knopf-Ereignis für Film auswählen verarbeiten bot.on('callback_query', async (callbackQuery) => { const chatId = callbackQuery.message.chat.id; const data = callbackQuery.data; if (data.startsWith('movie_')) { const movieIndex = parseInt(data.split('_')[1], 10); try { const latestMovies = await fetchLatest10Movies(); const selectedMovie = latestMovies[movieIndex]; if (selectedMovie) { // Bildunterschrift erstellen und kürzen, falls nötig const movieDetails = createCaption(selectedMovie.title, selectedMovie.summary, selectedMovie.addedAt); if (selectedMovie.thumb) { const imageUrl = `${PLEX_DOMAIN}${selectedMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}`; bot.sendPhoto(chatId, imageUrl, { caption: movieDetails, parse_mode: 'Markdown' }).catch(error => { logError(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { bot.sendMessage(chatId, movieDetails, { parse_mode: 'Markdown' }).catch(error => { logError(`Error sending message to chatId ${chatId}: ${error.message}`); }); } logMessage(`Sent movie details for movie index ${movieIndex} to chatId ${chatId}`); } else { bot.sendMessage(chatId, 'Film nicht gefunden.').catch(error => { logError(`Error sending movie not found message to chatId ${chatId}: ${error.message}`); }); } } catch (error) { handleError(chatId, error); } } }); function handleError(chatId, error) { if (error.response) { bot.sendMessage(chatId, `Fehler beim Abrufen der Daten. Statuscode: ${error.response.status}`).catch(err => { logError(`Error sending error message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching data: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { bot.sendMessage(chatId, 'Fehler beim Abrufen der Daten. Keine Antwort vom Server.').catch(err => { logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); }); logError(`Error fetching data: No response from server`); } else { logError(`Error fetching data: ${error.message}`); } } // Route für das Dashboard app.get('/admin/dashboard', (req, res) => { if (!req.session.user) { // Überprüfung, ob der Benutzer eingeloggt ist return res.redirect('/login'); // Weiterleitung zur Login-Seite } res.sendFile(__dirname + '/views/admin-dashboard.html'); // Sende die HTML-Datei }); // API-Endpunkt für Bot-Laufzeit app.get('/api/bot-uptime', (req, res) => { const uptime = process.uptime(); const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); const seconds = Math.floor(uptime % 60); res.json({ runtime: `${hours}h ${minutes}m ${seconds}s` }); }); // API-Endpunkt für Dateiprüfung app.get('/api/file-check', (req, res) => { const requiredFiles = ['user.yml', 'faq.json', 'subscribers.json', 'dev_reports.json', 'w_offen.json', 'feedback.log', 'command_history.json', 'error.log', 'Cache/cache-series.json', 'Cache/cache.json', 'Log/message.log', 'wunsch', 'backups']; let fileStatus = requiredFiles.map(file => ({ file: file, exists: fs.existsSync(file) })); res.json(fileStatus); }); // API-Endpunkt für Serverinformationen app.get('/api/server-info', (req, res) => { const totalMemory = os.totalmem() / (1024 ** 3); // In GB const freeMemory = os.freemem() / (1024 ** 3); // In GB const serverInfo = { platform: os.platform(), architecture: os.arch(), totalMemory: totalMemory.toFixed(2), freeMemory: freeMemory.toFixed(2) }; res.json(serverInfo); }); // Route für das Fehlerprotokoll app.get('/api/error-log', (req, res) => { fs.readFile('./error.log', 'utf8', (err, data) => { if (err) { return res.status(500).send('Fehler beim Lesen des Fehlerprotokolls'); } res.send(data); }); }); // Route für die Kommando-Historie app.get('/api/command-history', (req, res) => { fs.readFile('./command_history.json', 'utf8', (err, data) => { if (err) { return res.status(500).send('Fehler beim Lesen der Kommando-Historie'); } res.send(data); }); }); // Route zum Leeren des Fehlerprotokolls app.post('/api/clear-error-log', (req, res) => { fs.writeFile('./error.log', '', (err) => { if (err) { return res.status(500).json({ success: false, message: 'Fehler beim Leeren des Fehlerprotokolls' }); } res.json({ success: true }); }); }); // Route zum Leeren der Kommando-Historie app.post('/api/clear-command-history', (req, res) => { fs.writeFile('./command_history.json', '', (err) => { if (err) { return res.status(500).json({ success: false, message: 'Fehler beim Leeren der Kommando-Historie' }); } res.json({ success: true }); }); }); // Route zum Abrufen der FAQs app.get('/api/faqs', (req, res) => { const faqs = loadFaqs(); res.json(faqs); }); // Route zum Hinzufügen einer neuen FAQ app.post('/api/add-faq', (req, res) => { const faqs = loadFaqs(); const { question, answer } = req.body; faqs.push({ question, answer }); saveFaqs(faqs); res.json({ success: true }); }); // Route zum Löschen einer FAQ app.delete('/api/delete-faq', (req, res) => { const faqs = loadFaqs(); const index = req.body.index; if (index >= 0 && index < faqs.length) { faqs.splice(index, 1); saveFaqs(faqs); res.json({ success: true }); } else { res.status(400).json({ success: false }); } }); app.get('/api/wishes', (req, res) => { fs.readFile('w_offen.json', 'utf8', (err, data) => { if (err) { return res.status(500).json({ error: 'Fehler beim Lesen der Wünsche' }); } try { // Hier kannst du die Logik zum Parsen der Datei manuell implementieren const wishes = parseWishes(data); res.json(wishes); } catch (error) { return res.status(500).json({ error: 'Fehler beim Verarbeiten der Wünsche' }); } }); }); // Funktion zum manuellen Parsen der nicht-standardisierten Daten function parseWishes(data) { const wishes = []; const lines = data.split('\n'); let currentWish = {}; for (let line of lines) { if (line.startsWith('- userId:')) { if (Object.keys(currentWish).length > 0) { wishes.push(currentWish); // vorherigen Wunsch speichern } currentWish = { userId: parseInt(line.split(': ')[1]) }; // userId speichern } else if (line.startsWith('message: |-')) { // Nächste Zeile ist der Beginn der Nachricht currentWish.message = ''; } else if (currentWish.message !== undefined) { currentWish.message += line + '\n'; // Nachricht aufbauen } } // Den letzten Wunsch hinzufügen if (Object.keys(currentWish).length > 0) { wishes.push(currentWish); } return wishes; } // Endpoint für das Feedback app.get('/api/feedback', (req, res) => { const feedbackFilePath = path.join(__dirname, 'feedback.log'); fs.readFile(feedbackFilePath, 'utf8', (err, data) => { if (err) { console.error('Fehler beim Lesen der feedback.log:', err); return res.status(500).send('Fehler beim Laden des Feedbacks.'); } res.send(data); }); }); // Endpunkt /api/users, um die user.yml-Datei zu lesen und die Daten im JSON-Format zurückzugeben app.get('/api/users', (req, res) => { try { // Pfad zur user.yml-Datei const filePath = path.join(__dirname, 'user.yml'); // YAML-Datei laden const file = fs.readFileSync(filePath, 'utf8'); // YAML in ein JSON-Objekt konvertieren const data = yaml.parse(file); // 'parse' Funktion verwenden // Benutzerobjekte in ein Array umwandeln const usersArray = Object.values(data).map(user => { // Überprüfen, ob nightModes vorhanden sind und Werte abrufen const nightMode = user.nightModes && user.nightModes.length > 0 ? user.nightModes[0] : null; // Wenn kein Nachtmodus vorhanden ist, dann "Deaktiviert" anzeigen const nightModeDisplay = nightMode ? `${nightMode.startTime} - ${nightMode.endTime}` : 'Deaktiviert'; // Hier den gewünschten Text verwenden return { userId: user.userId, username: user.username, notifications: user.notifications, firstUsed: user.firstUsed, favoriteGenres: user.favoriteGenres && user.favoriteGenres.length > 0 ? user.favoriteGenres.join(', ') // Falls favoriteGenres existiert, kommagetrenntes Format : (user.favoriteGenre || 'Nicht festgelegt'), // Fallback auf favoriteGenre oder Standardwert commandCount: user.commandCount || 0, // Standard auf 0, wenn nicht vorhanden userLevel: user.userLevel || 'Nicht festgelegt', // Standard-Wert nightMode: nightModeDisplay // Verwendung der neuen Logik für die Nachtmodus-Anzeige }; }); // JSON-Daten zurückgeben res.json(usersArray); } catch (err) { console.error('Fehler beim Laden der YAML-Datei:', err); res.status(500).json({ message: 'Fehler beim Laden der Benutzerdaten' }); } }); let lastRestart = new Date(); // Speichere den aktuellen Zeitpunkt als letzten Neustart // Funktion zum Formatieren des Datums const formatLastRestartDate = (date) => { const options = { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false // 24-Stunden-Format }; return date.toLocaleString('de-DE', options); }; // Beispiel: Funktion, die beim Neustart des Bots aufgerufen wird function onBotRestart() { lastRestart = new Date(); // Aktualisiere den letzten Neustart } // Endpunkt für den letzten Neustart app.get('/api/last-restart', (req, res) => { // Hier ist der letzte Neustart korrekt referenziert res.json({ lastRestart: formatLastRestartDate(lastRestart) }); }); // Beispiel: Rufe die Funktion auf, wenn der Bot neu gestartet wird onBotRestart(); app.post('/api/send-message', async (req, res) => { const { message } = req.body; // Überprüfen, ob die Nachricht leer ist if (!message) { return res.status(400).json({ success: false, error: 'Nachricht darf nicht leer sein.' }); } try { const users = yaml.load(USER_YML_PATH); const sendMessages = Object.keys(users).map(userChatId => { return bot.sendMessage(userChatId, `❗️Systemnachricht\n\n"${message}"`).catch(error => { logError(`Fehler beim Senden der Systemnachricht an chatId ${userChatId}: ${error.message}`); }); }).filter(promise => promise !== undefined); await Promise.all(sendMessages); res.json({ success: true }); } catch (error) { console.error('Fehler beim Senden der Nachricht an alle Benutzer:', error); res.status(500).json({ success: false, error: error.message }); } }); app.get('/api/media-count', async (req, res) => { try { const mediaData = await fetchAllMedia(); // Stelle sicher, dass diese Funktion existiert const { movieCount, showCount } = mediaData; // Zieht die Anzahl der Filme und Serien res.json({ movieCount, showCount }); } catch (error) { console.error('Fehler beim Abrufen der Medienanzahl:', error); res.status(500).json({ error: 'Fehler beim Abrufen der Medienanzahl.' }); } }); // Express-Endpunkt zum Empfangen des Wunsches app.post('/api/telegram-wunsch', (req, res) => { const { wunsch, type } = req.body; // Überprüfe, ob req.user vorhanden ist und ob chatId existiert, andernfalls Dummy-ID verwenden const chatId = req.user && req.user.chatId ? req.user.chatId : '123456789'; // Dummy chatId sendWish(wunsch, type, chatId) .then(() => { res.json({ message: 'Dein Wunsch wurde erfolgreich gesendet!' }); }) .catch(err => { console.error('Fehler beim Senden des Wunsches:', err); res.status(500).json({ message: 'Fehler beim Senden deines Wunsches.' }); }); }); app.get('/api/admin-password', (req, res) => { res.json({ password: process.env.ADMIN_PW }); }); const { exec } = require('child_process'); const path7zip = require('7zip-bin').path7za; // Pfad zu 7zip const BACKUP_DIR = path.join(__dirname, 'backups'); const ZIP_PASSWORD = process.env.ZIP_PW; // Passwort aus der .env-Datei // Sicherstellen, dass der Backup-Ordner existiert if (!fs.existsSync(BACKUP_DIR)) { fs.mkdirSync(BACKUP_DIR, { recursive: true }); } // Middleware für statische Dateien app.use('/backups', express.static(BACKUP_DIR)); // API-Endpunkt für das Erstellen eines Backups app.post('/api/create-backup', (req, res) => { const backupFileName = `backup_${Date.now()}.zip`; const backupFilePath = path.join(BACKUP_DIR, backupFileName); // Erstelle das Backup als ZIP mit Passwort const command = `"${path7zip}" a -tzip "${backupFilePath}" * -p${ZIP_PASSWORD} -xr!backups -xr!node_modules`; exec(command, { cwd: __dirname }, (err, stdout, stderr) => { if (err) { console.error('Fehler beim Erstellen des Backups:', err); return res.status(500).json({ success: false, error: 'Fehler beim Erstellen des Backups' }); } console.log(`Backup erfolgreich erstellt: ${backupFileName}`); checkBackupCount(); // Überprüfe die Anzahl der Backups res.json({ success: true, fileName: backupFileName }); }); }); // API-Endpunkt für das Abrufen der Backups app.get('/api/backups', (req, res) => { fs.readdir(BACKUP_DIR, (err, files) => { if (err) { console.error('Fehler beim Lesen des Backup-Verzeichnisses:', err); return res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Backups' }); } const backups = files.map(file => ({ name: file, date: fs.statSync(path.join(BACKUP_DIR, file)).mtime, })); res.json({ success: true, backups }); }); }); // API-Endpunkt für das Löschen eines Backups app.post('/api/delete-backup', (req, res) => { const { backupName } = req.body; fs.unlink(path.join(BACKUP_DIR, backupName), (err) => { if (err) { console.error('Fehler beim Löschen des Backups:', err); return res.status(500).json({ success: false, error: 'Fehler beim Löschen des Backups' }); } console.log(`Backup gelöscht: ${backupName}`); res.json({ success: true }); }); }); // API-Endpunkt zum Herunterladen eines Backups app.get('/api/download-backup/:backupName', (req, res) => { const { backupName } = req.params; const filePath = path.join(BACKUP_DIR, backupName); // Überprüfen, ob die Datei existiert if (fs.existsSync(filePath)) { res.download(filePath, backupName, (err) => { if (err) { console.error('Fehler beim Herunterladen des Backups:', err); res.status(500).json({ success: false, error: 'Fehler beim Herunterladen des Backups' }); } }); } else { res.status(404).json({ success: false, error: 'Backup nicht gefunden' }); } }); // Funktion zur Überprüfung der Anzahl der Backups und ggf. Löschen älterer Backups function checkBackupCount() { const maxBackupCount = 5; // Maximale Anzahl an Backups fs.readdir(BACKUP_DIR, (err, files) => { if (err) { return console.error('Fehler beim Überprüfen der Backups:', err); } if (files.length > maxBackupCount) { const sortedFiles = files .map(file => ({ name: file, time: fs.statSync(path.join(BACKUP_DIR, file)).mtime.getTime(), })) .sort((a, b) => a.time - b.time); // Lösche die ältesten Backups, um die Anzahl zu reduzieren const filesToDelete = sortedFiles.slice(0, files.length - maxBackupCount); filesToDelete.forEach(file => { fs.unlink(path.join(BACKUP_DIR, file.name), (err) => { if (err) { console.error('Fehler beim Löschen alter Backups:', err); } else { console.log(`Altes Backup gelöscht: ${file.name}`); } }); }); } }); } app.post('/api/toggle-debug', (req, res) => { debugMode = req.body.debugMode; console.log(`Debug-Modus wurde ${debugMode ? 'aktiviert' : 'deaktiviert'}`); res.json({ success: true, debugMode }); }); app.get('/api/debug-status', (req, res) => { res.json({ debugMode }); }); // Beispiel-Endpoint für den Backup-Download app.post('/api/download-backup', (req, res) => { const { backupName, password } = req.body; // Überprüfe das Passwort if (password !== process.env.ADMIN_PW) { return res.status(403).json({ success: false, error: 'Falsches Passwort' }); } // Der Download-Link oder die Logik für den Backup-Download const backupPath = `path/to/backups/${backupName}`; if (fs.existsSync(backupPath)) { res.json({ success: true, downloadUrl: `/backups/${backupName}` }); } else { res.status(404).json({ success: false, error: 'Backup nicht gefunden' }); } }); // API-Endpunkt zum Abrufen der Entwicklerberichte app.get('/api/dev-reports', (req, res) => { try { const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH)); res.json(reports); } catch (error) { console.error('Fehler beim Laden der Entwicklerberichte:', error); res.status(500).json({ message: 'Fehler beim Laden der Entwicklerberichte.' }); } }); // Route zum Löschen eines Dev Reports app.delete('/api/dev-reports', (req, res) => { const reportId = parseInt(req.query.id, 10); try { const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH)); const updatedReports = reports.filter(report => report.id !== reportId); // Lösche den Bericht fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(updatedReports, null, 2)); // Datei aktualisieren res.status(204).send(); // 204 No Content } catch (error) { console.error('Fehler beim Löschen des Berichts:', error); res.status(500).send('Interner Serverfehler'); } }); app.use(bodyParser.json()); // API zum Empfangen der Berichte von der HTML-Seite app.post('/api/submit-report', (req, res) => { const { type, user, message } = req.body; // Falls keine Chat-ID vorhanden ist, generiere eine zufällige ID const chatId = user.id || Math.floor(Math.random() * 1000000); const newReport = { id: Date.now(), // Verwende die aktuelle Zeit als eindeutige ID type, user: { name: user.name || 'Anonym', id: chatId }, message, timestamp: new Date().toISOString() }; try { // Berichte aus der Datei laden oder ein leeres Array verwenden let reports = []; if (fs.existsSync(DEV_REPORTS_FILE_PATH)) { reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH, 'utf-8')); } // Füge den neuen Bericht hinzu reports.push(newReport); // Datei aktualisieren fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(reports, null, 2)); // Optional: Senden des Berichts an Telegram sendToTelegram(newReport); res.status(200).json({ message: 'Bericht erfolgreich übermittelt.' }); } catch (error) { console.error('Fehler beim Schreiben des Berichts:', error); res.status(500).json({ message: 'Fehler beim Schreiben des Berichts.' }); } }); function sendToTelegram(report) { const messageTemplate = `📩 ${report.type}\n\nvon: ${report.user.name} (${report.user.id})\n\n"${report.message}"`; // Telegram API URL const telegramApiUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`; // Sende die Nachricht axios.post(telegramApiUrl, { chat_id: DEV_CHAT_ID, // Sende an die in der .env gespeicherte Dev Chat ID text: messageTemplate, parse_mode: 'Markdown' // Formatierung der Nachricht }) .then(response => { console.log('Nachricht erfolgreich an Telegram gesendet:', response.data); }) .catch(error => { console.error('Fehler beim Senden der Nachricht an Telegram:', error); }); } // Ende Frontend /// Definition der logDebug-Funktion function logDebug(message) { console.log(`${new Date().toISOString()} - DEBUG: ${message}`); } // Express-Server starten app.listen(PORT, () => { console.log(`Webhook server running on port ${PORT}`); }); // Log-Rotation function rotateLogs() { const today = format(new Date(), 'yyyy-MM-dd'); const logFilePath = path.join(LOG_DIR, `${today}.log`); // Lösche die Log-Datei von gestern, wenn sie existiert const yesterday = format(new Date(Date.now() - 24 * 60 * 60 * 1000), 'yyyy-MM-dd'); const oldLogFilePath = path.join(LOG_DIR, `${yesterday}.log`); if (fs.existsSync(oldLogFilePath)) { fs.unlinkSync(oldLogFilePath); // Lösche die alte Logdatei logMessage(`Deleted old log file: ${yesterday}`); } } // Logs täglich um Mitternacht rotieren function scheduleDailyRotation() { const now = new Date(); const millisTillMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0, 0) - now; setTimeout(function() { rotateLogs(); // Rotieren der Logs um Mitternacht setInterval(rotateLogs, 24 * 60 * 60 * 1000); // Danach täglich wiederholen }, millisTillMidnight); } // Starte die tägliche Rotation scheduleDailyRotation(); console.log('Bot is running...');