From cb6f920a77bd6ecc566d369a44493f306c4a4d24 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 18 Aug 2024 14:18:05 +0000 Subject: [PATCH] bot.js aktualisiert --- bot.js | 961 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 906 insertions(+), 55 deletions(-) diff --git a/bot.js b/bot.js index 1933672..f8ccbaf 100644 --- a/bot.js +++ b/bot.js @@ -31,6 +31,7 @@ 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; // Debug-Ausgaben für Pfade console.log('USER_YML_PATH:', USER_YML_PATH); @@ -269,27 +270,601 @@ function logError(error) { 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.0.0"; // 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, 20); + + 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, notifications: true }; // Standardmäßig Benachrichtigungen aktiviert + users[chatId] = { userId: userId, username: username, 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. +Willkommen ${username}, +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})`); + 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; @@ -339,61 +914,73 @@ async function fetchLatestMovies() { } } + + // Funktion zum Überprüfen und Benachrichtigen über neue Filme async function checkForNewMovies() { - const movies = await fetchLatestMovies(); + try { + const movies = await fetchLatest10Movies(); // Verwende fetchLatest10Movies, um die letzten 10 Filme zu erhalten - if (movies.length > 0) { - const latestMovie = movies[0]; + 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); + 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}` : ''; + 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; + // 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 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}`); - }); + 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); + }).filter(promise => promise !== undefined); - await Promise.all(sendMessages); - console.log(`Sent new movie message to all users`); + 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}`); } } -// Plane die Überprüfung alle 5 Minuten +// 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; @@ -451,9 +1038,11 @@ bot.onText(/\/latestmovie/, async (msg) => { // /info-Befehl verarbeiten bot.onText(/\/info/, async (msg) => { const chatId = msg.chat.id; - const plexDomain = process.env.PLEX_DOMAIN; + const plexDomain = PLEX_DOMAIN; try { + // Überprüfe den Serverstatus + const serverStatus = await checkServerStatus(); const { movieCount, showCount, @@ -465,7 +1054,13 @@ bot.onText(/\/info/, async (msg) => { newestMovie } = await fetchAllMedia(); - const message = `In der Bibliothek befinden sich derzeit:\n\n` + + // 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` + @@ -476,6 +1071,8 @@ bot.onText(/\/info/, async (msg) => { `🆕 Neuester Film: ${newestMovie.title} (${newestMovie.year})\n\n\n` + `© 2024 M_Viper`; + + const options = { reply_markup: JSON.stringify({ inline_keyboard: [ @@ -497,6 +1094,32 @@ bot.onText(/\/info/, async (msg) => { } }); +// 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 { @@ -603,13 +1226,13 @@ function findNewestMedia(mediaArray) { // Funktion zum Abrufen aller Filme async function fetchAllMovies() { try { - const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); + 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 = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.PLEX_TOKEN}`; + 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) { @@ -628,13 +1251,13 @@ async function fetchAllMovies() { // Funktion zum Abrufen aller Serien async function fetchAllShows() { try { - const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL); + 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 = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.PLEX_TOKEN}`; + 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) { @@ -650,6 +1273,24 @@ async function fetchAllShows() { } } +// 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 { @@ -998,6 +1639,30 @@ async function fetchDailyRecommendation() { } } +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; + } +} + // /empfehlung-Befehl verarbeiten bot.onText(/\/empfehlung/, async (msg) => { const chatId = msg.chat.id; @@ -1050,23 +1715,196 @@ bot.onText(/\/empfehlung/, async (msg) => { } }); +// Session-Management für Feedback +const feedbackSessions = {}; -// /help-Befehl verarbeiten -bot.onText(/\/help/, (msg) => { +// 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:\nVon 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}`); + }); + }); +} + +// Handler für den /feedback Befehl +bot.onText(/\/feedback/, (msg) => { const chatId = msg.chat.id; - const helpMessage = `📜 **Hier ist eine Liste der verfügbaren Befehle:**\n\n` + + // 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: new Date().toISOString() }); + 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]; + } +}); + +const logDir = path.join(__dirname, 'Log'); +const errorLogPath = path.join(logDir, 'error.log'); + +// Fehlerprotokollierungsfunktion +function logError(error) { + const errorMessage = `${new Date().toISOString()} - Error: ${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()); +} + +// Handler für den /f_log Befehl +bot.onText(/\/f_log/, (msg) => { + const chatId = msg.chat.id; + const userId = msg.from.id; + + // Überprüfen, ob der Benutzer autorisiert ist + if (isUserAuthorized(userId)) { + try { + // Überprüfen, ob die Feedback-Datei existiert + if (fs.existsSync(feedbackFilePath)) { + // Pfad für die temporäre Textdatei + const tempFilePath = path.join(__dirname, 'feedback_log.txt'); + + // Konvertiere die YAML-Datei in ein Textformat + const feedbackData = fs.readFileSync(feedbackFilePath, 'utf8'); + + // Speichern der Feedback-Daten als .txt-Datei + fs.writeFileSync(tempFilePath, feedbackData); + + // Senden der .txt-Datei an den Benutzer + bot.sendDocument(chatId, tempFilePath).then(() => { + // Löschen der temporären Datei nach dem Senden + 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` + - `❓ /help - Zeigt diese Hilfennachricht an.`; + `💬 /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 - Zeigt Verwaltungsbefehle.\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}`); @@ -1076,6 +1914,23 @@ bot.onText(/\/help/, (msg) => { }); +// 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 { @@ -1100,7 +1955,7 @@ bot.onText(/\/latest10movies/, async (msg) => { const latestMovies = await fetchLatest10Movies(); if (latestMovies.length > 0) { - const numberEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']; + 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'; @@ -1194,9 +2049,6 @@ function handleError(chatId, error) { } } - - - // Funktion zum Verarbeiten von Webhook-Anfragen app.post('/mywebhook', async (req, res) => { try { @@ -1228,7 +2080,6 @@ app.post('/mywebhook', async (req, res) => { } }); - // Express-Server starten app.listen(PORT, () => { console.log(`Webhook server running on port ${PORT}`); @@ -1263,4 +2114,4 @@ function scheduleDailyRotation() { // Starte die tägliche Rotation scheduleDailyRotation(); -console.log('Bot is running...'); +console.log('Bot is running...'); \ No newline at end of file