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; 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); } // 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}`; // Formatieren des Hinzufügungsdatums const addedDate = addedAt ? dayjs(addedAt * 1000).format('DD.MM.YYYY') : 'Unbekannt'; // Umrechnung von Unix-Zeitstempel in Millisekunden const caption = `📺 *Titel:* ${title}\n\n` + `📝 *Beschreibung:* \n${summary}\n\n` + `📅 *Hinzugefügt am:* ${addedDate}`; bot.sendPhoto(chatId, imageUrl, { caption, parse_mode: 'Markdown' }); } 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}`); } } }); // Umgebungsvariable für die Chat-ID der Entwickler const DEV_CHAT_ID = parseInt(process.env.DEV_CHAT_ID, 10); // 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' }] ] } }; } // 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; 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:'))) { 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; const messageType = msg.reply_to_message.text.includes('Funktionswunsch') ? 'Funktionswunsch' : 'Bug'; const devMessage = `📩 *${messageType}*\n\n` + `von: ${userName} (${userId})\n\n` + `"${text}"`; try { console.log('Sende Nachricht an Entwickler-Chat-ID:', DEV_CHAT_ID); // Debugging-Ausgabe await bot.sendMessage(DEV_CHAT_ID, devMessage, { parse_mode: 'Markdown' }); console.log('Nachricht erfolgreich gesendet.'); bot.sendMessage(chatId, '✅ Ihre Nachricht wurde erfolgreich gesendet! Vielen Dank für Ihr Feedback.'); } catch (error) { console.error('Fehler beim Senden der Nachricht:', error); bot.sendMessage(chatId, '🚫 Etwas ist schiefgelaufen. Ihre Nachricht konnte nicht gesendet werden.'); } } }); // Handler für den /bot-Befehl bot.onText(/\/bot/, (msg) => { const chatId = msg.chat.id; const userId = msg.from.id; // Überprüfe, ob der Benutzer autorisiert ist if (userId.toString() === USER1_ID || userId.toString() === USER2_ID) { try { // Bot-Version const botVersion = "1.5.2"; // Hier kannst du die tatsächliche Version dynamisch einfügen // 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 const users = yaml.load(USER_YML_PATH); const userCount = Object.keys(users).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 `; // 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 🔄 *Letzter Neustart:* ${lastRestart} \n 💾 *Speicherbelegung:* ${memoryStats} \n 🔑 *Bot Token:* ${botToken.slice(0, 10)}... (gekürzt 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()} `; // Nachricht senden bot.sendMessage(chatId, infoMessage, { parse_mode: 'Markdown' }).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}`); }); } } else { bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.'); } }); // 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' // Benutzerdaten in user.yml speichern let users = yaml.load(USER_YML_PATH); users[chatId] = { userId: userId, username: username, notifications: false }; // Standardmäßig Benachrichtigungen deaktiviert fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4)); 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 deaktiviert. Um sie zu aktivieren, tippe 👉 /notification_on. `; bot.sendMessage(chatId, welcomeMessage); // /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.'); } }); 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}` : ''; 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 { 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; } } // 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 Wunsch ist eingegangen:\n\n🔹 **Typ:** ${type}\n\n🔹 **Titel:**\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; } } async function fetchRandomKidsMovie() { try { const movies = await fetchAllMovies(); const kidsGenres = ["Kids", "Family", "Kinderfilme", "Animation", "Cartoon", "Familienfilm"]; // Filter movies by checking if any of the genres match the kidsGenres list const kidsMovies = movies.filter(movie => movie.Genre && movie.Genre.some(genre => kidsGenres.includes(genre.tag)) ); if (kidsMovies.length > 0) { // Select a random movie from the filtered list const randomIndex = Math.floor(Math.random() * kidsMovies.length); return kidsMovies[randomIndex]; } else { logError("No kids movies found with the specified genres."); return null; } } catch (error) { logError(`Error fetching random kids movie: ${error.message}`); throw error; } } // 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); // Bild anzeigen, wenn vorhanden if (movieThumb) { await bot.sendPhoto(chatId, movieThumb, { caption: message, parse_mode: 'Markdown' }).catch(error => { logError(`Error sending photo to chatId ${chatId}: ${error.message}`); }); } else { await bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }).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 Hilfennachricht function createHelpMessage(chatId) { // Basis-Hilfe-Nachricht für alle Benutzer let 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` + `📺 /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` + `ℹ️ /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` + `⭐ /top\\_rated - Zeigt die am besten bewerteten Filme an.\n\n` + `💭 /wunsch - Nutze diesen Befehl, um einen Filmwunsch zu äußern.\n\n` + `🔝 /empfehlung - Film Empfehlung des Tages.\n\n` + `💬 /feedback - Gib Feedback zum Bot.\n\n` + `🔧 /dev - Funktionswunsch oder Bug melden. \n\n` + `❓ /help - Zeigt diese Hilfennachricht an. \n\n\n`; const additionalCommands = `*👨‍💻 Admin Befehle* \n\n` + `🤖 /bot - Zeigt Informationen über den Bot.\n\n` + `🛠️ /admin - sendet eine Nachricht an alle Nutzer.\n\n` + `👤 /user - Zeigt Benutzerinformationen an.\n\n` + `📝 /logs - Zeigt die letzten Fehlermeldungen an.\n\n` + `🗑️ /log\\_delete - Löscht Logs.\n\n` + `📝 /f\\_log - Sendet die Feedback als .txt-Datei.`; // Debug-Ausgaben zur Überprüfung console.log(`Received chatId: ${chatId}`); console.log(`Configured USER1_ID: ${USER1_ID}`); console.log(`Configured USER2_ID: ${USER2_ID}`); // Nur Benutzer in der .env-Datei erhalten die zusätzlichen Befehle if (chatId.toString() === USER1_ID || chatId.toString() === USER2_ID) { helpMessage += additionalCommands; } return helpMessage; } // /help-Befehl verarbeiten bot.onText(/\/help/, (msg) => { const chatId = msg.chat.id; const helpMessage = createHelpMessage(chatId); 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; } } // 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); } }); // 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}`); } } // 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...');