Plex-Telegram-Bot/bot.js

2250 lines
80 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 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;
}
}
// /empfehlung-Befehl verarbeiten
bot.onText(/\/empfehlung/, async (msg) => {
const chatId = msg.chat.id;
try {
const dailyMovie = await fetchDailyRecommendation();
if (dailyMovie) {
// Film empfehlen
const movieTitle = dailyMovie.title || 'Unbekannt';
const movieSummary = dailyMovie.summary || 'Keine Zusammenfassung verfügbar';
const movieThumb = dailyMovie.thumb ? `${process.env.PLEX_DOMAIN}${dailyMovie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : '';
const message = `Hier ist der empfohlene Film des Tages:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}`;
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
await bot.sendPhoto(chatId, movieThumb, { caption: message }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
await bot.sendMessage(chatId, message).catch(error => {
logError(`Error sending message to chatId ${chatId}: ${error.message}`);
});
}
logMessage(`Sent daily recommendation to chatId ${chatId}`);
} else {
await bot.sendMessage(chatId, 'Keine Empfehlungen verfügbar.').catch(error => {
logError(`Error sending no recommendation message to chatId ${chatId}: ${error.message}`);
});
logMessage(`No daily recommendation found for chatId ${chatId}`);
}
} catch (error) {
let errorMessage = 'Fehler beim Abrufen der Empfehlung.';
if (error.response) {
errorMessage += ` Statuscode: ${error.response.status}`;
logError(`Error fetching daily recommendation: ${error.response.status} - ${error.response.statusText}`);
} else if (error.request) {
errorMessage += ' Keine Antwort vom Server.';
logError(`Error fetching daily recommendation: No response from server`);
} else {
errorMessage += ` Unbekannter Fehler: ${error.message}`;
logError(`Error fetching daily recommendation: ${error.message}`);
}
await bot.sendMessage(chatId, errorMessage).catch(err => {
logError(`Error sending daily recommendation error message to chatId ${chatId}: ${err.message}`);
});
}
});
// 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...');