From 692178e9cb6f97fb11b243de8159f56c90ae8314 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Thu, 15 Aug 2024 17:40:21 +0000 Subject: [PATCH] bot.js aktualisiert --- bot.js | 2112 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 1280 insertions(+), 832 deletions(-) diff --git a/bot.js b/bot.js index 1a22684..5c56619 100644 --- a/bot.js +++ b/bot.js @@ -1,832 +1,1280 @@ -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 { format } = require('date-fns'); -const express = require('express'); -const bodyParser = require('body-parser'); - -// 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 CHANGE_DIR = path.resolve(PROJECT_ROOT, process.env.CHANGE_DIR); -const PORT = process.env.PORT; -const MAX_CHANGE_FILE_SIZE = parseInt(process.env.MAX_CHANGE_FILE_SIZE, 10); -const USER1_ID = process.env.USER1_ID; -const USER2_ID = process.env.USER2_ID; - -// 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); -console.log('CHANGE_DIR:', CHANGE_DIR); - -// Sicherstellen, dass Verzeichnisse und Dateien existieren -if (!fs.existsSync(LOG_DIR)) { - fs.mkdirSync(LOG_DIR, { recursive: true }); -} - -if (!fs.existsSync(CHANGE_DIR)) { - fs.mkdirSync(CHANGE_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 -} - -// 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 = format(new Date(), 'yyyy-MM-dd'); - const logFilePath = path.join(LOG_DIR, `${today}.log`); - fs.appendFileSync(logFilePath, `${format(new Date(), 'HH:mm:ss')} - ${message}\n`); -} - -// Funktion zur Fehlerprotokollierung -function logError(error) { - const errorMessage = `${format(new Date(), 'HH:mm:ss')} - Error: ${error}\n`; - fs.appendFileSync(ERROR_LOG_PATH, errorMessage); -} - -// Funktion zum Protokollieren von Debug-Informationen in die change-Datei -function logDebug(message) { - const now = new Date(); - const changeFilePath = path.join(CHANGE_DIR, `change_${format(now, 'yyyy-MM-dd_HH')}.log`); - - if (fs.existsSync(changeFilePath) && fs.statSync(changeFilePath).size > MAX_CHANGE_FILE_SIZE) { - fs.renameSync(changeFilePath, path.join(CHANGE_DIR, `change_${format(now, 'yyyy-MM-dd_HH')}_old.log`)); - } - - fs.appendFileSync(changeFilePath, `${format(now, 'HH:mm:ss')} - Debug: ${message}\n`); -} - -// 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 { - // Hole alle Sektionen - const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); - const sections = sectionsData.MediaContainer.Directory; - - let movies = []; - - // Hole Filme aus jeder Sektion - 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')); - } - } - - // Sortiere Filme nach 'addedAt' Zeitstempel in absteigender Reihenfolge - 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 Suchen von Filmen nach Genre, Titel und Schauspielern -async function searchMovies(query) { - try { - // Alle Filme abrufen - const movies = await fetchAllMovies(); - - // Suchbegriff in Kleinbuchstaben umwandeln - const searchQuery = query.toLowerCase(); - - // Filtere Filme basierend auf dem Suchbegriff - const results = movies.filter(movie => { - // Sicherstellen, dass wir auf vorhandene Attribute zugreifen - const title = (movie.title || '').toLowerCase(); - const summary = (movie.summary || '').toLowerCase(); - const genres = (movie.genres || []).map(g => g.toLowerCase()); // Genre ist eine Liste - const actors = (movie.actors || []).map(a => a.toLowerCase()); // Schauspieler ist eine Liste - - // Überprüfen, ob der Suchbegriff in einem der Attribute vorkommt - return title.includes(searchQuery) || - summary.includes(searchQuery) || - genres.some(genre => genre.includes(searchQuery)) || - actors.some(actor => actor.includes(searchQuery)); - }).map(movie => ({ - title: movie.title || 'Unbekannt', - summary: movie.summary || 'Keine Zusammenfassung verfügbar', - thumb: movie.thumb ? `${PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : '', - genres: (movie.genres || []).join(', '), // Genre-Liste in einen String umwandeln - actors: (movie.actors || []).join(', ') // Schauspieler-Liste in einen String umwandeln - })); - - return results; - } catch (error) { - logError(`Error searching movies: ${error.message}`); - throw error; - } - } - - -// Funktion zum Abrufen eines zufälligen Films -async function fetchRandomMovie() { - try { - const movies = await fetchAllMovies(); - 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; - } -} - -// /start-Befehl verarbeiten -bot.onText(/\/start/, (msg) => { - const chatId = msg.chat.id; - const userId = msg.from.id; - - // Benutzerdaten in user.yml speichern - let users = yaml.load(USER_YML_PATH); - users[chatId] = userId; - fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); - - const welcomeMessage = ` -Willkommen! Dein Zugang zum Bot wurde erfolgreich eingerichtet. - -Um die verfügbaren Befehle anzuzeigen, tippe /help. - `; - bot.sendMessage(chatId, welcomeMessage); - - // /start-Befehl protokollieren - logMessage(`Received /start command from chatId ${chatId} (userId ${userId})`); -}); - -// /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}` : ''; - - const message = `Der zuletzt hinzugefügte Film ist:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}\n\nHinzugefügt am: ${addedAtDate}`; - - // Bild anzeigen, wenn vorhanden - if (movieThumb) { - bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { - logError(`Error sending photo to chatId ${chatId}: ${error.message}`); - }); - } else { - bot.sendMessage(chatId, message).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 { - bot.sendMessage(chatId, 'Fehler beim Abrufen der neuesten Filme. Unbekannter Fehler.').catch(err => { - logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching latest movie: ${error.message}`); - } - } -}); - -// /info-Befehl verarbeiten -bot.onText(/\/info/, async (msg) => { - const chatId = msg.chat.id; - const plexDomain = process.env.PLEX_DOMAIN; // Lese die Plex-Domain aus der Umgebungsvariablen - - try { - const { movieCount, showCount } = await fetchAllMedia(); - const message = `In der Bibliothek befinden sich derzeit:\n\nFilme: ${movieCount}\nSerien: ${showCount}\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}`); - }); - - logMessage(`Sent media count, copyright, and Plex button to chatId ${chatId}`); - } catch (error) { - logError(`Error fetching media count: ${error.message}`); - await bot.sendMessage(chatId, 'Fehler beim Abrufen der Medienanzahl.').catch(err => { - logError(`Error sending media count error message to chatId ${chatId}: ${err.message}`); - }); - } -}); - -// Funktion zum Abrufen der Anzahl aller Filme und Serien -async function fetchAllMedia() { - try { - const movies = await fetchAllMovies(); - const shows = await fetchAllShows(); // Diese Funktion musst du ebenfalls hinzufügen - - return { - movieCount: movies.length, - showCount: shows.length - }; - } catch (error) { - logError(`Error fetching all media: ${error.message}`); - throw error; - } -} - -// Funktion zum Abrufen aller Serien -async function fetchAllShows() { - try { - // Hole alle Sektionen - const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); - const sections = sectionsData.MediaContainer.Directory; - - let shows = []; - - // Hole Serien aus jeder Sektion - for (const section of sections) { - const sectionUrl = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.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; - } -} - - // Funktion zum Erstellen des Inline-Keyboard 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 -async function sendWish(wish, type, chatId) { - const message = `❗️ Achtung ❗️\n\nEin neuer ${type} Wunsch ist eingegangen:\n\nType: ${type}\n\nTitel:\n${wish}`; - - try { - await Promise.all([ - bot.sendMessage(USER1_ID, message), - bot.sendMessage(USER2_ID, message), - ]); - logMessage(`Sent ${type} wish to users ${USER1_ID} and ${USER2_ID}`); - } catch (error) { - logError(`Error sending ${type} wish: ${error.message}`); - console.error(`Error details: ${error}`); - } -} - -// 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'; - bot.sendMessage(chatId, `Du hast ${type} ausgewählt. Bitte gib den Titel des ${type} ein.`).catch(error => { - logError(`Error sending type confirmation message to chatId ${chatId}: ${error.message}`); - }); - userStates[chatId] = { type, waitingForWish: true }; // Setze den Status auf "wartend auf Wunsch" - } - - // Markiere die Callback-Abfrage als beantwortet - bot.answerCallbackQuery(query.id).catch(error => { - logError(`Error answering callback query: ${error.message}`); - }); -}); - -// Verarbeite eingehende Nachrichten -bot.on('message', async (msg) => { - const chatId = msg.chat.id; - const text = msg.text; - - if (userStates[chatId] && userStates[chatId].waitingForWish) { - // Verarbeite den Titel des Wunsches - const wish = text.trim(); // Titel erhalten - if (wish) { - const type = userStates[chatId].type; - await sendWish(wish, type, chatId); - bot.sendMessage(chatId, `Dein ${type}-Wunsch wurde übermittelt.`).catch(error => { - logError(`Error sending wish confirmation to chatId ${chatId}: ${error.message}`); - }); - logMessage(`Received and forwarded ${type} wish from chatId ${chatId}: ${wish}`); - userStates[chatId].waitingForWish = false; // Benutzerstatus zurücksetzen - } else { - bot.sendMessage(chatId, `Bitte gib den Titel des ${userStates[chatId].type} ein.`).catch(error => { - logError(`Error sending empty wish message to chatId ${chatId}: ${error.message}`); - }); - } - return; // Beende die Verarbeitung, wenn der Benutzer in der Eingabestimmung ist - } - - if (text.startsWith('/wunsch')) { - // Benutzer zur Auswahl des Typs (Film oder Serie) auffordern - bot.sendMessage(chatId, 'Möchtest du einen Film oder eine Serie wünschen? Wähle bitte eine Option:', getTypeKeyboard()).catch(error => { - logError(`Error sending type request message to chatId ${chatId}: ${error.message}`); - }); - userStates[chatId] = { waitingForType: true }; // Setze den Status auf "wartend auf Typ" - } - - if (userStates[chatId] && userStates[chatId].waitingForQuery) { - // Verarbeite Suchabfragen, falls der Benutzer darauf wartet - const query = text; // Suchbegriff erhalten - - try { - const results = await searchMovies(query); - if (results.length === 0) { - 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 - for (const movie of results) { - const { title, summary, thumb } = movie; - const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`; - - if (thumb) { - await bot.sendPhoto(chatId, thumb, { caption: message }).catch(error => { - logError(`Error sending photo to chatId ${chatId}: ${error.message}`); - }); - } else { - await bot.sendMessage(chatId, message).catch(error => { - logError(`Error sending message to chatId ${chatId}: ${error.message}`); - }); - } - } - - logMessage(`Sent search results for query "${query}" to chatId ${chatId}`); - } - } catch (error) { - if (error.response) { - bot.sendMessage(chatId, `Fehler beim Durchführen der Suche. Statuscode: ${error.response.status}`).catch(err => { - logError(`Error sending search error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`); - } else if (error.request) { - bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Keine Antwort vom Server.').catch(err => { - logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error searching movies: No response from server`); - } else { - bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Unbekannter Fehler.').catch(err => { - logError(`Error sending unknown error message to chatId ${chatId}: ${error.message}`); - }); - logError(`Error searching movies: ${error.message}`); - } - } - - // Benutzerstatus zurücksetzen - userStates[chatId].waitingForQuery = false; - } -}); - -// /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}`; - - // Bild anzeigen, wenn vorhanden - if (movieThumb) { - bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { - logError(`Error sending photo to chatId ${chatId}: ${error.message}`); - }); - } else { - bot.sendMessage(chatId, message).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) { - 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 - for (const movie of results) { - const { title, summary, thumb } = movie; - const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`; - - if (thumb) { - await bot.sendPhoto(chatId, thumb, { caption: message }).catch(error => { - logError(`Error sending photo to chatId ${chatId}: ${error.message}`); - }); - } else { - await bot.sendMessage(chatId, message).catch(error => { - logError(`Error sending message to chatId ${chatId}: ${error.message}`); - }); - } - } - - logMessage(`Sent search results for query "${query}" to chatId ${chatId}`); - } - } catch (error) { - if (error.response) { - bot.sendMessage(chatId, `Fehler beim Durchführen der Suche. Statuscode: ${error.response.status}`).catch(err => { - logError(`Error sending search error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`); - } else if (error.request) { - bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Keine Antwort vom Server.').catch(err => { - logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error searching movies: No response from server`); - } else { - bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Unbekannter Fehler.').catch(err => { - logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error searching movies: ${error.message}`); - } - } - - // Benutzerstatus zurücksetzen - userStates[chatId].waitingForQuery = false; - } -}); - -// Funktion zum Abrufen der gut bewerteten Filme -async function fetchTopRatedMovies() { - try { - const movies = await fetchAllMovies(); - if (!movies.length) return []; - - // Filtere Filme mit Bewertung (hier als Beispiel angenommen, dass die Bewertung in `rating` vorhanden ist) - const ratedMovies = movies.filter(movie => movie.rating && movie.rating > 0); - - // Sortiere Filme nach Bewertung (absteigend) - 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 -async function fetchDailyRecommendation() { - try { - const ratedMovies = await fetchTopRatedMovies(); - if (ratedMovies.length === 0) return null; - - // Heute ist der Index der zu zeigende Film - const todayIndex = new Date().getDate() % ratedMovies.length; - return ratedMovies[todayIndex]; - } catch (error) { - logError(`Error fetching daily recommendation: ${error.message}`); - throw error; - } -} - -// /empfehlung-Befehl verarbeiten -bot.onText(/\/empfehlung/, async (msg) => { - const chatId = msg.chat.id; - - try { - const dailyMovie = await fetchDailyRecommendation(); - if (dailyMovie) { - const movieTitle = dailyMovie.title || 'Unbekannt'; - const movieSummary = dailyMovie.summary || 'Keine Zusammenfassung verfügbar'; - const movieThumb = dailyMovie.thumb ? `${PLEX_DOMAIN}${dailyMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : ''; - - const message = `Hier ist der empfohlene Film des Tages:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`; - - // Bild anzeigen, wenn vorhanden - if (movieThumb) { - bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { - logError(`Error sending photo to chatId ${chatId}: ${error.message}`); - }); - } else { - bot.sendMessage(chatId, message).catch(error => { - logError(`Error sending message to chatId ${chatId}: ${error.message}`); - }); - } - - logMessage(`Sent daily recommendation to chatId ${chatId}`); - } else { - 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) { - if (error.response) { - bot.sendMessage(chatId, `Fehler beim Abrufen der Empfehlung. Statuscode: ${error.response.status}`).catch(err => { - logError(`Error sending error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching daily recommendation: ${error.response.status} - ${error.response.statusText}`); - } else if (error.request) { - bot.sendMessage(chatId, 'Fehler beim Abrufen der Empfehlung. Keine Antwort vom Server.').catch(err => { - logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching daily recommendation: No response from server`); - } else { - bot.sendMessage(chatId, 'Fehler beim Abrufen der Empfehlung. Unbekannter Fehler.').catch(err => { - logError(`Error sending unknown error message to chatId ${chatId}: ${error.message}`); - }); - logError(`Error fetching daily recommendation: ${error.message}`); - } - } -}); - -// /help-Befehl verarbeiten -bot.onText(/\/help/, (msg) => { - const chatId = msg.chat.id; - - const helpMessage = `📜 **Hier ist eine Liste der verfügbaren Befehle:**\n\n` + - `👋 **/start** - Registriert deinen Zugang.\n\n` + - `🎬 **/latestmovie** - Zeigt den zuletzt hinzugefügten Film an.\n\n` + - `📅 **/latest10movies** - Zeigt die letzten 10 hinzugefügten Filme an.\n\n` + - `ℹ️ **/info** - Gibt die Anzahl der Filme und Serien aus.\n\n` + - `🎲 **/zufall** - Zeigt einen zufälligen Film an.\n\n` + - `🔍 **/search** - Startet die Filmsuche. \n\n` + - `💭 **/wunsch** - Nutze diesen Befehl, um einen Filmwunsch zu äußern. \n\n` + - `🔝 **/empfehlung** - Film Empfehlung des Tages.\n\n` + - `❓ **/help** - Zeigt diese Hilfennachricht an.`; - - - bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown' }).catch(error => { - logError(`Error sending help message to chatId ${chatId}: ${error.message}`); - }); - - logMessage(`Sent help message to chatId ${chatId}`); -}); - -// 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; - } -} - -// /latest10movies-Befehl verarbeiten -bot.onText(/\/latest10movies/, async (msg) => { - const chatId = msg.chat.id; - - try { - const latestMovies = await fetchLatest10Movies(); - - if (latestMovies.length > 0) { - let message = 'Letzten 10 hinzugefügten Filme:\n\n'; - latestMovies.forEach(movie => { - message += `${movie.title || 'Unbekannt'}\n`; - }); - - bot.sendMessage(chatId, message).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) { - if (error.response) { - bot.sendMessage(chatId, `Fehler beim Abrufen der letzten Filme. Statuscode: ${error.response.status}`).catch(err => { - logError(`Error sending error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching latest 10 movies: ${error.response.status} - ${error.response.statusText}`); - } else if (error.request) { - bot.sendMessage(chatId, 'Fehler beim Abrufen der letzten Filme. Keine Antwort vom Server.').catch(err => { - logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching latest 10 movies: No response from server`); - } else { - bot.sendMessage(chatId, 'Fehler beim Abrufen der letzten Filme. Unbekannter Fehler.').catch(err => { - logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`); - }); - logError(`Error fetching latest 10 movies: ${error.message}`); - } - } -}); - -// Funktion zum Verarbeiten von Webhook-Anfragen -app.post('/webhook', async (req, res) => { - try { - const event = req.body; - - // Ereignisprotokoll für Debugging - logDebug(`Received webhook event: ${JSON.stringify(event, null, 2)}`); - - if (event.type === 'library.new' && event.Metadata) { - const addedMovie = event.Metadata; - const movieTitle = addedMovie.title || 'Unbekannt'; - const message = `Ein neuer Film wurde hinzugefügt:\n\nTitel: ${movieTitle}`; - - // Nachricht an alle Benutzer senden - const users = yaml.load(USER_YML_PATH); - const sendMessages = Object.keys(users).map(chatId => - bot.sendMessage(chatId, message).catch(error => { - logError(`Error sending message to chatId ${chatId}: ${error.message}`); - }) - ); - - // Warte, bis alle Nachrichten gesendet wurden - await Promise.all(sendMessages); - logMessage(`Sent new movie message to all users`); - } else { - logDebug(`Unhandled event type or missing metadata: ${event.type}`); - } - - res.sendStatus(200); // Empfang der Anfrage bestätigen - } catch (error) { - logError(`Error processing webhook: ${error.message}`); - res.sendStatus(500); // Serverfehler - } -}); - -// 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...'); +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'); // Importiere das Plugin +const express = require('express'); +const bodyParser = require('body-parser'); +const NodeCache = require('node-cache'); +const schedule = require('node-schedule'); +const moment = require('moment'); + +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; + +// 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); +} + +// /start-Befehl verarbeiten +bot.onText(/\/start/, (msg) => { + const chatId = msg.chat.id; + const userId = msg.from.id; + + // Benutzerdaten in user.yml speichern + let users = yaml.load(USER_YML_PATH); + users[chatId] = { userId: userId, notifications: true }; // Standardmäßig Benachrichtigungen aktiviert + fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); + + const welcomeMessage = ` +Willkommen! Dein Zugang zum Bot wurde erfolgreich eingerichtet. + +Um die verfügbaren Befehle anzuzeigen, tippe /help. + `; + bot.sendMessage(chatId, welcomeMessage); + + // /start-Befehl protokollieren + logMessage(`Received /start command from chatId ${chatId} (userId ${userId})`); +}); + +// /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.'); + } +}); + +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() { + const movies = await fetchLatestMovies(); + + if (movies.length > 0) { + const latestMovie = movies[0]; + + if (!lastAddedMovieTime || dayjs.unix(latestMovie.addedAt).isAfter(lastAddedMovieTime)) { + // Neuer Film hinzugefügt + lastAddedMovieTime = dayjs.unix(latestMovie.addedAt); + + 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`); + } + } +} + +// Plane die Überprüfung alle 5 Minuten +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}` : ''; + + const message = `Der zuletzt hinzugefügte Film ist:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}\n\nHinzugefügt am: ${addedAtDate}`; + + // Bild anzeigen, wenn vorhanden + if (movieThumb) { + bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { + logError(`Error sending photo to chatId ${chatId}: ${error.message}`); + }); + } else { + bot.sendMessage(chatId, message).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 { + bot.sendMessage(chatId, 'Fehler beim Abrufen der neuesten Filme. Unbekannter Fehler.').catch(err => { + logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`); + }); + logError(`Error fetching latest movie: ${error.message}`); + } + } +}); + + + + + +// /info-Befehl verarbeiten +bot.onText(/\/info/, async (msg) => { + const chatId = msg.chat.id; + const plexDomain = process.env.PLEX_DOMAIN; + + try { + const { + movieCount, + showCount, + episodeCount, + seasonCount, + topGenre, + totalSize, + oldestMovie, + newestMovie + } = await fetchAllMedia(); + + const message = `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: ${totalSize}\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}`); + }); + + 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 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_LIBRARY_URL); + const sections = sectionsData.MediaContainer.Directory; + + let movies = []; + + for (const section of sections) { + const sectionUrl = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.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_LIBRARY_URL); + const sections = sectionsData.MediaContainer.Directory; + + let shows = []; + + for (const section of sections) { + const sectionUrl = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.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; + } +} + +// 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; + } +} + + + + + + + + + // Funktion zum Erstellen des Inline-Keyboard 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 +async function sendWish(wish, type, chatId) { + const message = `❗️ Achtung ❗️\n\nEin neuer ${type} Wunsch ist eingegangen:\n\nType: ${type}\n\nTitel:\n${wish}`; + + try { + await Promise.all([ + bot.sendMessage(USER1_ID, message), + bot.sendMessage(USER2_ID, message), + ]); + logMessage(`Sent ${type} wish to users ${USER1_ID} and ${USER2_ID}`); + } catch (error) { + logError(`Error sending ${type} wish: ${error.message}`); + console.error(`Error details: ${error}`); + } +} + +// 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'; + bot.sendMessage(chatId, `Du hast ${type} ausgewählt. Bitte gib den Titel des ${type} ein.`).catch(error => { + logError(`Error sending type confirmation message to chatId ${chatId}: ${error.message}`); + }); + userStates[chatId] = { type, waitingForWish: true }; // Setze den Status auf "wartend auf Wunsch" + } + + // Markiere die Callback-Abfrage als beantwortet + bot.answerCallbackQuery(query.id).catch(error => { + logError(`Error answering callback query: ${error.message}`); + }); +}); + +// Verarbeite eingehende Nachrichten +bot.on('message', async (msg) => { + const chatId = msg.chat.id; + const text = msg.text; + + if (userStates[chatId] && userStates[chatId].waitingForWish) { + // Verarbeite den Titel des Wunsches + const wish = text.trim(); // Titel erhalten + if (wish) { + const type = userStates[chatId].type; + await sendWish(wish, type, chatId); + bot.sendMessage(chatId, `Dein ${type}-Wunsch wurde übermittelt.`).catch(error => { + logError(`Error sending wish confirmation to chatId ${chatId}: ${error.message}`); + }); + logMessage(`Received and forwarded ${type} wish from chatId ${chatId}: ${wish}`); + userStates[chatId].waitingForWish = false; // Benutzerstatus zurücksetzen + } else { + bot.sendMessage(chatId, `Bitte gib den Titel des ${userStates[chatId].type} ein.`).catch(error => { + logError(`Error sending empty wish message to chatId ${chatId}: ${error.message}`); + }); + } + return; // Beende die Verarbeitung, wenn der Benutzer in der Eingabestimmung ist + } + + if (text.startsWith('/wunsch')) { + // Benutzer zur Auswahl des Typs (Film oder Serie) auffordern + bot.sendMessage(chatId, 'Möchtest du einen Film oder eine Serie wünschen? Wähle bitte eine Option:', getTypeKeyboard()).catch(error => { + logError(`Error sending type request message to chatId ${chatId}: ${error.message}`); + }); + userStates[chatId] = { waitingForType: true }; // Setze den Status auf "wartend auf Typ" + } + + if (userStates[chatId] && userStates[chatId].waitingForQuery) { + // Verarbeite Suchabfragen, falls der Benutzer darauf wartet + const query = text; // Suchbegriff erhalten + + try { + const results = await searchMovies(query); + if (results.length === 0) { + 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 + for (const movie of results) { + const { title, summary, thumb } = movie; + const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`; + + if (thumb) { + await bot.sendPhoto(chatId, thumb, { caption: message }).catch(error => { + logError(`Error sending photo to chatId ${chatId}: ${error.message}`); + }); + } else { + await bot.sendMessage(chatId, message).catch(error => { + logError(`Error sending message to chatId ${chatId}: ${error.message}`); + }); + } + } + + logMessage(`Sent search results for query "${query}" to chatId ${chatId}`); + } + } catch (error) { + if (error.response) { + bot.sendMessage(chatId, `Fehler beim Durchführen der Suche. Statuscode: ${error.response.status}`).catch(err => { + logError(`Error sending search error message to chatId ${chatId}: ${err.message}`); + }); + logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`); + } else if (error.request) { + bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Keine Antwort vom Server.').catch(err => { + logError(`Error sending no response message to chatId ${chatId}: ${err.message}`); + }); + logError(`Error searching movies: No response from server`); + } else { + logError(`Error searching movies: ${error.message}`); + } + } + + // Benutzerstatus zurücksetzen + userStates[chatId].waitingForQuery = false; + } +}); + +// /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}`; + + // Bild anzeigen, wenn vorhanden + if (movieThumb) { + bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { + logError(`Error sending photo to chatId ${chatId}: ${error.message}`); + }); + } else { + bot.sendMessage(chatId, message).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; + } +} + +// /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}` : ''; + + const message = `Hier ist der empfohlene Film des Tages:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`; + + // Bild anzeigen, wenn vorhanden + if (movieThumb) { + await bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => { + logError(`Error sending photo to chatId ${chatId}: ${error.message}`); + }); + } else { + await bot.sendMessage(chatId, message).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}`); + }); + } +}); + + +// /help-Befehl verarbeiten +bot.onText(/\/help/, (msg) => { + const chatId = msg.chat.id; + + const helpMessage = `📜 **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` + + `🎬 /latestmovie - Zeigt den zuletzt hinzugefügten Film an.\n\n` + + `📅 /latest10movies - Zeigt die letzten 10 hinzugefügten Filme an.\n\n` + + `ℹ️ /info - Gibt die Anzahl der Filme und Serien aus.\n\n` + + `🎲 /zufall - Zeigt einen zufälligen Film an.\n\n` + + `🔍 /search - Startet die Filmsuche.\n\n` + + `💭 /wunsch - Nutze diesen Befehl, um einen Filmwunsch zu äußern.\n\n` + + `🔝 /empfehlung - Film Empfehlung des Tages.\n\n` + + `❓ /help - Zeigt diese Hilfennachricht an.`; + + bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown' }).catch(error => { + logError(`Error sending help message to chatId ${chatId}: ${error.message}`); + }); + + logMessage(`Sent help message to chatId ${chatId}`); +}); + + +// 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; + } +} + +// /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); + } +}); + +// 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) { + const movieDetails = ` +🎬 *Titel*: ${selectedMovie.title || 'Unbekannt'}\n\n +📝 *Zusammenfassung*: \n${selectedMovie.summary || 'Keine Zusammenfassung verfügbar.'}\n\n +📅 *Hinzugefügt am*: ${dayjs(selectedMovie.addedAt * 1000).format('DD.MM.YYYY')} + `; + + 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}`); + } +} + + + + +// Funktion zum Verarbeiten von Webhook-Anfragen +app.post('/mywebhook', async (req, res) => { + try { + const event = req.body; + logDebug(`Received webhook event: ${JSON.stringify(event, null, 2)}`); + + if (event.type === 'library.new' && event.Metadata) { + const addedMovie = event.Metadata; + const movieTitle = addedMovie.title || 'Unbekannt'; + const message = `Ein neuer Film wurde hinzugefügt:\n\nTitel: ${movieTitle}`; + + const users = yaml.load(USER_YML_PATH); + const sendMessages = Object.keys(users).map(chatId => + bot.sendMessage(chatId, message).catch(error => { + logError(`Error sending message to chatId ${chatId}: ${error.message}`); + }) + ); + + await Promise.all(sendMessages); + logMessage(`Sent new movie message to all users`); + } else { + logDebug(`Unhandled event type or missing metadata: ${event.type}`); + } + + res.sendStatus(200); + } catch (error) { + logError(`Error processing webhook: ${error.message}`); + res.sendStatus(500); + } +}); + + +// 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...');