Plex-Telegram-Bot/bot.js

833 lines
32 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require('dotenv').config();
const TelegramBot = require('node-telegram-bot-api');
const axios = require('axios');
const fs = require('fs');
const yaml = require('yamljs');
const path = require('path');
const { format } = require('date-fns');
const express = require('express');
const bodyParser = require('body-parser');
// Setze PROJECT_ROOT auf das aktuelle Verzeichnis
const PROJECT_ROOT = __dirname;
// Konstanten aus .env-Datei
const BOT_TOKEN = process.env.BOT_TOKEN;
const PLEX_TOKEN = process.env.PLEX_TOKEN;
const PLEX_DOMAIN = process.env.PLEX_DOMAIN;
const PLEX_LIBRARY_URL = `${PLEX_DOMAIN}/library/sections/all?X-Plex-Token=${PLEX_TOKEN}`;
const USER_YML_PATH = path.resolve(PROJECT_ROOT, process.env.USER_YML_PATH);
const LOG_DIR = path.resolve(PROJECT_ROOT, process.env.LOG_DIR);
const ERROR_LOG_PATH = path.resolve(LOG_DIR, process.env.ERROR_LOG_PATH);
const CHANGE_DIR = path.resolve(PROJECT_ROOT, process.env.CHANGE_DIR);
const PORT = process.env.PORT;
const MAX_CHANGE_FILE_SIZE = parseInt(process.env.MAX_CHANGE_FILE_SIZE, 10);
const USER1_ID = process.env.USER1_ID;
const USER2_ID = process.env.USER2_ID;
// Debug-Ausgaben für Pfade
console.log('USER_YML_PATH:', USER_YML_PATH);
console.log('LOG_DIR:', LOG_DIR);
console.log('ERROR_LOG_PATH:', ERROR_LOG_PATH);
console.log('CHANGE_DIR:', CHANGE_DIR);
// Sicherstellen, dass Verzeichnisse und Dateien existieren
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
if (!fs.existsSync(CHANGE_DIR)) {
fs.mkdirSync(CHANGE_DIR, { recursive: true });
}
if (!fs.existsSync(USER_YML_PATH)) {
fs.writeFileSync(USER_YML_PATH, yaml.stringify({}, 4));
}
if (!fs.existsSync(ERROR_LOG_PATH)) {
fs.writeFileSync(ERROR_LOG_PATH, ''); // Leere Datei erstellen
}
// Telegram-Bot-Instanz erstellen
const bot = new TelegramBot(BOT_TOKEN, { polling: true });
// Express-Server für Webhooks
const app = express();
app.use(bodyParser.json());
// Funktion zum Protokollieren von allgemeinen Nachrichten
function logMessage(message) {
const today = format(new Date(), 'yyyy-MM-dd');
const logFilePath = path.join(LOG_DIR, `${today}.log`);
fs.appendFileSync(logFilePath, `${format(new Date(), 'HH:mm:ss')} - ${message}\n`);
}
// Funktion zur Fehlerprotokollierung
function logError(error) {
const errorMessage = `${format(new Date(), 'HH:mm:ss')} - Error: ${error}\n`;
fs.appendFileSync(ERROR_LOG_PATH, errorMessage);
}
// Funktion zum Protokollieren von Debug-Informationen in die change-Datei
function logDebug(message) {
const now = new Date();
const changeFilePath = path.join(CHANGE_DIR, `change_${format(now, 'yyyy-MM-dd_HH')}.log`);
if (fs.existsSync(changeFilePath) && fs.statSync(changeFilePath).size > MAX_CHANGE_FILE_SIZE) {
fs.renameSync(changeFilePath, path.join(CHANGE_DIR, `change_${format(now, 'yyyy-MM-dd_HH')}_old.log`));
}
fs.appendFileSync(changeFilePath, `${format(now, 'HH:mm:ss')} - Debug: ${message}\n`);
}
// Funktion zum Abrufen von Plex-Daten
async function fetchPlexData(url) {
try {
const response = await axios.get(url, {
headers: {
'X-Plex-Token': PLEX_TOKEN
}
});
return response.data;
} catch (error) {
logError(`Error fetching Plex data: ${error.message}`);
throw error;
}
}
// Funktion zum Abrufen aller Filme
async function fetchAllMovies() {
try {
// Hole alle Sektionen
const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL);
const sections = sectionsData.MediaContainer.Directory;
let movies = [];
// Hole Filme aus jeder Sektion
for (const section of sections) {
const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`;
const sectionData = await fetchPlexData(sectionUrl);
if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) {
const metadata = sectionData.MediaContainer.Metadata;
movies = movies.concat(metadata.filter(media => media.type === 'movie'));
}
}
// Sortiere Filme nach 'addedAt' Zeitstempel in absteigender Reihenfolge
movies.sort((a, b) => (b.addedAt || 0) - (a.addedAt || 0));
return movies;
} catch (error) {
logError(`Error fetching all movies: ${error.message}`);
throw error;
}
}
// Funktion zum Suchen von Filmen nach Genre, Titel und Schauspielern
async function searchMovies(query) {
try {
// Alle Filme abrufen
const movies = await fetchAllMovies();
// Suchbegriff in Kleinbuchstaben umwandeln
const searchQuery = query.toLowerCase();
// Filtere Filme basierend auf dem Suchbegriff
const results = movies.filter(movie => {
// Sicherstellen, dass wir auf vorhandene Attribute zugreifen
const title = (movie.title || '').toLowerCase();
const summary = (movie.summary || '').toLowerCase();
const genres = (movie.genres || []).map(g => g.toLowerCase()); // Genre ist eine Liste
const actors = (movie.actors || []).map(a => a.toLowerCase()); // Schauspieler ist eine Liste
// Überprüfen, ob der Suchbegriff in einem der Attribute vorkommt
return title.includes(searchQuery) ||
summary.includes(searchQuery) ||
genres.some(genre => genre.includes(searchQuery)) ||
actors.some(actor => actor.includes(searchQuery));
}).map(movie => ({
title: movie.title || 'Unbekannt',
summary: movie.summary || 'Keine Zusammenfassung verfügbar',
thumb: movie.thumb ? `${PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : '',
genres: (movie.genres || []).join(', '), // Genre-Liste in einen String umwandeln
actors: (movie.actors || []).join(', ') // Schauspieler-Liste in einen String umwandeln
}));
return results;
} catch (error) {
logError(`Error searching movies: ${error.message}`);
throw error;
}
}
// Funktion zum Abrufen eines zufälligen Films
async function fetchRandomMovie() {
try {
const movies = await fetchAllMovies();
if (movies.length === 0) return null;
const randomIndex = Math.floor(Math.random() * movies.length);
return movies[randomIndex];
} catch (error) {
logError(`Error fetching random movie: ${error.message}`);
throw error;
}
}
// /start-Befehl verarbeiten
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id;
// Benutzerdaten in user.yml speichern
let users = yaml.load(USER_YML_PATH);
users[chatId] = userId;
fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4));
const welcomeMessage = `
Willkommen! Dein Zugang zum Bot wurde erfolgreich eingerichtet.
Um die verfügbaren Befehle anzuzeigen, tippe /help.
`;
bot.sendMessage(chatId, welcomeMessage);
// /start-Befehl protokollieren
logMessage(`Received /start command from chatId ${chatId} (userId ${userId})`);
});
// /latestmovie-Befehl verarbeiten
bot.onText(/\/latestmovie/, async (msg) => {
const chatId = msg.chat.id;
try {
const movies = await fetchAllMovies();
const sortedMovies = movies
.filter(movie => movie.addedAt)
.sort((a, b) => b.addedAt - a.addedAt);
const latestMovie = sortedMovies[0];
if (latestMovie) {
const movieTitle = latestMovie.title || 'Unbekannt';
const movieSummary = latestMovie.summary || 'Keine Zusammenfassung verfügbar';
const addedAtDate = new Date((latestMovie.addedAt || 0) * 1000).toLocaleString(); // Konvertierung von Unix-Zeitstempel in lesbares Datum
const movieThumb = latestMovie.thumb ? `${PLEX_DOMAIN}${latestMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : '';
const message = `Der zuletzt hinzugefügte Film ist:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}\n\nHinzugefügt am: ${addedAtDate}`;
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
logMessage(`Sent latest movie info to chatId ${chatId}`);
} else {
bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => {
logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No movies found for chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Abrufen der neuesten Filme. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest movie: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Abrufen der neuesten Filme. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest movie: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Abrufen der neuesten Filme. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest movie: ${error.message}`);
}
}
});
// /info-Befehl verarbeiten
bot.onText(/\/info/, async (msg) => {
const chatId = msg.chat.id;
const plexDomain = process.env.PLEX_DOMAIN; // Lese die Plex-Domain aus der Umgebungsvariablen
try {
const { movieCount, showCount } = await fetchAllMedia();
const message = `In der Bibliothek befinden sich derzeit:\n\nFilme: ${movieCount}\nSerien: ${showCount}\n\n© 2024 M_Viper`;
const options = {
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Zu Plex gehen', url: plexDomain }]
]
})
};
await bot.sendMessage(chatId, message, options).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
logMessage(`Sent media count, copyright, and Plex button to chatId ${chatId}`);
} catch (error) {
logError(`Error fetching media count: ${error.message}`);
await bot.sendMessage(chatId, 'Fehler beim Abrufen der Medienanzahl.').catch(err => {
logError(`Error sending media count error message to chatId ${chatId}: ${err.message}`);
});
}
});
// Funktion zum Abrufen der Anzahl aller Filme und Serien
async function fetchAllMedia() {
try {
const movies = await fetchAllMovies();
const shows = await fetchAllShows(); // Diese Funktion musst du ebenfalls hinzufügen
return {
movieCount: movies.length,
showCount: shows.length
};
} catch (error) {
logError(`Error fetching all media: ${error.message}`);
throw error;
}
}
// Funktion zum Abrufen aller Serien
async function fetchAllShows() {
try {
// Hole alle Sektionen
const sectionsData = await fetchPlexData(PLEX_LIBRARY_URL);
const sections = sectionsData.MediaContainer.Directory;
let shows = [];
// Hole Serien aus jeder Sektion
for (const section of sections) {
const sectionUrl = `${process.env.PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${process.env.PLEX_TOKEN}`;
const sectionData = await fetchPlexData(sectionUrl);
if (sectionData.MediaContainer && sectionData.MediaContainer.Metadata) {
const metadata = sectionData.MediaContainer.Metadata;
shows = shows.concat(metadata.filter(media => media.type === 'show'));
}
}
return shows;
} catch (error) {
logError(`Error fetching all shows: ${error.message}`);
throw error;
}
}
// Funktion zum Erstellen des Inline-Keyboard für die Auswahl von Film oder Serie
function getTypeKeyboard() {
return {
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Film', callback_data: 'type_film' }],
[{ text: 'Serie', callback_data: 'type_serie' }]
]
})
};
}
// Funktion zum Senden des Wunsches an zwei Benutzer
async function sendWish(wish, type, chatId) {
const message = `❗️ Achtung ❗️\n\nEin neuer ${type} Wunsch ist eingegangen:\n\nType: ${type}\n\nTitel:\n${wish}`;
try {
await Promise.all([
bot.sendMessage(USER1_ID, message),
bot.sendMessage(USER2_ID, message),
]);
logMessage(`Sent ${type} wish to users ${USER1_ID} and ${USER2_ID}`);
} catch (error) {
logError(`Error sending ${type} wish: ${error.message}`);
console.error(`Error details: ${error}`);
}
}
// Verarbeite Callback Queries (für die Inline-Buttons)
bot.on('callback_query', async (query) => {
const chatId = query.message.chat.id;
const data = query.data;
if (data.startsWith('type_')) {
// Benutzer hat den Typ ausgewählt (Film oder Serie)
const type = data === 'type_film' ? 'Film' : 'Serie';
bot.sendMessage(chatId, `Du hast ${type} ausgewählt. Bitte gib den Titel des ${type} ein.`).catch(error => {
logError(`Error sending type confirmation message to chatId ${chatId}: ${error.message}`);
});
userStates[chatId] = { type, waitingForWish: true }; // Setze den Status auf "wartend auf Wunsch"
}
// Markiere die Callback-Abfrage als beantwortet
bot.answerCallbackQuery(query.id).catch(error => {
logError(`Error answering callback query: ${error.message}`);
});
});
// Verarbeite eingehende Nachrichten
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
if (userStates[chatId] && userStates[chatId].waitingForWish) {
// Verarbeite den Titel des Wunsches
const wish = text.trim(); // Titel erhalten
if (wish) {
const type = userStates[chatId].type;
await sendWish(wish, type, chatId);
bot.sendMessage(chatId, `Dein ${type}-Wunsch wurde übermittelt.`).catch(error => {
logError(`Error sending wish confirmation to chatId ${chatId}: ${error.message}`);
});
logMessage(`Received and forwarded ${type} wish from chatId ${chatId}: ${wish}`);
userStates[chatId].waitingForWish = false; // Benutzerstatus zurücksetzen
} else {
bot.sendMessage(chatId, `Bitte gib den Titel des ${userStates[chatId].type} ein.`).catch(error => {
logError(`Error sending empty wish message to chatId ${chatId}: ${error.message}`);
});
}
return; // Beende die Verarbeitung, wenn der Benutzer in der Eingabestimmung ist
}
if (text.startsWith('/wunsch')) {
// Benutzer zur Auswahl des Typs (Film oder Serie) auffordern
bot.sendMessage(chatId, 'Möchtest du einen Film oder eine Serie wünschen? Wähle bitte eine Option:', getTypeKeyboard()).catch(error => {
logError(`Error sending type request message to chatId ${chatId}: ${error.message}`);
});
userStates[chatId] = { waitingForType: true }; // Setze den Status auf "wartend auf Typ"
}
if (userStates[chatId] && userStates[chatId].waitingForQuery) {
// Verarbeite Suchabfragen, falls der Benutzer darauf wartet
const query = text; // Suchbegriff erhalten
try {
const results = await searchMovies(query);
if (results.length === 0) {
bot.sendMessage(chatId, 'Keine Filme gefunden, die deiner Suche entsprechen.').catch(error => {
logError(`Error sending no results message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No search results found for chatId ${chatId} with query "${query}"`);
} else {
// Erstelle Nachrichten für jedes Ergebnis
for (const movie of results) {
const { title, summary, thumb } = movie;
const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`;
if (thumb) {
await bot.sendPhoto(chatId, thumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
await bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
}
logMessage(`Sent search results for query "${query}" to chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Durchführen der Suche. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending search error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error searching movies: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${error.message}`);
});
logError(`Error searching movies: ${error.message}`);
}
}
// Benutzerstatus zurücksetzen
userStates[chatId].waitingForQuery = false;
}
});
// /zufall-Befehl verarbeiten
bot.onText(/\/zufall/, async (msg) => {
const chatId = msg.chat.id;
try {
const randomMovie = await fetchRandomMovie();
if (randomMovie) {
const movieTitle = randomMovie.title || 'Unbekannt';
const movieSummary = randomMovie.summary || 'Keine Zusammenfassung verfügbar';
const movieThumb = randomMovie.thumb ? `${PLEX_DOMAIN}${randomMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : '';
const message = `Hier ist ein zufälliger Film:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`;
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
logMessage(`Sent random movie info to chatId ${chatId}`);
} else {
bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => {
logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No movies found for chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Abrufen eines zufälligen Films. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching random movie: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Abrufen eines zufälligen Films. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching random movie: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Abrufen eines zufälligen Films. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching random movie: ${error.message}`);
}
}
});
// Speichern des Status der Benutzerinteraktionen
const userStates = {}; // Einfache In-Memory-Datenstruktur
// /search-Befehl verarbeiten
bot.onText(/\/search/, (msg) => {
const chatId = msg.chat.id;
// Setze den Status auf "wartet auf Suchbegriff"
userStates[chatId] = { waitingForQuery: true };
const message = 'Bitte gib den Suchbegriff für die Film-Suche ein.';
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending search prompt to chatId ${chatId}: ${error.message}`);
});
logMessage(`Prompted for search query from chatId ${chatId}`);
});
// Eingehende Nachrichten verarbeiten
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
// Überprüfen, ob der Benutzer auf eine Suchabfrage wartet
if (userStates[chatId] && userStates[chatId].waitingForQuery) {
const query = text; // Suchbegriff erhalten
try {
const results = await searchMovies(query);
if (results.length === 0) {
bot.sendMessage(chatId, 'Keine Filme gefunden, die deiner Suche entsprechen.').catch(error => {
logError(`Error sending no results message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No search results found for chatId ${chatId} with query "${query}"`);
} else {
// Erstelle Nachrichten für jedes Ergebnis
for (const movie of results) {
const { title, summary, thumb } = movie;
const message = `Titel: ${title}\n\nZusammenfassung: \n\n${summary}`;
if (thumb) {
await bot.sendPhoto(chatId, thumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
await bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
}
logMessage(`Sent search results for query "${query}" to chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Durchführen der Suche. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending search error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error searching movies: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error searching movies: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Durchführen der Suche. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error searching movies: ${error.message}`);
}
}
// Benutzerstatus zurücksetzen
userStates[chatId].waitingForQuery = false;
}
});
// Funktion zum Abrufen der gut bewerteten Filme
async function fetchTopRatedMovies() {
try {
const movies = await fetchAllMovies();
if (!movies.length) return [];
// Filtere Filme mit Bewertung (hier als Beispiel angenommen, dass die Bewertung in `rating` vorhanden ist)
const ratedMovies = movies.filter(movie => movie.rating && movie.rating > 0);
// Sortiere Filme nach Bewertung (absteigend)
ratedMovies.sort((a, b) => (b.rating || 0) - (a.rating || 0));
return ratedMovies;
} catch (error) {
logError(`Error fetching top-rated movies: ${error.message}`);
throw error;
}
}
// Funktion zum Abrufen des Films des Tages
async function fetchDailyRecommendation() {
try {
const ratedMovies = await fetchTopRatedMovies();
if (ratedMovies.length === 0) return null;
// Heute ist der Index der zu zeigende Film
const todayIndex = new Date().getDate() % ratedMovies.length;
return ratedMovies[todayIndex];
} catch (error) {
logError(`Error fetching daily recommendation: ${error.message}`);
throw error;
}
}
// /empfehlung-Befehl verarbeiten
bot.onText(/\/empfehlung/, async (msg) => {
const chatId = msg.chat.id;
try {
const dailyMovie = await fetchDailyRecommendation();
if (dailyMovie) {
const movieTitle = dailyMovie.title || 'Unbekannt';
const movieSummary = dailyMovie.summary || 'Keine Zusammenfassung verfügbar';
const movieThumb = dailyMovie.thumb ? `${PLEX_DOMAIN}${dailyMovie.thumb}?X-Plex-Token=${PLEX_TOKEN}` : '';
const message = `Hier ist der empfohlene Film des Tages:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`;
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
logMessage(`Sent daily recommendation to chatId ${chatId}`);
} else {
bot.sendMessage(chatId, 'Keine Empfehlungen verfügbar.').catch(error => {
logError(`Error sending no recommendation message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No daily recommendation found for chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Abrufen der Empfehlung. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching daily recommendation: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Abrufen der Empfehlung. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching daily recommendation: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Abrufen der Empfehlung. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${error.message}`);
});
logError(`Error fetching daily recommendation: ${error.message}`);
}
}
});
// /help-Befehl verarbeiten
bot.onText(/\/help/, (msg) => {
const chatId = msg.chat.id;
const helpMessage = `📜 **Hier ist eine Liste der verfügbaren Befehle:**\n\n` +
`👋 **/start** - Registriert deinen Zugang.\n\n` +
`🎬 **/latestmovie** - Zeigt den zuletzt hinzugefügten Film an.\n\n` +
`📅 **/latest10movies** - Zeigt die letzten 10 hinzugefügten Filme an.\n\n` +
` **/info** - Gibt die Anzahl der Filme und Serien aus.\n\n` +
`🎲 **/zufall** - Zeigt einen zufälligen Film an.\n\n` +
`🔍 **/search** - Startet die Filmsuche. \n\n` +
`💭 **/wunsch** - Nutze diesen Befehl, um einen Filmwunsch zu äußern. \n\n` +
`🔝 **/empfehlung** - Film Empfehlung des Tages.\n\n` +
`❓ **/help** - Zeigt diese Hilfennachricht an.`;
bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown' }).catch(error => {
logError(`Error sending help message to chatId ${chatId}: ${error.message}`);
});
logMessage(`Sent help message to chatId ${chatId}`);
});
// Funktion zum Abrufen der letzten 10 hinzugefügten Filme
async function fetchLatest10Movies() {
try {
const movies = await fetchAllMovies();
const sortedMovies = movies
.filter(movie => movie.addedAt)
.sort((a, b) => b.addedAt - a.addedAt)
.slice(0, 10); // Nimm nur die neuesten 10 Filme
return sortedMovies;
} catch (error) {
logError(`Error fetching latest 10 movies: ${error.message}`);
throw error;
}
}
// /latest10movies-Befehl verarbeiten
bot.onText(/\/latest10movies/, async (msg) => {
const chatId = msg.chat.id;
try {
const latestMovies = await fetchLatest10Movies();
if (latestMovies.length > 0) {
let message = 'Letzten 10 hinzugefügten Filme:\n\n';
latestMovies.forEach(movie => {
message += `${movie.title || 'Unbekannt'}\n`;
});
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
logMessage(`Sent latest 10 movies info to chatId ${chatId}`);
} else {
bot.sendMessage(chatId, 'Keine Filme gefunden.').catch(error => {
logError(`Error sending no movies message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No movies found for chatId ${chatId}`);
}
} catch (error) {
if (error.response) {
bot.sendMessage(chatId, `Fehler beim Abrufen der letzten Filme. Statuscode: ${error.response.status}`).catch(err => {
logError(`Error sending error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest 10 movies: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
bot.sendMessage(chatId, 'Fehler beim Abrufen der letzten Filme. Keine Antwort vom Server.').catch(err => {
logError(`Error sending no response message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest 10 movies: No response from server`);
} else {
bot.sendMessage(chatId, 'Fehler beim Abrufen der letzten Filme. Unbekannter Fehler.').catch(err => {
logError(`Error sending unknown error message to chatId ${chatId}: ${err.message}`);
});
logError(`Error fetching latest 10 movies: ${error.message}`);
}
}
});
// Funktion zum Verarbeiten von Webhook-Anfragen
app.post('/webhook', async (req, res) => {
try {
const event = req.body;
// Ereignisprotokoll für Debugging
logDebug(`Received webhook event: ${JSON.stringify(event, null, 2)}`);
if (event.type === 'library.new' && event.Metadata) {
const addedMovie = event.Metadata;
const movieTitle = addedMovie.title || 'Unbekannt';
const message = `Ein neuer Film wurde hinzugefügt:\n\nTitel: ${movieTitle}`;
// Nachricht an alle Benutzer senden
const users = yaml.load(USER_YML_PATH);
const sendMessages = Object.keys(users).map(chatId =>
bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
})
);
// Warte, bis alle Nachrichten gesendet wurden
await Promise.all(sendMessages);
logMessage(`Sent new movie message to all users`);
} else {
logDebug(`Unhandled event type or missing metadata: ${event.type}`);
}
res.sendStatus(200); // Empfang der Anfrage bestätigen
} catch (error) {
logError(`Error processing webhook: ${error.message}`);
res.sendStatus(500); // Serverfehler
}
});
// Express-Server starten
app.listen(PORT, () => {
console.log(`Webhook server running on port ${PORT}`);
});
// Log-Rotation
function rotateLogs() {
const today = format(new Date(), 'yyyy-MM-dd');
const logFilePath = path.join(LOG_DIR, `${today}.log`);
// Lösche die Log-Datei von gestern, wenn sie existiert
const yesterday = format(new Date(Date.now() - 24 * 60 * 60 * 1000), 'yyyy-MM-dd');
const oldLogFilePath = path.join(LOG_DIR, `${yesterday}.log`);
if (fs.existsSync(oldLogFilePath)) {
fs.unlinkSync(oldLogFilePath); // Lösche die alte Logdatei
logMessage(`Deleted old log file: ${yesterday}`);
}
}
// Logs täglich um Mitternacht rotieren
function scheduleDailyRotation() {
const now = new Date();
const millisTillMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0, 0) - now;
setTimeout(function() {
rotateLogs(); // Rotieren der Logs um Mitternacht
setInterval(rotateLogs, 24 * 60 * 60 * 1000); // Danach täglich wiederholen
}, millisTillMidnight);
}
// Starte die tägliche Rotation
scheduleDailyRotation();
console.log('Bot is running...');