Plex-Telegram-Bot/bot.js

2295 lines
81 KiB
JavaScript
Raw Normal View History

2024-08-15 17:40:21 +00:00
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;
2024-08-18 14:18:05 +00:00
const AUTHORIZED_USER_ID = process.env.AUTHORIZED_USER_ID;
2024-09-01 15:04:00 +00:00
const errorLogPath = process.env.ERROR_LOG_PATH;
2024-08-15 17:40:21 +00:00
// 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);
}
2024-08-18 14:18:05 +00:00
// 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
2024-09-01 15:04:00 +00:00
const botVersion = "1.5.2"; // Hier kannst du die tatsächliche Version dynamisch einfügen
2024-08-18 14:18:05 +00:00
// 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
2024-09-01 15:04:00 +00:00
const topMovies = movies.slice(0, 15);
2024-08-18 14:18:05 +00:00
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);
}
2024-08-15 17:40:21 +00:00
// /start-Befehl verarbeiten
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id;
2024-08-18 14:18:05 +00:00
const username = msg.from.username || 'Unbekannt'; // Benutzername, falls vorhanden, sonst 'Unbekannt'
2024-08-15 17:40:21 +00:00
// Benutzerdaten in user.yml speichern
let users = yaml.load(USER_YML_PATH);
2024-09-01 15:04:00 +00:00
users[chatId] = { userId: userId, username: username, notifications: false }; // Standardmäßig Benachrichtigungen deaktiviert
2024-08-15 17:40:21 +00:00
fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4));
const welcomeMessage = `
2024-09-01 15:04:00 +00:00
👋 Willkommen ${username}!
Dein Zugang zum Bot wurde erfolgreich eingerichtet.
2024-08-15 17:40:21 +00:00
2024-09-01 15:04:00 +00:00
Um die verfügbaren Befehle anzuzeigen, tippe 👉 /help.
🔔 Hinweis: Benachrichtigungen über neue Filme sind standardmäßig deaktiviert.
Um sie zu aktivieren, tippe 👉 /notification_on.
2024-08-15 17:40:21 +00:00
`;
2024-09-01 15:04:00 +00:00
bot.sendMessage(chatId, welcomeMessage);
2024-08-15 17:40:21 +00:00
// /start-Befehl protokollieren
2024-08-18 14:18:05 +00:00
logMessage(`Received /start command from chatId ${chatId} (userId ${userId}, username ${username})`);
2024-08-15 17:40:21 +00:00
});
// /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 [];
}
}
2024-08-18 14:18:05 +00:00
// 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}`);
});
}
2024-08-15 17:40:21 +00:00
}
2024-08-18 14:18:05 +00:00
}).filter(promise => promise !== undefined);
2024-08-15 17:40:21 +00:00
2024-08-18 14:18:05 +00:00
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() }));
}
2024-08-15 17:40:21 +00:00
}
2024-08-18 14:18:05 +00:00
} catch (error) {
logError(`Error checking for new movies: ${error.message}`);
2024-08-15 17:40:21 +00:00
}
}
2024-08-18 14:18:05 +00:00
// 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
2024-08-15 17:40:21 +00:00
schedule.scheduleJob('*/1 * * * *', checkForNewMovies);
// Initiale Überprüfung beim Start
checkForNewMovies();
// /latestmovie-Befehl verarbeiten
bot.onText(/\/latestmovie/, async (msg) => {
const chatId = msg.chat.id;
2024-09-01 15:04:00 +00:00
2024-08-15 17:40:21 +00:00
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;
2024-09-01 15:04:00 +00:00
const messageId = msg.message_id;
2024-08-18 14:18:05 +00:00
const plexDomain = PLEX_DOMAIN;
2024-08-15 17:40:21 +00:00
try {
2024-08-18 14:18:05 +00:00
// Überprüfe den Serverstatus
const serverStatus = await checkServerStatus();
2024-08-15 17:40:21 +00:00
const {
movieCount,
showCount,
episodeCount,
seasonCount,
topGenre,
totalSize,
oldestMovie,
newestMovie
} = await fetchAllMedia();
2024-08-18 14:18:05 +00:00
// Serverstatus Text
const serverStatusText = serverStatus
? '🟢 Server Status: Online'
: '🔴 Server Status: Offline';
const message = `${serverStatusText}\n\n` +
`*In der Bibliothek befinden sich derzeit:*\n\n` +
2024-08-15 17:40:21 +00:00
`📽️ Filme: ${movieCount}\n\n` +
`📺 Serien: ${showCount}\n\n` +
`🎞️ Episoden: ${episodeCount}\n\n` +
`📚 Staffeln: ${seasonCount}\n\n\n` +
`📊 Top-Genre: ${topGenre}\n\n` +
2024-09-01 15:04:00 +00:00
`💾 Gesamtgröße-Filme: ${totalSize}\n\n` +
`💾 Gesamtgröße-Serien: 1.70TB\n\n\n` +
2024-08-15 17:40:21 +00:00
`⏳ Ältester Film: ${oldestMovie.title} (${oldestMovie.year})\n\n` +
`🆕 Neuester Film: ${newestMovie.title} (${newestMovie.year})\n\n\n` +
`© 2024 M_Viper`;
2024-08-18 14:18:05 +00:00
2024-08-15 17:40:21 +00:00
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}`);
});
2024-09-01 15:04:00 +00:00
// Ursprüngliche Nachricht löschen (den /info-Befehl)
await bot.deleteMessage(chatId, messageId).catch(error => {
logError(`Error deleting message from chatId ${chatId}: ${error.message}`);
});
2024-08-15 17:40:21 +00:00
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}`);
});
}
});
2024-08-18 14:18:05 +00:00
// 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;
}
}
2024-08-15 17:40:21 +00:00
// 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 {
2024-08-18 14:18:05 +00:00
const sectionsData = await fetchPlexData(`${PLEX_DOMAIN}/library/sections?X-Plex-Token=${PLEX_TOKEN}`);
2024-08-15 17:40:21 +00:00
const sections = sectionsData.MediaContainer.Directory;
let movies = [];
for (const section of sections) {
2024-08-18 14:18:05 +00:00
const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`;
2024-08-15 17:40:21 +00:00
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 {
2024-08-18 14:18:05 +00:00
const sectionsData = await fetchPlexData(`${PLEX_DOMAIN}/library/sections?X-Plex-Token=${PLEX_TOKEN}`);
2024-08-15 17:40:21 +00:00
const sections = sectionsData.MediaContainer.Directory;
let shows = [];
for (const section of sections) {
2024-08-18 14:18:05 +00:00
const sectionUrl = `${PLEX_DOMAIN}/library/sections/${section.key}/all?X-Plex-Token=${PLEX_TOKEN}`;
2024-08-15 17:40:21 +00:00
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;
}
}
2024-08-18 14:18:05 +00:00
// 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}`);
}
});
}
2024-08-15 17:40:21 +00:00
// 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' }]
]
})
};
}
2024-09-01 15:04:00 +00:00
2024-08-15 17:40:21 +00:00
// Funktion zum Senden des Wunsches an zwei Benutzer
async function sendWish(wish, type, chatId) {
2024-08-15 21:37:36 +00:00
const message = `✨ **Achtung!** ✨\n\nEin neuer Wunsch ist eingegangen:\n\n🔹 **Typ:** ${type}\n\n🔹 **Titel:**\n${wish}`;
2024-08-15 17:40:21 +00:00
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;
}
}
2024-08-18 14:18:05 +00:00
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;
}
}
2024-09-06 06:26:13 +00:00
// Funktion zum Kürzen der Zusammenfassung
function truncateSummary(summary, maxLength) {
if (summary.length > maxLength) {
return summary.slice(0, maxLength) + '...'; // Kürzen und "..." hinzufügen
}
return summary;
}
// Funktion zum Erstellen der Bildunterschrift
function createCaption(title, summary) {
// Initiale Bildunterschrift ohne Kürzung
let caption = `
Hier ist der empfohlene Film des Tages:
🎬 Titel: ${title || 'Unbekannt'}
📝 Zusammenfassung:
${summary || 'Keine Zusammenfassung verfügbar'}
`;
// Überprüfen, ob die Bildunterschrift zu lang ist
if (caption.length > MAX_CAPTION_LENGTH) {
// Berechnen der maximalen Länge für die Zusammenfassung
const maxSummaryLength = MAX_CAPTION_LENGTH - (caption.length - summary.length);
// Kürzen der Zusammenfassung auf die berechnete Länge
const truncatedSummary = truncateSummary(summary, maxSummaryLength);
// Neu zusammenstellen der Bildunterschrift mit der gekürzten Zusammenfassung
caption = `
Hier ist der empfohlene Film des Tages:
🎬 Titel: ${title || 'Unbekannt'}
📝 Zusammenfassung:
${truncatedSummary}
`;
}
return caption;
}
2024-08-15 17:40:21 +00:00
// /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}` : '';
2024-09-06 06:26:13 +00:00
// Erstellen der Bildunterschrift und Kürzen, falls nötig
const message = createCaption(movieTitle, movieSummary);
2024-08-15 17:40:21 +00:00
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
2024-09-06 06:26:13 +00:00
await bot.sendPhoto(chatId, movieThumb, { caption: message, parse_mode: 'Markdown' }).catch(error => {
2024-08-15 17:40:21 +00:00
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
2024-09-06 06:26:13 +00:00
await bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }).catch(error => {
2024-08-15 17:40:21 +00:00
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}`);
});
}
});
2024-09-01 15:04:00 +00:00
2024-09-06 06:26:13 +00:00
2024-08-18 14:18:05 +00:00
// Session-Management für Feedback
const feedbackSessions = {};
2024-08-15 17:40:21 +00:00
2024-08-18 14:18:05 +00:00
// 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
2024-09-01 15:04:00 +00:00
const message = `📢 Neues Feedback:\n\n Von userId: "${userId}"\n\n"${feedback}"`;
2024-08-18 14:18:05 +00:00
adminChatIds.forEach(adminChatId => {
bot.sendMessage(adminChatId, message).catch(error => {
logError(`Fehler beim Senden von Feedback an Admin chatId ${adminChatId}: ${error.message}`);
});
});
}
2024-09-01 15:04:00 +00:00
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}`);
});
});
}
2024-08-18 14:18:05 +00:00
// Handler für den /feedback Befehl
bot.onText(/\/feedback/, (msg) => {
const chatId = msg.chat.id;
// Startet eine Feedback-Sitzung
feedbackSessions[chatId] = { waitingForFeedback: true };
2024-09-01 15:04:00 +00:00
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}`);
});
2024-08-18 14:18:05 +00:00
});
// Handler für den /cancel Befehl
bot.onText(/\/cancel/, (msg) => {
const chatId = msg.chat.id;
if (feedbackSessions[chatId]) {
delete feedbackSessions[chatId];
2024-09-01 15:04:00 +00:00
bot.sendMessage(chatId, 'Feedback wurde abgebrochen.', { parse_mode: 'Markdown' })
.catch(error => {
logError(`Fehler beim Senden der Abbruch-Nachricht an chatId ${chatId}: ${error.message}`);
});
2024-08-18 14:18:05 +00:00
}
});
// 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
2024-09-01 15:04:00 +00:00
saveFeedbackToFile({ chatId, feedback, timestamp: dayjs().format('YYYY-MM-DD HH:mm:ss') });
2024-08-18 14:18:05 +00:00
sendFeedbackToAdmins(userId, feedback);
2024-09-01 15:04:00 +00:00
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}`);
});
2024-08-18 14:18:05 +00:00
delete feedbackSessions[chatId];
}
});
2024-09-01 15:04:00 +00:00
// Beispiel zur erweiterten Fehlerbehandlung im Bot
bot.on('polling_error', (error) => {
logError(`Polling Error: ${error.code} - ${error.message}`);
});
2024-08-18 14:18:05 +00:00
// Handler für den /f_log Befehl
bot.onText(/\/f_log/, (msg) => {
2024-08-15 17:40:21 +00:00
const chatId = msg.chat.id;
2024-08-18 14:18:05 +00:00
const userId = msg.from.id;
2024-08-15 17:40:21 +00:00
2024-08-18 14:18:05 +00:00
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);
2024-09-01 15:04:00 +00:00
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}`);
});
2024-08-18 14:18:05 +00:00
});
} else {
const errMsg = `Keine Feedback-Datei gefunden unter ${feedbackFilePath}.`;
console.log(errMsg);
2024-09-01 15:04:00 +00:00
bot.sendMessage(chatId, `${errMsg}`)
.catch(error => {
logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${error.message}`);
});
2024-08-18 14:18:05 +00:00
}
} catch (error) {
logError(`Fehler beim Senden der Feedback-Log-Datei: ${error.message}`);
2024-09-01 15:04:00 +00:00
bot.sendMessage(chatId, '❌ Fehler beim Senden der Feedback-Log-Datei.')
.catch(err => {
logError(`Fehler beim Senden der Fehlermeldung an chatId ${chatId}: ${err.message}`);
});
2024-08-18 14:18:05 +00:00
}
} else {
const errMsg = `Unberechtigter Zugriff auf /f_log von userId ${userId}.`;
console.log(errMsg);
2024-09-01 15:04:00 +00:00
bot.sendMessage(chatId, `${errMsg}`)
.catch(error => {
logError(`Unberechtigter Zugriff auf /f_log von userId ${userId}: ${error.message}`);
});
2024-08-18 14:18:05 +00:00
}
});
// 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` +
2024-08-15 17:40:21 +00:00
`👋 /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` +
2024-08-18 14:18:05 +00:00
`📺 /serien - Zeigt eine Liste aller Serien an.\n\n` +
2024-08-15 17:40:21 +00:00
`🎬 /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` +
2024-08-18 14:18:05 +00:00
`⭐ /top\\_rated - Zeigt die am besten bewerteten Filme an.\n\n` +
2024-08-15 17:40:21 +00:00
`💭 /wunsch - Nutze diesen Befehl, um einen Filmwunsch zu äußern.\n\n` +
`🔝 /empfehlung - Film Empfehlung des Tages.\n\n` +
2024-08-18 14:18:05 +00:00
`💬 /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` +
2024-09-01 15:04:00 +00:00
`🛠️ /admin - sendet eine Nachricht an alle Nutzer.\n\n` +
2024-08-18 14:18:05 +00:00
`👤 /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);
2024-08-15 17:40:21 +00:00
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}`);
});
2024-08-18 14:18:05 +00:00
// 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;
}
}
2024-08-15 17:40:21 +00:00
// 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;
}
}
2024-09-01 15:04:00 +00:00
// 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;
}
2024-08-15 17:40:21 +00:00
// /latest10movies-Befehl verarbeiten
bot.onText(/\/latest10movies/, async (msg) => {
const chatId = msg.chat.id;
try {
const latestMovies = await fetchLatest10Movies();
if (latestMovies.length > 0) {
2024-09-01 15:04:00 +00:00
const numberEmojis = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟'];
2024-08-15 17:40:21 +00:00
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) {
2024-09-01 15:04:00 +00:00
// Bildunterschrift erstellen und kürzen, falls nötig
const movieDetails = createCaption(selectedMovie.title, selectedMovie.summary, selectedMovie.addedAt);
2024-08-15 17:40:21 +00:00
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}`);
}
}
2024-09-01 15:04:00 +00:00
2024-08-15 17:40:21 +00:00
// 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();
2024-08-18 14:18:05 +00:00
console.log('Bot is running...');