Plex-Telegram-Bot/plex-bot.js

5548 lines
198 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');
const express = require('express');
const bodyParser = require('body-parser');
const NodeCache = require('node-cache');
const schedule = require('node-schedule');
const moment = require('moment');
const nodemailer = require('nodemailer');
const { scheduleJob } = require('node-schedule');
const { format } = require('date-fns');
const archiver = require('archiver');
const today = format(new Date(), 'yyyy-MM-dd');
console.log(today); // Sollte das aktuelle Datum im Format yyyy-MM-dd ausgeben
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);
}
const faqFilePath = path.join(__dirname, 'faq.json'); // Pfad zur faq.json im Hauptverzeichnis
const authorizedUsers = [USER1_ID, USER2_ID];
// Funktion zum Laden der FAQs
function loadFaqs() {
if (!fs.existsSync(faqFilePath)) {
fs.writeFileSync(faqFilePath, JSON.stringify([])); // Leere Datei erstellen, wenn sie nicht existiert
}
const faqs = JSON.parse(fs.readFileSync(faqFilePath));
return faqs;
}
// Funktion zum Speichern der FAQs
function saveFaqs(faqs) {
fs.writeFileSync(faqFilePath, JSON.stringify(faqs, null, 2));
}
// Befehl zum Abrufen von Trailern
bot.onText(/\/trailer/, (msg) => {
const chatId = msg.chat.id;
// Nach dem Filmtitel fragen
bot.sendMessage(chatId, 'Bitte geben Sie den Titel des Films ein:');
// Auf die nächste Nachricht warten, die den Filmnamen enthält
bot.once('message', async (msg) => {
const filmTitle = msg.text;
try {
// YouTube API URL für die Suche
const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=${encodeURIComponent(filmTitle + ' trailer')}&key=${process.env.YOUTUBE_API_KEY}`;
const response = await axios.get(url);
const videos = response.data.items;
// Überprüfen, ob Videos gefunden wurden
if (videos.length > 0) {
const videoId = videos[0].id.videoId; // ID des ersten gefundenen Trailers
const trailerUrl = `https://www.youtube.com/watch?v=${videoId}`;
const reply = `Hier ist der Trailer für "${filmTitle}": ${trailerUrl}`;
bot.sendMessage(chatId, reply);
} else {
bot.sendMessage(chatId, `Leider konnte ich keinen Trailer für "${filmTitle}" finden.`);
}
} catch (error) {
console.error('Fehler beim Abrufen des Trailers:', error);
bot.sendMessage(chatId, 'Es gab ein Problem beim Abrufen des Trailers. Bitte versuche es später erneut.');
}
});
});
// Globale Variable zum Zählen der Passwortanforderungen
let passwordRequestCount = 0;
let passwordChangeRequired = false; // Sperrt Login, wenn Passwortänderung nötig ist
let newPassword = ''; // Speichert das neue Passwort
bot.onText(/\/passwd/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id.toString();
// Überprüfen, ob das Passwort geändert werden muss
if (passwordChangeRequired) {
const reply = `⚠️ Der Befehl /passwd ist zurzeit nicht verfügbar! Der Zugang wurde gesperrt bitte wenden dich an den Admin.`;
bot.sendMessage(chatId, reply, { parse_mode: 'HTML' });
return; // Beende die Ausführung, wenn das Passwort geändert werden muss
}
if (authorizedUsers.includes(userId)) {
// Zähler für Passwortanforderungen erhöhen
passwordRequestCount++;
// Wenn die Anzahl der Anfragen 10 erreicht, muss das Passwort geändert werden
if (passwordRequestCount >= 10) {
newPassword = generateRandomPassword(); // Zufälliges sicheres Passwort generieren
updateEnvPassword(newPassword); // Neues Passwort in der .env speichern
const changePwMessage = `⚠️ Du hast das Passwort zu oft angefordert. Der Zugang wurde gesperrt.`;
bot.sendMessage(chatId, changePwMessage, {
parse_mode: 'HTML',
protect_content: true,
}).then((sentMessage) => {
setTimeout(() => {
bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => {
console.error('Fehler beim Löschen der Nachricht:', err);
});
}, 30000); // 30 Sekunden
});
// Zähler zurücksetzen und Sperre aktivieren
passwordRequestCount = 0;
passwordChangeRequired = true; // Passwort muss geändert werden
return; // Beende die Ausführung, um das alte Passwort nicht mehr zu senden
}
// Passwort aus der .env-Datei holen
const password = process.env.ADMIN_PW;
const reply = `🔒 Das Passwort für den Adminbereich lautet:\n\n<span class="tg-spoiler">${password}</span>\n\n‼️<em>Hinweis:‼\n</em> Diese Nachricht wird automatisch in 30 Sekunden gelöscht.`;
bot.sendMessage(chatId, reply, {
parse_mode: 'HTML',
protect_content: true, // Inhalt schützen
}).then((sentMessage) => {
setTimeout(() => {
bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => {
console.error('Fehler beim Löschen der Antwortnachricht:', err);
});
}, 30000); // 30 Sekunden
});
setTimeout(() => {
bot.deleteMessage(chatId, msg.message_id).catch((err) => {
console.error('Fehler beim Löschen der ursprünglichen Nachricht:', err);
});
}, 30000); // 30 Sekunden
// Nachricht an den Dev senden
const devMessage = `🔒 Das Passwort für den Adminbereich wurde angefordert von:\n\n👤 <strong>@${msg.from.username}</strong>\n\n🆔 ID: <strong>${userId}</strong>\n\n📅 Datum: <strong>${new Date().toLocaleDateString('de-DE')}</strong>\n\n🕒 Uhrzeit: <strong>${new Date().toLocaleTimeString('de-DE')}</strong>`;
bot.sendMessage(process.env.DEV_CHAT_ID, devMessage, { parse_mode: 'HTML' }).catch((err) => {
console.error('Fehler beim Senden der Dev-Nachricht:', err);
});
} else {
const reply = `🚫 Zugriff verweigert!\nLeider hast du keine Berechtigung, diesen Befehl auszuführen.`;
bot.sendMessage(chatId, reply, {
parse_mode: 'HTML',
protect_content: true,
}).then((sentMessage) => {
setTimeout(() => {
bot.deleteMessage(chatId, sentMessage.message_id).catch((err) => {
console.error('Fehler beim Löschen der Antwortnachricht:', err);
});
}, 30000); // 30 Sekunden
});
setTimeout(() => {
bot.deleteMessage(chatId, msg.message_id).catch((err) => {
console.error('Fehler beim Löschen der ursprünglichen Nachricht:', err);
});
}, 30000); // 30 Sekunden
}
});
// /key Befehl
bot.onText(/\/key/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id.toString();
if (authorizedUsers.includes(userId)) {
if (passwordChangeRequired) {
const reply = `🔑 Neues Passwort:\n<span class="tg-spoiler">${newPassword}</span>\n\nDiese Nachricht wird in 30 Sekunden gelöscht.`;
bot.sendMessage(chatId, reply, { parse_mode: 'HTML' }).then((sentMessage) => {
setTimeout(() => {
bot.deleteMessage(chatId, sentMessage.message_id);
}, 30000);
});
setTimeout(() => {
bot.deleteMessage(chatId, msg.message_id);
}, 30000);
passwordChangeRequired = false; // /passwd wieder aktivieren
} else {
bot.sendMessage(chatId, "⚠️ Das Passwort wurde nicht geändert. Du kannst den /passwd Befehl nutzen.", { parse_mode: 'HTML' });
}
} else {
bot.sendMessage(chatId, "🚫 Zugriff verweigert!", { parse_mode: 'HTML' });
}
});
// Funktion zum Generieren eines sicheren zufälligen Passworts mit mindestens 12 Zeichen
function generateRandomPassword() {
const uppercaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lowercaseChars = 'abcdefghijklmnopqrstuvwxyz';
const digits = '0123456789';
const specialChars = '@$!+-*&';
const allChars = uppercaseChars + lowercaseChars + digits + specialChars;
let password = '';
// Stelle sicher, dass das Passwort mindestens je einen Großbuchstaben, Kleinbuchstaben, Zahl und Sonderzeichen enthält
password += uppercaseChars.charAt(Math.floor(Math.random() * uppercaseChars.length));
password += lowercaseChars.charAt(Math.floor(Math.random() * lowercaseChars.length));
password += digits.charAt(Math.floor(Math.random() * digits.length));
password += specialChars.charAt(Math.floor(Math.random() * specialChars.length));
// Fülle das restliche Passwort mit zufälligen Zeichen auf, um 12 Zeichen zu erreichen
for (let i = 4; i < 12; i++) {
password += allChars.charAt(Math.floor(Math.random() * allChars.length));
}
// Das Passwort zufällig durchmischen
return shuffleString(password);
}
// Funktion zum Mischen eines Strings (optional, um die Zeichen zufällig anzuordnen)
function shuffleString(string) {
const array = string.split('');
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array.join('');
}
// Funktion zum Aktualisieren des Passworts in der .env-Datei
function updateEnvPassword(newPassword) {
const envFilePath = path.resolve(__dirname, '.env');
// Die aktuelle .env-Datei lesen
let envContent = fs.readFileSync(envFilePath, 'utf-8');
// Das Passwort in der Datei ändern
envContent = envContent.replace(/ADMIN_PW=.*/, `ADMIN_PW=${newPassword}`);
// Die geänderte Datei speichern
fs.writeFileSync(envFilePath, envContent);
// Umgebungsvariable aktualisieren, ohne den Bot neu zu starten
process.env.ADMIN_PW = newPassword;
// Passwortänderung abgeschlossen
passwordChangeRequired = false; // Sperre aufheben, Login wieder möglich
}
// /faq Befehl: Zeigt alle FAQs an
bot.onText(/\/faq/, (msg) => {
const chatId = msg.chat.id;
const faqs = loadFaqs();
if (faqs.length === 0) {
bot.sendMessage(chatId, 'Es gibt derzeit keine FAQs.');
} else {
let response = 'Häufig gestellte Fragen:\n\n';
faqs.forEach((faq, index) => {
response += `${index + 1}. *${faq.question}*\n${faq.answer}\n\n`;
});
bot.sendMessage(chatId, response, { parse_mode: 'Markdown' });
}
});
// /add_faq Befehl: Interaktives Hinzufügen einer neuen FAQ (nur für autorisierte Benutzer)
bot.onText(/\/add_faq/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id.toString();
if (!authorizedUsers.includes(userId)) {
bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.');
return;
}
// Frage nach der FAQ-Frage
bot.sendMessage(chatId, 'Bitte gib die FAQ-Frage ein:', {
reply_markup: { force_reply: true }
}).then(sentMessage => {
bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => {
const question = reply.text;
// Frage nach der FAQ-Antwort
bot.sendMessage(chatId, 'Bitte gib die Antwort auf die Frage ein:', {
reply_markup: { force_reply: true }
}).then(sentMessage => {
bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => {
const answer = reply.text;
// FAQ speichern
const faqs = loadFaqs();
faqs.push({ question, answer });
saveFaqs(faqs);
bot.sendMessage(chatId, '✅ FAQ erfolgreich hinzugefügt.');
});
});
});
});
});
// /del_faq Befehl: Interaktives Entfernen einer FAQ (nur für autorisierte Benutzer)
bot.onText(/\/del_faq/, (msg) => {
const chatId = msg.chat.id;
const userId = msg.from.id.toString();
if (!authorizedUsers.includes(userId)) {
bot.sendMessage(chatId, '❌ Du bist nicht autorisiert, diesen Befehl auszuführen.');
return;
}
const faqs = loadFaqs();
if (faqs.length === 0) {
bot.sendMessage(chatId, 'Es gibt derzeit keine FAQs zum Löschen.');
return;
}
// Liste der FAQs anzeigen und um Eingabe der Nummer bitten
let response = 'Welche FAQ möchtest du löschen?\n\n';
faqs.forEach((faq, index) => {
response += `${index + 1}. *${faq.question}*\n${faq.answer}\n\n`;
});
bot.sendMessage(chatId, response, {
parse_mode: 'Markdown',
reply_markup: { force_reply: true }
}).then(sentMessage => {
bot.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, (reply) => {
const faqIndex = parseInt(reply.text, 10) - 1;
if (isNaN(faqIndex) || faqIndex < 0 || faqIndex >= faqs.length) {
bot.sendMessage(chatId, '❌ Ungültige Auswahl.');
return;
}
// FAQ löschen
faqs.splice(faqIndex, 1);
saveFaqs(faqs);
bot.sendMessage(chatId, '✅ FAQ erfolgreich gelöscht.');
});
});
});
// Pfad zur Abonnentendatei
const subscribersFilePath = './subscribers.json';
const moviesApiUrl = `${process.env.PLEX_DOMAIN}/api/movies/latest`; // Beispiel-API-URL, anpassen
// Erstelle die subscribers.json, wenn sie nicht existiert
if (!fs.existsSync(subscribersFilePath)) {
fs.writeFileSync(subscribersFilePath, JSON.stringify([]));
}
let subscribers = [];
// Lade Abonnenten aus der subscribers.json
function loadSubscribers() {
try {
const data = fs.readFileSync(subscribersFilePath);
subscribers = JSON.parse(data);
} catch (error) {
console.error('Fehler beim Laden der Abonnenten:', error);
}
}
// Sende den Newsletter
async function sendNewsletter() {
loadSubscribers(); // Abonnenten laden
if (subscribers.length === 0) {
console.log('Keine Abonnenten gefunden.');
return;
}
const movies = await fetchLatestMovies(); // Filme abrufen
const htmlContent = createNewsletterContent(movies); // HTML-Inhalt erstellen
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
subscribers.forEach(subscriber => {
const mailOptions = {
from: process.env.SMTP_USER,
to: subscriber.email,
subject: 'Wöchentlicher Film-Newsletter',
html: htmlContent,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log('Fehler beim Senden der E-Mail:', error);
}
console.log('Newsletter gesendet an:', subscriber.email);
});
});
}
// Sofortige Bestätigungs-E-Mail senden
async function sendConfirmationEmail(email) {
const latestMovies = await fetchLatestMovies(); // Den zuletzt hinzugefügten Film abrufen
const latestMovie = latestMovies.length > 0 ? latestMovies[0] : null;
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
const logoUrl = process.env.PLEX_LOGO_URL; // Füge hier die URL zu deinem Plex-Logo hinzu
const latestMovieThumb = latestMovie ? `${process.env.PLEX_DOMAIN}${latestMovie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : '';
const latestMovieTitle = latestMovie ? latestMovie.title : 'Kein Film gefunden';
const latestMovieSummary = latestMovie ? latestMovie.summary : 'Keine Zusammenfassung verfügbar';
const mailOptions = {
from: process.env.SMTP_USER,
to: email,
subject: '🎉 Bestätigung der Newsletter-Anmeldung 🎉',
html: `
<div style="font-family: Arial, sans-serif; text-align: center; background-color: #f4f4f4; padding: 20px; border-radius: 8px;">
<h1 style="color: #4CAF50;">Willkommen zum Viper-Plex Newsletter!</h1>
<p style="font-size: 18px;">Vielen Dank, dass Sie sich für unseren Newsletter angemeldet haben! 🎊</p>
<p style="font-size: 16px;">Ab sofort erhalten Sie jeden Sonntag die neuesten Informationen über spannende Filme.</p>
<img src="${logoUrl}" alt="Plex Logo" style="width: 100px; margin: 20px 0;"/>
<h2 style="color: #2196F3;">Zuletzt hinzugefügter Film:</h2>
<p style="font-size: 20px; font-weight: bold;">${latestMovieTitle}</p>
${latestMovieThumb ? `<img src="${latestMovieThumb}" alt="${latestMovieTitle} Poster" width="200" height="300" style="margin: 10px 0;"/>` : ''}
<p style="font-size: 18px;"><strong>Zusammenfassung:</strong> ${latestMovieSummary}</p>
<p style="font-size: 16px; color: #777;">Wir freuen uns, Sie als Teil unserer Viper-Plex Familie zu haben!</p>
<footer style="margin-top: 20px; font-size: 14px; color: #999;">Falls Sie Fragen haben, zögern Sie nicht, uns zu kontaktieren.</footer>
</div>
`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log('Fehler beim Senden der Bestätigungs-E-Mail:', error);
}
console.log('Bestätigungs-E-Mail gesendet an:', email);
});
}
// Filme abrufen
async function fetchLatestMovies() {
try {
const response = await axios.get(moviesApiUrl);
// Filtere nur Filme aus und ignoriere Serien und Staffeln
return response.data.movies.filter(movie => !movie.isSeries);
} catch (error) {
console.error('Fehler beim Abrufen der Filme:', error);
return [];
}
}
// Erstelle den HTML-Inhalt des Newsletters
function createNewsletterContent(movies) {
let html = `
<div style="font-family: Arial, sans-serif; text-align: center; background-color: #f4f4f4; padding: 20px; border-radius: 8px;">
<h1 style="color: #4CAF50;">Neueste Filme auf Viper-Plex</h1>
<div style="width: 80%; margin: auto;">
`;
movies.forEach(movie => {
const movieTitle = movie.title || 'Unbekannt';
const movieSummary = movie.summary || 'Keine Zusammenfassung verfügbar';
const addedAtDate = new Date((movie.addedAt || 0) * 1000).toLocaleString('de-DE'); // Konvertierung von Unix-Zeitstempel in lesbares Datum
const movieThumb = movie.thumb ? `${process.env.PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : '';
html += `
<div style="background: #fff; border-radius: 8px; padding: 15px; margin: 10px 0; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);">
<h2 style="color: #2196F3;">${movieTitle}</h2>
${movieThumb ? `<img src="${movieThumb}" alt="${movieTitle} Poster" width="200" height="300" style="border-radius: 5px;"/>` : ''}
<p><strong>Zusammenfassung:</strong> ${movieSummary}</p>
<p><strong>Hinzugefügt am:</strong> ${addedAtDate}</p>
</div>
`;
});
html += `
</div>
</div>
`;
return html;
}
// Abmeldung vom Newsletter
bot.on('callback_query', (query) => {
const chatId = query.from.id; // Extrahiere die chatId aus dem Benutzer
// Überprüfen, ob die Abmeldung angefordert wird
if (query.data.startsWith('unsubscribe_')) {
const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId);
if (subscriberIndex !== -1) {
const subscriber = subscribers[subscriberIndex];
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: 'Ja',
callback_data: `unsubscribe_yes_${chatId}`,
},
{
text: 'Nein',
callback_data: `unsubscribe_no_${chatId}`,
},
],
],
},
};
bot.sendMessage(chatId, `😥 Möchten Sie sich wirklich von dem Newsletter abmelden, ${subscriber.username}?`, options);
} else {
bot.sendMessage(chatId, '❗️ Sie sind nicht für den Newsletter angemeldet.');
}
}
});
// Verarbeite die Callback-Daten für die Bestätigung
bot.on('callback_query', (query) => {
const chatId = query.from.id; // Hier verwenden wir query.from.id, um die chatId zu erhalten
if (query.data.startsWith('unsubscribe_yes')) {
const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId);
if (subscriberIndex !== -1) {
subscribers.splice(subscriberIndex, 1); // Abonnenten entfernen
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
bot.sendMessage(chatId, '✅ Sie wurden erfolgreich vom Newsletter abgemeldet.');
} else {
bot.sendMessage(chatId, '❗️ Abonnent nicht gefunden.');
}
} else if (query.data.startsWith('unsubscribe_no')) {
bot.sendMessage(chatId, '❌ Abmeldung vom Newsletter abgebrochen.');
}
});
// Abmeldebefehl (z.B. /unsubscribe)
bot.onText(/\/unsubscribe/, (msg) => {
const chatId = msg.chat.id;
unsubscribeFromNewsletter(chatId);
});
// Planen des Newsletter-Versands jeden Sonntag um 10:00 Uhr
schedule.scheduleJob('0 10 * * 0', () => {
console.log('Sende wöchentlichen Newsletter...');
sendNewsletter();
});
// Abonnieren
bot.onText(/\/newsletter/, (msg) => {
const chatId = msg.chat.id;
const username = msg.from.username || 'Unbekannt';
// Überprüfen, ob der Benutzer bereits abonniert ist
const subscriber = subscribers.find(subscriber => subscriber.chatId === chatId);
if (!subscriber) {
// Wenn nicht abonniert, frage nach der E-Mail-Adresse
bot.sendMessage(chatId, 'Bitte geben Sie Ihre E-Mail-Adresse ein:');
bot.once('message', (msg) => {
const email = msg.text;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(email)) {
// Neuen Abonnenten hinzufügen
subscribers.push({ chatId, email, username });
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
sendConfirmationEmail(email); // Bestätigungs-E-Mail senden
bot.sendMessage(chatId, '🎉 Sie haben sich erfolgreich für den Newsletter angemeldet!');
} else {
bot.sendMessage(chatId, '❌ Ungültige E-Mail-Adresse. Bitte versuchen Sie es erneut.');
}
});
} else {
// Wenn bereits abonniert, zeige die Optionen an
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: 'Abmelden',
callback_data: `unsubscribe_${chatId}`,
},
{
text: 'Mailadresse ändern',
callback_data: `change_email_${chatId}`,
},
],
// Zusätzliche Optionen für Administratoren
...(isAdmin(chatId) ? [
[
{
text: 'Send Newsletter',
callback_data: 'send_newsletter',
},
{
text: 'Abonnenten',
callback_data: 'list_subscribers',
},
{
text: 'Abonnenten Entfernen',
callback_data: 'remove_subscriber',
},
]
] : []),
],
},
};
bot.sendMessage(chatId, 'Sie sind bereits angemeldet. Was möchten Sie tun?', options);
}
});
// Funktion, um zu überprüfen, ob der Benutzer ein Administrator ist
function isAdmin(chatId) {
const adminIds = [process.env.USER1_ID, process.env.USER2_ID];
return adminIds.includes(chatId.toString());
}
// Callback-Handler für die Buttons
bot.on('callback_query', (query) => {
const chatId = query.from.id; // chatId aus der Anfrage erhalten
if (query.data.startsWith('change_email')) {
bot.sendMessage(chatId, 'Bitte geben Sie Ihre neue E-Mail-Adresse ein:');
bot.once('message', (msg) => {
const newEmail = msg.text;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(newEmail)) {
const subscriberIndex = subscribers.findIndex(subscriber => subscriber.chatId === chatId);
if (subscriberIndex !== -1) {
subscribers[subscriberIndex].email = newEmail; // E-Mail-Adresse aktualisieren
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
bot.sendMessage(chatId, '✅ Ihre E-Mail-Adresse wurde erfolgreich aktualisiert.');
}
} else {
bot.sendMessage(chatId, '❌ Ungültige E-Mail-Adresse. Bitte versuchen Sie es erneut.');
}
});
} else if (query.data === 'send_newsletter') {
sendNewsletter(); // Newsletter sofort senden
bot.sendMessage(chatId, '📧 Der Newsletter wurde gesendet!');
} else if (query.data === 'list_subscribers') {
// Hier wird die Abonnentenliste formatiert
const subscriberList = subscribers.map(subscriber => `🔹 @${subscriber.username} - ${subscriber.email}`).join('\n') || 'Keine Abonnenten gefunden.';
bot.sendMessage(chatId, `📋 Abonnenten:\n\n${subscriberList}`);
} else if (query.data === 'remove_subscriber') {
bot.sendMessage(chatId, 'Bitte geben Sie die E-Mail-Adresse des Abonnenten ein, den Sie entfernen möchten:');
bot.once('message', (msg) => {
const emailToRemove = msg.text;
const subscriberIndex = subscribers.findIndex(subscriber => subscriber.email === emailToRemove);
if (subscriberIndex !== -1) {
subscribers.splice(subscriberIndex, 1); // Abonnenten entfernen
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
bot.sendMessage(chatId, `✅ Der Abonnent ${emailToRemove} wurde entfernt.`);
} else {
bot.sendMessage(chatId, '❌ Abonnent nicht gefunden.');
}
});
}
});
// Lade Abonnenten beim Start
loadSubscribers();
// Profilbefehl
bot.onText(/\/profil/, (msg) => {
const chatId = msg.chat.id;
const userFilePath = path.join(__dirname, 'user.yml');
const subscribersFilePath = path.join(__dirname, 'subscribers.json');
const wishesFilePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`);
const feedbackFilePath = path.join(__dirname, 'feedback.log');
// Schritt 1: Benutzerinformationen aus user.yml lesen
fs.readFile(userFilePath, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${userFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Laden der Benutzerinformationen.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
const users = load(userData);
const user = users[chatId] || {}; // Benutzerdaten für den aktuellen Benutzer
// Initialisiere Benutzerinformationen
const userName = escapeMarkdownV2(user.username || 'Unbekannt');
const userId = chatId;
const firstUsedDate = escapeMarkdownV2(formatDate(user.firstUsed || new Date().toISOString())); // Aktuelles Datum verwenden, falls nicht vorhanden
// Benutzerlevel initialisieren
const commandCount = user.commandCount || 0; // Anzahl der Befehle aus den Benutzerdaten
const wishesCount = user.wishesCount || 0; // Anzahl der Wünsche aus Benutzerdaten
const userLevel = getUserLevel(commandCount, wishesCount); // Benutzerlevel ermitteln
// Lieblingsgenre aus user.yml ermitteln
const favoriteGenre = user.favoriteGenre || "Nicht festgelegt"; // Lieblingsgenre aus user.yml oder Standardwert
// Admin und Dev IDs aus .env auslesen
const adminIds = [process.env.USER1_ID, process.env.USER2_ID];
const devId = process.env.DEV_CHAT_ID;
// Bestimme die Rolle basierend auf der ID
let roles = [];
if (adminIds.includes(String(chatId))) {
roles.push('Admin');
}
if (String(chatId) === devId) {
roles.push('DEV');
}
const role = roles.length > 0 ? roles.join(', ') : 'Benutzer';
fs.readFile(subscribersFilePath, 'utf8', (err, subsData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${subscribersFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Laden des Newsletter-Status.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
let subscribers;
try {
subscribers = JSON.parse(subsData); // JSON sicher parsen
} catch (parseErr) {
console.error('Fehler beim Parsen der subscribers.json:', parseErr);
bot.sendMessage(chatId, 'Fehler beim Verarbeiten der Abonnentendaten.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Status prüfen, ob der Benutzer abonniert ist
const isSubscribed = subscribers.some(subscriber => subscriber.chatId === chatId);
const newsletterStatus = isSubscribed ? 'Ja' : 'Nein';
// Schritt 3: Wünsche aus wishes_${chatId}.json lesen
fs.readFile(wishesFilePath, 'utf8', (err, wishesData) => {
let wishesCount = 0; // Initialisierung der Wünsche
let notificationStatus = user.notifications ? 'Ja' : 'Nein';
if (!err) {
try {
const userWishes = JSON.parse(wishesData);
wishesCount = userWishes.length;
} catch (parseErr) {
console.error('Fehler beim Parsen der wishes-Datei:', parseErr);
}
}
// Schritt 4: Anzahl der Feedbacks zählen
fs.stat(feedbackFilePath, (err) => {
let feedbackCount = 0; // Standardwert für Feedbacks
if (!err) { // Datei existiert
fs.readFile(feedbackFilePath, 'utf8', (err, feedbackData) => {
if (!err) {
const feedbackLines = feedbackData.split('\n');
feedbackCount = feedbackLines.filter(line => line.includes(`chatId ${chatId}`)).length; // Zähle nur die Feedbacks des aktuellen Benutzers
}
// Benutzerlevel aktualisieren basierend auf den aktuellen Wünschen
const updatedUserLevel = getUserLevel(commandCount, wishesCount);
// Schritt 5: Nachricht formatieren und senden
const profileMessage = `
📝 *Profil Informationen:*\n\n
👤 *Name:* @${userName}\n
🔑 *ID:* ${userId}\n
👤 *Nutzerrolle:* ${role}\n
🌟 *Benutzerlevel:* ${updatedUserLevel}\n
📅 *Registrierung:* ${firstUsedDate}\n
📰 *Newsletter:* ${newsletterStatus}\n
📋 *Anzahl der Wünsche:* ${wishesCount}\n
📬 *Anzahl der Feedbacks:* ${feedbackCount}\n
🔔 *Benachrichtigung:* ${notificationStatus}\n
`.trim(); // Whitespace entfernen
// Sende Profilinformationen und zeige Button an
bot.sendMessage(chatId, profileMessage, {
parse_mode: 'MarkdownV2',
reply_markup: {
inline_keyboard: [
[{ text: 'Profil Bearbeiten', callback_data: 'edit_profile' }]
]
}
}).catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
});
} else {
// Datei existiert nicht, einfach die Nachricht senden
const profileMessage = `
📝 *Profil Informationen:*\n\n
👤 *Name:* @${userName}\n
🔑 *ID:* ${userId}\n
👤 *Nutzerrolle:* ${role}\n
🌟 *Benutzerlevel:* ${userLevel}\n
📅 *Registrierung:* ${firstUsedDate}\n
📰 *Newsletter:* ${newsletterStatus}\n
📋 *Anzahl der Wünsche:* ${wishesCount}\n
📬 *Anzahl der Feedbacks:* 0\n
🔔 *Benachrichtigung:* ${notificationStatus}\n
`.trim(); // Whitespace entfernen
bot.sendMessage(chatId, profileMessage, {
parse_mode: 'MarkdownV2',
reply_markup: {
inline_keyboard: [
[{ text: 'Profil Bearbeiten', callback_data: 'edit_profile' }]
]
}
}).catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
});
});
});
});
// Callback query handler for profile editing
bot.on('callback_query', (callbackQuery) => {
const action = callbackQuery.data;
const chatId = callbackQuery.message.chat.id;
if (action === 'edit_profile') {
// Zeige Bearbeitungsoptionen an, wenn der Benutzer "Profil Bearbeiten" drückt
bot.sendMessage(chatId, '🔍 Was möchten Sie tun? Wählen Sie eine der folgenden Optionen:', {
reply_markup: {
inline_keyboard: [
[
{ text: 'Lieblingsgenre setzen', callback_data: 'set_favorite_genre' },
{ text: 'Profil zurücksetzen', callback_data: 'reset_profile' }
],
[
{ text: 'Punkte löschen', callback_data: 'delete_points' },
{ text: 'Profil löschen', callback_data: 'delete_profile' }
]
]
}
});
} else if (action === 'set_favorite_genre') {
bot.sendMessage(chatId, 'Bitte geben Sie Ihre Lieblingsgenres ein, getrennt durch Kommas. Verfügbare Genres sind: \n\nAction, Abenteuer, Anime, Dokumentation, Drama, Familie, Fantasy, Horror, Katastrophen, Kinderfilme, Komödie, Krimi, Mystery, Syfy, Thriller, Western.');
// Hier fangen wir die Nachricht des Benutzers ab
bot.once('message', (msg) => {
const newFavoriteGenre = msg.text;
// Debugging: Logge das neue Lieblingsgenre
console.log(`Neues Lieblingsgenre: ${newFavoriteGenre} für Benutzer ${chatId}`);
// Update the favorite genre in user.yml
fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Lieblingsgenres.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
const users = load(userData);
// Überprüfen, ob der Benutzer bereits existiert
if (users[chatId]) {
// Setze das Lieblingsgenre
users[chatId].favoriteGenre = newFavoriteGenre; // Aktualisiere das Lieblingsgenre
} else {
// Benutzer initialisieren, falls nicht vorhanden
users[chatId] = {
userId: chatId,
username: msg.from.username,
firstUsed: new Date().toISOString(),
notifications: true, // Standardwert, falls nicht gesetzt
commandCount: 0, // Standardwert für Befehlsanzahl
userLevel: 'Neuling', // Standardbenutzerlevel
favoriteGenre: newFavoriteGenre // Setze das Lieblingsgenre
};
}
// Schreibe die aktualisierten Benutzerinformationen zurück in die Datei
fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Lieblingsgenres.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
bot.sendMessage(chatId, `✅ Ihr Lieblingsgenre wurde auf "${newFavoriteGenre}" gesetzt.`)
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
});
});
} else if (action === 'delete_points') {
// Punkte auf 0 setzen
fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Zurücksetzen der Punkte.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
const users = load(userData);
// Überprüfen, ob der Benutzer existiert
if (users[chatId]) {
users[chatId].commandCount = 0; // Setze die Punkte auf 0
// Schreibe die aktualisierten Benutzerinformationen zurück in die Datei
fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Zurücksetzen der Punkte.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
bot.sendMessage(chatId, '✅ Ihre Punkte wurden erfolgreich auf 0 gesetzt.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
} else {
bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
} else if (action === 'reset_profile') {
// Profil zurücksetzen
fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Zurücksetzen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
const users = load(userData);
// Überprüfen, ob der Benutzer existiert
if (users[chatId]) {
// Setze die Standardwerte zurück
users[chatId] = {
userId: chatId,
username: users[chatId].username, // Behalte den Benutzernamen bei
firstUsed: users[chatId].firstUsed, // Behalte das erste Nutzungsdatum bei
notifications: true, // Standardwert für Benachrichtigungen
commandCount: 0, // Punkte zurücksetzen
userLevel: 'Neuling', // Benutzerlevel zurücksetzen
favoriteGenre: 'Nicht festgelegt' // Setze das Lieblingsgenre auf den Standardwert
};
// Schreibe die aktualisierten Benutzerinformationen zurück in die Datei
fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Zurücksetzen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
bot.sendMessage(chatId, '✅ Ihr Profil wurde erfolgreich zurückgesetzt.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
} else {
bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
} else if (action === 'delete_profile') {
// Profil löschen
fs.readFile(USER_YML_PATH, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
const users = load(userData);
// Überprüfen, ob der Benutzer existiert
if (users[chatId]) {
// Benutzer aus user.yml entfernen
delete users[chatId];
// Schreibe die aktualisierten Benutzerinformationen zurück in die Datei
fs.writeFile(USER_YML_PATH, dump(users), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${USER_YML_PATH}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Lösche zugehörige Einträge in w_offen.json
const wOffenFilePath = path.join(__dirname, 'w_offen.json'); // Pfad zur w_offen.json-Datei
fs.readFile(wOffenFilePath, 'utf8', (err, wOffenData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${wOffenFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
let wOffen;
try {
wOffen = JSON.parse(wOffenData); // Sicheres Parsen der JSON-Daten
} catch (parseErr) {
console.error(`Fehler beim Parsen der w_offen.json: ${parseErr}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Entferne alle Wünsche des Benutzers
wOffen = wOffen.filter(wish => wish.userId !== chatId);
// Schreibe die aktualisierten Wünsche zurück in die w_offen.json
fs.writeFile(wOffenFilePath, JSON.stringify(wOffen, null, 2), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${wOffenFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Lösche die Datei im Wunsch-Ordner
const wunschFolderPath = path.join(__dirname, 'wunsch');
const userFilePath = path.join(wunschFolderPath, `wishes_${chatId}.json`); // Stelle sicher, dass der Dateiname korrekt ist
fs.unlink(userFilePath, (err) => {
if (err && err.code !== 'ENOENT') { // ENOENT bedeutet, die Datei existiert nicht
console.error(`Fehler beim Löschen der Datei ${userFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Lösche den Benutzer aus subscribers.json
const subscribersFilePath = path.join(__dirname, 'subscribers.json');
fs.readFile(subscribersFilePath, 'utf8', (err, subscribersData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${subscribersFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
let subscribers;
try {
subscribers = JSON.parse(subscribersData);
} catch (parseErr) {
console.error(`Fehler beim Parsen der subscribers.json: ${parseErr}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
return;
}
// Entferne den Benutzer aus der Liste
const updatedSubscribers = subscribers.filter(subscriber => subscriber.chatId !== chatId);
// Schreibe die aktualisierten Abonnenten zurück in die Datei
fs.writeFile(subscribersFilePath, JSON.stringify(updatedSubscribers, null, 2), 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben in die Datei ${subscribersFilePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Löschen des Profils.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
bot.sendMessage(chatId, '✅ Ihr Profil wurde erfolgreich gelöscht.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
});
});
});
});
});
} else {
bot.sendMessage(chatId, '❌ Benutzer nicht gefunden.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
}
});
const { load, dump } = require('js-yaml'); // Stelle sicher, dass js-yaml installiert ist
// Funktion zum Escapen von Markdown V2-Sonderzeichen
function escapeMarkdownV2(text) {
return text.replace(/([_*[\]()~>#+\-=.])/g, '\\$1'); // Escape-Zeichen
}
// Funktion zum Formatieren des Datums in DD-MM-YYYY
function formatDate(dateString) {
const [year, month, day] = dateString.split('-'); // Datum in Jahr, Monat, Tag zerlegen
return `${day}-${month}-${year}`; // DD-MM-YYYY Format zurückgeben
}
// Funktion zum Bestimmen des Benutzerlevels
function getUserLevel(commandCount) {
let level = 'Neuling';
// Kriterien für die Vergabe des Benutzerlevels
if (commandCount >= 6000) {
level = 'Legendärer Benutzer';
} else if (commandCount >= 3000) {
level = 'Meister Benutzer';
} else if (commandCount >= 1600) {
level = 'Fortgeschrittener Benutzer';
} else if (commandCount >= 800) {
level = 'Erfahrener Benutzer';
} else if (commandCount >= 400) {
level = 'VIP Benutzer';
} else if (commandCount >= 200) {
level = 'Aktiver Benutzer';
} else if (commandCount >= 100) {
level = 'Gelegentlicher Benutzer';
} else if (commandCount >= 30) {
level = 'Neuling';
}
return level;
}
// Funktion zum Aktualisieren des Benutzerlevels
function updateUserLevel(chatId) {
const userFilePath = path.join(__dirname, 'user.yml');
// Benutzerinformationen aus user.yml lesen
fs.readFile(userFilePath, 'utf8', (err, userData) => {
if (err) {
console.error(`Fehler beim Lesen der Datei ${userFilePath}: ${err}`);
return;
}
const users = load(userData);
const user = users[chatId];
if (user) {
// Benutzerlevel bestimmen
const commandCount = user.commandCount || 0;
const wishCount = user.wishCount || 0;
user.userLevel = getUserLevel(commandCount, wishCount); // Benutzerlevel aktualisieren
// Benutzerinformationen zurück in die Datei schreiben
const updatedUserData = dump(users);
fs.writeFile(userFilePath, updatedUserData, 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben der Datei ${userFilePath}: ${err}`);
}
});
}
});
}
// Befehl zum Aktualisieren des Benutzerlevels bei jeder Nachricht
bot.on('message', (msg) => {
const chatId = msg.chat.id;
const userFilePath = path.join(__dirname, 'user.yml');
// Hier kannst du die Anzahl der Befehle erhöhen
fs.readFile(userFilePath, 'utf8', (err, userData) => {
if (!err) {
const users = load(userData);
if (users[chatId]) {
users[chatId].commandCount = (users[chatId].commandCount || 0) + 1;
const updatedUserData = dump(users);
fs.writeFile(userFilePath, updatedUserData, 'utf8', (err) => {
if (err) {
console.error(`Fehler beim Schreiben der Datei ${userFilePath}: ${err}`);
} else {
// Benutzerlevel aktualisieren, nachdem die Anzahl der Befehle erhöht wurde
updateUserLevel(chatId);
}
});
}
}
});
});
// Befehl zum Sichern der Dateien
bot.onText(/\/backup/, (msg) => {
const chatId = msg.chat.id;
// Überprüfen, ob die Nachricht vom Dev kommt
if (msg.from.id.toString() === process.env.DEV_CHAT_ID) {
const filesToBackup = [
'user.yml',
'faq.json',
'subscribers.json',
'w_offen.json',
'feedback.log',
'command_history.json',
'dev_reports.json'
];
const backupFolder = path.join(__dirname, 'wunsch'); // Pfad zum Wunsch-Ordner
const zipFilePath = path.join(__dirname, 'backup.zip'); // Speicherort für die ZIP-Datei
// Erstelle einen ZIP-Stream
const output = fs.createWriteStream(zipFilePath);
const archive = archiver('zip');
output.on('close', () => {
console.log(`Backup abgeschlossen, ${archive.pointer()} total bytes.`);
bot.sendDocument(chatId, zipFilePath, { caption: '📦 Hier ist dein Backup!' }) // Sende die ZIP-Datei an den Developer
.then(() => {
fs.unlinkSync(zipFilePath); // Lösche die ZIP-Datei nach dem Senden
})
.catch(err => {
console.error(`Fehler beim Senden der Backup-Datei: ${err.message}`);
bot.sendMessage(chatId, `❌ Fehler beim Senden der Backup-Datei: ${err.message}`);
});
});
archive.on('error', (err) => {
console.error(`Fehler beim Erstellen des Backups: ${err}`);
bot.sendMessage(chatId, `❌ Fehler beim Erstellen des Backups: ${err.message}`);
});
archive.pipe(output);
// Füge die Dateien hinzu
filesToBackup.forEach(file => {
const filePath = path.join(__dirname, file);
if (fs.existsSync(filePath)) {
archive.file(filePath, { name: file });
}
});
// Füge den Wunsch-Ordner hinzu, wenn er existiert
if (fs.existsSync(backupFolder)) {
archive.directory(backupFolder + '/', 'wunsch/'); // Füge den Inhalt des Wunsch-Ordners hinzu
}
archive.finalize(); // Beende die Archivierung
} else {
bot.sendMessage(chatId, '🚫 Dieser Befehl ist nur für den Developer verfügbar.');
}
});
let debugMode = false;
bot.onText(/\/setdebug/, (msg) => {
const chatId = msg.chat.id;
if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) {
return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich.");
}
debugMode = !debugMode;
const status = debugMode ? "aktiviert" : "deaktiviert";
bot.sendMessage(chatId, `🐞 Debug-Modus wurde ${status}.`);
});
const os = require('os');
bot.onText(/\/serverinfo/, (msg) => {
const chatId = msg.chat.id;
if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) {
return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich.");
}
const totalMemory = os.totalmem();
const freeMemory = os.freemem();
// Umrechnung in Gigabyte
const totalMemoryGB = (totalMemory / (1024 ** 3)).toFixed(2); // Umrechnen in GB
const freeMemoryGB = (freeMemory / (1024 ** 3)).toFixed(2); // Umrechnen in GB
const info = `🖥️ *Server-Info:*\n\n\n` +
`🔹 Plattform: ${os.platform()}\n\n` +
`🔹 Architektur: ${os.arch()}\n\n` +
`🔹 Gesamter Speicher: ${totalMemoryGB} GB\n\n` +
`🔹 Freier Speicher: ${freeMemoryGB} GB`;
bot.sendMessage(chatId, info);
});
bot.onText(/\/healthcheck/, async (msg) => {
const chatId = msg.chat.id;
if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) {
return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich.");
}
let responseMessages = [];
responseMessages.push("🖥️ *Bot-Status:*\n\n");
// 1. Überprüfung, ob der Bot online ist
responseMessages.push("✅ *Bot ist online und funktionsfähig.*\n");
// 2. Überprüfung der Verbindung zur Plex API
try {
const plexResponse = await axios.get(`${process.env.PLEX_DOMAIN}/status`, {
headers: {
'X-Plex-Token': process.env.PLEX_TOKEN
}
});
responseMessages.push("✅ *Verbindung zur Plex API ist erfolgreich.*\n\n\n");
} catch (error) {
responseMessages.push("❌ *Verbindung zur Plex API fehlgeschlagen.*\n\n\n");
}
// 3. Überprüfung, ob wichtige Dateien vorhanden sind
responseMessages.push("📂 *Dateiüberprüfung:*\n\n");
const requiredFiles = ['user.yml', 'faq.json', 'subscribers.json', 'dev_reports.json', 'w_offen.json', 'feedback.log', 'command_history.json', 'error.log', 'Cache/cache-series.json', 'Cache/cache.json', 'Log/message.log', 'wunsch', 'backups'];
for (const file of requiredFiles) {
if (fs.existsSync(file)) {
responseMessages.push(`✅ *Datei ${file} ist vorhanden.*\n`);
} else {
responseMessages.push(`❌ *Datei ${file} fehlt.*\n`);
}
}
// Sende die gesammelten Antworten als Nachricht
bot.sendMessage(chatId, responseMessages.join(''), { parse_mode: 'Markdown' });
});
const commandHistoryFilePath = './command_history.json'; // Pfad zur Datei
let commandHistory = [];
// Lade die Historie aus der Datei, falls vorhanden
if (fs.existsSync(commandHistoryFilePath)) {
try {
const data = fs.readFileSync(commandHistoryFilePath, 'utf8'); // Stelle sicher, dass die Datei als UTF-8 gelesen wird
commandHistory = JSON.parse(data);
} catch (error) {
console.error("Fehler beim Laden der Kommando-Historie:", error.message);
commandHistory = []; // Setze die Historie zurück, wenn ein Fehler auftritt
}
}
// Funktion zum Protokollieren der Befehle
function logCommand(command, username) {
const timestamp = new Date(); // Aktuelles Datum und Uhrzeit
const formattedDate = timestamp.toLocaleString(); // Formatierung des Datums
// Füge den Befehl zur Historie hinzu
commandHistory.push(`${formattedDate} - @${username} - ${command}`);
if (commandHistory.length > 30) {
commandHistory.shift(); // Behalte nur die letzten 10 Befehle
}
// Speichere die Historie in der Datei
fs.writeFileSync(commandHistoryFilePath, JSON.stringify(commandHistory, null, 2));
}
// Funktion zum Formatieren der Historie
function formatCommandHistory(history) {
return history.map(entry => {
const [date, time, username, command] = entry.split(' - ');
return `${date} ${time} | ${username} | ${command}`;
}).join('\n'); // Jeder Eintrag wird in eine neue Zeile geschrieben
}
bot.onText(/\/command_history/, (msg) => {
const chatId = msg.chat.id;
if (msg.from.id !== parseInt(process.env.DEV_CHAT_ID)) {
return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für den Entwickler zugänglich.");
}
if (commandHistory.length === 0) {
return bot.sendMessage(chatId, "📜 Keine Befehle in der Historie gefunden.");
}
const historyMessage = `🗃️ Kommando-Historie:\n\n` +
`Datum - Uhrzeit | Benutzername | Befehl\n` +
`-----------------------------------------\n` +
formatCommandHistory(commandHistory).replace(/,/g, ''); // Entferne Kommas und füge neue Zeilen hinzu
bot.sendMessage(chatId, historyMessage);
});
// Beispiel für andere Befehle
bot.onText(/\/start/, (msg) => {
logCommand('/update', msg.from.username);
// Logik für den Update-Befehl...
});
bot.onText(/\/notification_on/, (msg) => {
logCommand('/notification_on', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/notification_off/, (msg) => {
logCommand('/notification_off', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/serien/, (msg) => {
logCommand('/serien', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/latestmovie/, (msg) => {
logCommand('/latestmovie', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/latest10movies/, (msg) => {
logCommand('/latest10movies', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/top_rated/, (msg) => {
logCommand('/top_rated', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/wunsch/, (msg) => {
logCommand('/wunsch', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/trailer/, (msg) => {
logCommand('/trailer', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/empfehlung/, (msg) => {
logCommand('/empfehlung', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/newsletter/, (msg) => {
logCommand('/newsletter', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/help/, (msg) => {
logCommand('/help', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/profil/, (msg) => {
logCommand('/profil', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/w_list/, (msg) => {
logCommand('/w_list', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/dev/, (msg) => {
logCommand('/dev', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/feedback/, (msg) => {
logCommand('/feedback', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/faq/, (msg) => {
logCommand('/faq', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/info/, (msg) => {
logCommand('/info', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/bot/, (msg) => {
logCommand('/bot', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/admin/, (msg) => {
logCommand('/admin', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/open_wishes/, (msg) => {
logCommand('/open_wishes', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/user/, (msg) => {
logCommand('/user', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/update/, (msg) => {
logCommand('/update', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/logs/, (msg) => {
logCommand('/logs', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/logs_delete/, (msg) => {
logCommand('/logs_delete', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/f_log/, (msg) => {
logCommand('/f_log', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/add_faq/, (msg) => {
logCommand('/add_faq', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/del_faq/, (msg) => {
logCommand('/del_faq', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/command_history/, (msg) => {
logCommand('/command_history', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/backup/, (msg) => {
logCommand('/backup', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/serverinfo/, (msg) => {
logCommand('/serverinfo', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/healthcheck/, (msg) => {
logCommand('/healthcheck', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/setdebug/, (msg) => {
logCommand('/setdebug', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/support/, (msg) => {
logCommand('/support', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/night/, (msg) => {
logCommand('/night', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/n_off/, (msg) => {
logCommand('/n_off', msg.from.username);
// Logik für den Befehl...
});
bot.onText(/\/passwd/, (msg) => {
logCommand('/passwd', msg.from.username);
// Logik für den Befehl...
});
const userId1 = Number(process.env.USER1_ID); // USER1_ID aus .env laden und in Zahl umwandeln
const userId2 = Number(process.env.USER2_ID); // USER2_ID aus .env laden und in Zahl umwandeln
bot.onText(/\/support/, (msg) => {
const chatId = msg.chat.id;
// Direkt die Telegram-ID verwenden
const adminId = 5507179337;
// Überprüfen, ob die Benutzer-ID in den autorisierten IDs enthalten ist
if (msg.from.id !== userId1 && msg.from.id !== userId2) {
return bot.sendMessage(chatId, "🚫 Dieser Befehl ist nur für autorisierte Benutzer zugänglich.");
}
bot.sendMessage(chatId, "💬 Bitte gib zusätzliche Informationen für den Support an:");
// Setze einen Listener für die nächste Nachricht des Benutzers
bot.once('message', async (reply) => {
const additionalText = reply.text || "Keine zusätzlichen Informationen bereitgestellt.";
const filesToZip = [
'error.log',
'command_history.json',
'user.yml',
'subscribers.json',
];
const logFolder = 'Log';
const zipPath = 'support.zip';
const output = fs.createWriteStream(zipPath);
const archive = archiver('zip');
output.on('close', async () => {
const botName = process.env.BOT_NAME || "Unbekannter Bot"; // Bot-Namen aus der .env
const adminNames = `${userId1}, ${userId2}`; // Namen der Administratoren
const supportMessage = `🛠️ *Externe Support-Anfrage* \n\n\n` +
`🔧 Bot-Name: @${botName}\n\n` +
`👨‍💻 Administratoren:\n ${adminNames}\n\n\n` +
`💬 Zusätzliche Informationen:\n\n ${additionalText}`;
await bot.sendMessage(adminId, supportMessage, { parse_mode: 'Markdown' });
await bot.sendDocument(adminId, zipPath);
fs.unlinkSync(zipPath); // Löscht die ZIP-Datei nach dem Senden
});
archive.on('error', (err) => {
throw err;
});
archive.pipe(output);
// Füge die Dateien zum ZIP-Archiv hinzu
filesToZip.forEach((file) => {
if (fs.existsSync(file)) {
archive.file(file, { name: file });
} else {
console.warn(`Datei ${file} nicht gefunden.`);
}
});
// Füge den Log-Ordner hinzu
if (fs.existsSync(logFolder)) {
archive.directory(logFolder + '/', logFolder + '/');
}
await archive.finalize(); // Warte, bis das Archiv abgeschlossen ist
});
});
// 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}`; // Beispiel-URL anpassen
// Debugging-Ausgabe
console.log(`Image URL: ${imageUrl}`);
// Formatieren des Hinzufügungsdatums
const addedDate = addedAt ? dayjs(addedAt * 1000).format('DD.MM.YYYY') : 'Unbekannt';
const caption = `📺 *Titel:* ${title}\n\n` +
`📝 *Beschreibung:* \n${summary}\n\n` +
`📅 *Hinzugefügt am:* ${addedDate}`;
// Bild herunterladen und lokal speichern
const imagePath = path.join(__dirname, 'temp_image.jpg');
const writer = fs.createWriteStream(imagePath);
const response = await axios({
url: imageUrl,
method: 'GET',
responseType: 'stream'
});
response.data.pipe(writer);
writer.on('finish', async () => {
// Senden des Bildes
try {
await bot.sendPhoto(chatId, imagePath, { caption, parse_mode: 'Markdown' });
// Optional: Nach dem Senden das Bild löschen
fs.unlinkSync(imagePath);
} catch (sendPhotoError) {
logError(`Fehler beim Senden des Fotos: ${sendPhotoError.message}`);
bot.sendMessage(chatId, 'Fehler beim Senden des Bildes. Bitte versuche es später erneut.');
}
});
writer.on('error', (error) => {
logError(`Fehler beim Schreiben der Bilddatei: ${error.message}`);
bot.sendMessage(chatId, 'Fehler beim Abrufen des Bildes. Bitte versuche es später erneut.');
});
} 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}`);
}
}
});
// Log-Error-Funktion (Optional)
function logError(message) {
const timestamp = new Date().toISOString();
fs.appendFileSync('error.log', `${timestamp} - ${message}\n`);
}
// Umgebungsvariable für die Chat-ID der Entwickler
const DEV_CHAT_ID = parseInt(process.env.DEV_CHAT_ID, 10);
// Der Pfad zur Datei, in der die Dev Reports gespeichert werden
const DEV_REPORTS_FILE_PATH = path.join(__dirname, 'dev_reports.json');
// Funktion zum Erstellen der Datei, wenn sie nicht vorhanden ist
function createDevReportsFile() {
if (!fs.existsSync(DEV_REPORTS_FILE_PATH)) {
fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify([])); // Leeres Array initialisieren
console.log('Dev Reports Datei erstellt.');
}
}
// 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' }],
[{ text: '🎬 Film Report', callback_data: 'film_report' }]
]
}
};
}
// 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;
case 'film_report':
responseText = '🎬 *Bitte geben Sie den Film Report ein:* \n\nTitel & Fehlerbeschreibung:';
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:') ||
msg.reply_to_message.text.includes('Bitte geben Sie den Film Report ein:'))) {
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;
let messageType;
let report;
if (msg.reply_to_message.text.includes('Funktionswunsch')) {
messageType = 'Funktionswunsch';
report = { type: messageType, user: { name: userName, id: userId }, message: text };
} else if (msg.reply_to_message.text.includes('Bug')) {
messageType = 'Bug';
report = { type: messageType, user: { name: userName, id: userId }, message: text };
} else if (msg.reply_to_message.text.includes('Film Report')) {
// Hier wird der gesamte Text für Titel und Fehlerbeschreibung verwendet
const userMessage = text.trim(); // Benutzertext
messageType = 'Film Report';
report = {
type: messageType,
user: { name: userName, id: userId },
message: userMessage // Der gesamte Benutzertext wird als Nachricht verwendet
};
}
// Dev Report in die Datei schreiben
try {
// Nur die DEV_CHAT_ID für Bug und Funktionswunsch
if (messageType === 'Bug' || messageType === 'Funktionswunsch') {
console.log('Sende Nachricht an Entwickler-Chat-ID:', DEV_CHAT_ID); // Debugging-Ausgabe
await bot.sendMessage(DEV_CHAT_ID, formatDevMessage(report), { parse_mode: 'Markdown' });
}
// DEV_CHAT_ID und USER2_ID für Film Reports
else if (messageType === 'Film Report') {
console.log('Sende Nachricht an Entwickler-Chat-ID und USER2_ID:', DEV_CHAT_ID, USER2_ID); // Debugging-Ausgabe
await bot.sendMessage(DEV_CHAT_ID, formatDevMessage(report), { parse_mode: 'Markdown' });
await bot.sendMessage(USER2_ID, formatDevMessage(report), { parse_mode: 'Markdown' });
}
console.log('Nachricht erfolgreich gesendet.');
// Dev Report in die JSON-Datei speichern
saveDevReport(report);
bot.sendMessage(chatId, '✅ Ihre Nachricht wurde erfolgreich gesendet! Vielen Dank.');
} catch (error) {
console.error('Fehler beim Senden der Nachricht:', error);
bot.sendMessage(chatId, '🚫 Etwas ist schiefgelaufen. Ihre Nachricht konnte nicht gesendet werden.');
}
}
});
// Funktion zur Formatierung der Dev-Nachricht
function formatDevMessage(report) {
return `📩 *${report.type}*\n\n` +
`von: ${report.user.name} (${report.user.id})\n\n` +
`"${report.message}"`;
}
// Funktion zum Speichern des Dev Reports in die JSON-Datei
function saveDevReport(report) {
const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH));
report.id = reports.length; // ID basierend auf der aktuellen Länge des Arrays zuweisen
reports.push(report);
fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(reports, null, 2)); // Schön formatieren
}
// Starte den Bot und erstelle die Datei
createDevReportsFile();
// Handler für den /bot-Befehl
bot.onText(/\/bot/, (msg) => {
const chatId = msg.chat.id;
try {
// Bot-Version dynamisch aus der .env-Datei
const botVersion = process.env.BOT_VERSION || "1.7.0";
// 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 aus user.yml
const users = yaml.load(USER_YML_PATH);
const userCount = Object.keys(users).length;
// Abonnentenanzahl aus subscribers.json
const subscribers = JSON.parse(fs.readFileSync('subscribers.json', 'utf8'));
const subscriberCount = subscribers.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
- Newsletter Versand jeden Sonntag \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
📰 *Abonnentenanzahl:* ${subscriberCount} \n
🔄 *Letzter Neustart:* ${lastRestart} \n
💾 *Speicherbelegung:* ${memoryStats} \n
🔑 *Bot Token:* ${botToken.slice(0, 0)}... (Ausgeblendet 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()}
`;
// Inline-Button erstellen
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Kontakt",
url: process.env.CONTACT_LINK // Link aus der .env-Datei
}
]
]
},
parse_mode: 'Markdown'
};
// Nachricht senden
bot.sendMessage(chatId, infoMessage, options).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}`);
});
}
});
// 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'
// Aktuelles Datum im ISO-Format
const firstUsedDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD
// Benutzerdaten in user.yml speichern
let users = yaml.load(USER_YML_PATH);
users[chatId] = {
userId: userId,
username: username,
notifications: true, // Standardmäßig Benachrichtigungen aktiviert
firstUsed: firstUsedDate, // Datum des ersten Gebrauchs
favoriteGenre: "Nicht festgelegt" // Standardwert für das Lieblingsgenre
};
fs.writeFileSync(USER_YML_PATH, yaml.stringify(users, 4));
// Bot-Start-Nachricht
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 aktiviert.
Um sie zu deaktivieren, tippe 👉 /notification_off.
👤 Möchtest du dein Profil sehen? Tippe 👉 /profil.`;
// Inline-Button zu einer Webadresse, basierend auf der Umgebungsvariable
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: 'zur Web Oberfläche',
url: process.env.PANEL_LINK // Verwendung der PANEL_LINK-Umgebungsvariable
}
]
]
}
};
bot.sendMessage(chatId, welcomeMessage, options);
// /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.');
}
});
// /update Befehl
bot.onText(/\/update/, async (msg) => {
const chatId = msg.chat.id;
// Überprüfen, ob der Benutzer berechtigt ist
if (chatId.toString() !== process.env.DEV_CHAT_ID) {
bot.sendMessage(chatId, '❌ Du hast keine Berechtigung, diesen Befehl auszuführen.');
return;
}
try {
// Benutzer.yml laden
const userYmlData = yaml.load(USER_YML_PATH);
// Aktuelles Datum im Format 'YYYY-MM-DD'
const currentDate = dayjs().format('YYYY-MM-DD');
// Durchlaufe alle Benutzer und aktualisiere das Datum, falls es fehlt
for (const userId in userYmlData) {
if (!userYmlData[userId].firstUsed) {
userYmlData[userId].firstUsed = currentDate; // Setze das aktuelle Datum
}
// Überprüfen, ob das Feld favoriteGenre existiert
if (!userYmlData[userId].favoriteGenre) {
userYmlData[userId].favoriteGenre = "Nicht festgelegt"; // Setze Standardwert, wenn das Genre fehlt
}
}
// Benutzer.yml speichern
fs.writeFileSync(USER_YML_PATH, yaml.stringify(userYmlData, 4));
bot.sendMessage(chatId, '✅ Die user.yml wurde erfolgreich aktualisiert.');
} catch (error) {
logError(`Fehler beim Aktualisieren der user.yml: ${error.message}`);
bot.sendMessage(chatId, `❌ Fehler beim Aktualisieren der user.yml: ${error.message}`);
}
});
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}` : '';
// Trailer-URL abrufen
const trailerUrl = await fetchTrailerUrl(latestMovie); // Funktion zum Abrufen des Trailer-Links
const message = `Der zuletzt hinzugefügte Film ist:\n\nTitel: ${movieTitle}\n\nZusammenfassung: \n${movieSummary}\n\nHinzugefügt am: ${addedAtDate}`;
// Erstelle den Inline-Button für den Trailer
const replyMarkup = {
inline_keyboard: [
[{ text: "Trailer ansehen", url: trailerUrl || '#' }] // Fallback-URL falls kein Trailer vorhanden
]
};
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
bot.sendPhoto(chatId, movieThumb, { caption: message, reply_markup: replyMarkup }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, message, { reply_markup: replyMarkup }).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;
}
}
const usersNightMode = {}; // Temporärer Speicher für Nachtmodus
// Funktion zum Laden der Benutzerdaten aus der user.yml
function loadUserData() {
if (!fs.existsSync(USER_YML_PATH)) {
fs.writeFileSync(USER_YML_PATH, yaml.stringify({}));
}
return yaml.load(USER_YML_PATH);
}
// Funktion zum Speichern der Benutzerdaten in die user.yml
function saveUserData(userData) {
fs.writeFileSync(USER_YML_PATH, yaml.stringify(userData, 4));
}
// Funktion zum Anheften der Nachtmodus-Nachricht
function pinNightModeMessage(chatId, messageId) {
bot.pinChatMessage(chatId, messageId).catch(err => console.error('Fehler beim Anheften der Nachricht:', err));
}
// Funktion zum Entfernen der angehefteten Nachricht
function unpinNightModeMessage(chatId) {
bot.unpinChatMessage(chatId).catch(err => console.error('Fehler beim Entfernen der angehefteten Nachricht:', err));
}
// /night Befehl
bot.onText(/\/night/, (msg) => {
const chatId = msg.chat.id;
const userData = loadUserData(); // Lade die Benutzerdaten
const userId = chatId.toString();
// Standard-Buttons
const buttons = [];
// Überprüfen, ob der Benutzer bereits Nachtmodi hat
if (!userData[userId] || !userData[userId].nightModes || userData[userId].nightModes.length === 0) {
buttons.push([{ text: '🌙 Nachtmodus eingeben', callback_data: 'input_night_mode' }]);
} else {
buttons.push(
[
{ text: '🛠️ Nachtmodus bearbeiten', callback_data: 'edit_night_mode' },
{ text: ' Weiteren Nachtmodus eingeben', callback_data: 'add_night_mode' }
],
[{ text: '🗑️ Nachtmodus löschen', callback_data: 'delete_night_mode' }]
);
}
// Zeige die Inline-Buttons an
bot.sendMessage(chatId, 'Wählen Sie eine Option:', {
reply_markup: {
inline_keyboard: buttons,
},
});
});
// Callback-Query-Handler für die Inline-Buttons
bot.on('callback_query', (query) => {
const chatId = query.message.chat.id;
const userId = chatId.toString();
const userData = loadUserData(); // Lade die Benutzerdaten
if (query.data === 'input_night_mode') {
// Eingabe eines neuen Nachtmodus
bot.sendMessage(chatId, 'Bitte geben Sie die Startzeit des Nachtmodus im Format HH:mm ein (z.B. 22:00):');
bot.once('message', (msg) => {
const startTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!startTime || !/^\d{2}:\d{2}$/.test(startTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
bot.sendMessage(chatId, 'Bitte geben Sie die Endzeit des Nachtmodus im Format HH:mm ein (z.B. 06:00):');
bot.once('message', (msg) => {
const endTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!endTime || !/^\d{2}:\d{2}$/.test(endTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
// Speichere die Nachtmodus-Daten
userData[userId] = userData[userId] || {};
userData[userId].nightModes = userData[userId].nightModes || [];
userData[userId].nightModes.push({ startTime, endTime });
saveUserData(userData); // Speichere die Daten in die yml-Datei
bot.sendMessage(chatId, `🌓 Nachtmodus geplant von ${startTime} bis ${endTime}.`);
});
});
} else if (query.data === 'edit_night_mode') {
// Bearbeiten eines bestehenden Nachtmodus
if (userData[userId] && userData[userId].nightModes && userData[userId].nightModes.length > 0) {
const nightModes = userData[userId].nightModes;
let nightModesText = 'Aktuelle Nachtmodi:\n';
nightModes.forEach((mode, index) => {
nightModesText += `${index + 1}: ${mode.startTime} bis ${mode.endTime}\n`;
});
nightModesText += 'Geben Sie die Nummer des Nachtmodus ein, den Sie bearbeiten möchten:';
bot.sendMessage(chatId, nightModesText);
bot.once('message', (msg) => {
const modeIndex = parseInt(msg.text) - 1;
if (isNaN(modeIndex) || modeIndex < 0 || modeIndex >= nightModes.length) {
return bot.sendMessage(chatId, 'Ungültige Auswahl.');
}
bot.sendMessage(chatId, 'Bitte geben Sie die neue Startzeit im Format HH:mm ein (z.B. 22:00):');
bot.once('message', (msg) => {
const newStartTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!newStartTime || !/^\d{2}:\d{2}$/.test(newStartTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
bot.sendMessage(chatId, 'Bitte geben Sie die neue Endzeit im Format HH:mm ein (z.B. 06:00):');
bot.once('message', (msg) => {
const newEndTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!newEndTime || !/^\d{2}:\d{2}$/.test(newEndTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
// Aktualisiere den Nachtmodus
userData[userId].nightModes[modeIndex] = { startTime: newStartTime, endTime: newEndTime };
saveUserData(userData); // Speichere die Änderungen
bot.sendMessage(chatId, `🌓 Nachtmodus aktualisiert auf ${newStartTime} bis ${newEndTime}.`);
});
});
});
} else {
bot.sendMessage(chatId, 'Es sind keine Nachtmodi zum Bearbeiten vorhanden.');
}
} else if (query.data === 'add_night_mode') {
// Eingabe eines weiteren Nachtmodus
bot.sendMessage(chatId, 'Bitte geben Sie die Startzeit des Nachtmodus im Format HH:mm ein (z.B. 22:00):');
bot.once('message', (msg) => {
const startTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!startTime || !/^\d{2}:\d{2}$/.test(startTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
bot.sendMessage(chatId, 'Bitte geben Sie die Endzeit des Nachtmodus im Format HH:mm ein (z.B. 06:00):');
bot.once('message', (msg) => {
const endTime = msg.text;
// Sicherstellen, dass msg.text existiert und gültig ist
if (!endTime || !/^\d{2}:\d{2}$/.test(endTime)) {
return bot.sendMessage(chatId, 'Ungültiges Zeitformat. Bitte geben Sie die Zeit im Format HH:mm ein.');
}
// Speichere den neuen Nachtmodus
userData[userId].nightModes.push({ startTime, endTime });
saveUserData(userData); // Speichere die Daten in die yml-Datei
bot.sendMessage(chatId, `🌓 Weiterer Nachtmodus geplant von ${startTime} bis ${endTime}.`);
});
});
} else if (query.data === 'delete_night_mode') {
// Zeige Liste der Nachtmodi zum Löschen
if (userData[userId] && userData[userId].nightModes && userData[userId].nightModes.length > 0) {
const nightModes = userData[userId].nightModes;
let nightModesText = '🔄 **Bitte wählen Sie einen Nachtmodus zum Löschen:**\n\n';
nightModes.forEach((mode, index) => {
nightModesText += `🕒 **${index + 1}:** ${mode.startTime} bis ${mode.endTime}\n`;
});
nightModesText += '\n🗑 *Geben Sie die Nummer des Nachtmodus ein, den Sie löschen möchten:*';
bot.sendMessage(chatId, nightModesText);
bot.once('message', (msg) => {
const modeIndex = parseInt(msg.text) - 1;
if (isNaN(modeIndex) || modeIndex < 0 || modeIndex >= nightModes.length) {
return bot.sendMessage(chatId, 'Ungültige Auswahl.');
}
// Lösche den ausgewählten Nachtmodus
userData[userId].nightModes.splice(modeIndex, 1); // Lösche den Nachtmodus an der gegebenen Indexposition
saveUserData(userData); // Speichere die Änderungen
bot.sendMessage(chatId, '🗑️ Der ausgewählte Nachtmodus wurde gelöscht.');
});
} else {
bot.sendMessage(chatId, 'Es gibt keinen Nachtmodus, der gelöscht werden kann.');
}
}
});
// Funktion zur Überprüfung, ob der Benutzer im Nachtmodus ist
function isUserInNightMode(chatId) {
const userData = loadUserData();
const userId = chatId.toString();
const userNightModes = userData[userId] && userData[userId].nightModes;
if (!userNightModes || userNightModes.length === 0) return false;
const now = moment();
return userNightModes.some(userNightMode => {
const start = moment(userNightMode.startTime, 'HH:mm');
const end = moment(userNightMode.endTime, 'HH:mm');
if (end.isBefore(start)) {
return now.isAfter(start) || now.isBefore(end); // Nachtmodus über Mitternacht
} else {
return now.isBetween(start, end); // Normaler Nachtmodus
}
});
}
// Funktion zur automatischen Aktivierung des Nachtmodus
function activateNightMode() {
const userData = loadUserData();
for (const userId in userData) {
const userNightModes = userData[userId] && userData[userId].nightModes;
if (!userNightModes) continue;
const now = moment();
userNightModes.forEach(userNightMode => {
const start = moment(userNightMode.startTime, 'HH:mm');
const end = moment(userNightMode.endTime, 'HH:mm');
// Nachtmodus über Mitternacht
if (end.isBefore(start)) {
if (now.isAfter(start) || now.isBefore(end)) {
handleNightModeActivation(userId, userData);
} else {
handleNightModeDeactivation(userId, userData);
}
}
// Normaler Nachtmodus
else {
if (now.isBetween(start, end)) {
handleNightModeActivation(userId, userData);
} else {
handleNightModeDeactivation(userId, userData);
}
}
});
}
}
// Funktion zur Aktivierung des Nachtmodus
function handleNightModeActivation(userId, userData) {
if (userData[userId].notifications !== false) {
userData[userId].originalNotifications = userData[userId].notifications;
userData[userId].notifications = false;
saveUserData(userData);
bot.sendMessage(userId, `🌓 Der Nachtmodus hat begonnen. Deine Benachrichtigungen wurden auf Stumm geschaltet.`)
.then((msg) => {
// Pin die Nachricht
pinNightModeMessage(userId, msg.message_id);
});
console.log(`Nachtmodus für Benutzer ${userId} aktiviert.`);
}
}
// Funktion zur Deaktivierung des Nachtmodus
function handleNightModeDeactivation(userId, userData) {
if (userData[userId].notifications === false) {
userData[userId].notifications = userData[userId].originalNotifications;
delete userData[userId].originalNotifications;
saveUserData(userData);
bot.sendMessage(userId, `🌓 Der Nachtmodus endet, die Benachrichtigungen sind jetzt wieder aktiv.`)
.then(() => {
// Unpin die Nachricht
unpinNightModeMessage(userId);
});
console.log(`Nachtmodus für Benutzer ${userId} deaktiviert.`);
}
}
// Automatische Nachtmodus-Aktivierung überwachen
setInterval(() => {
activateNightMode();
}, 60 * 1000); // Überprüfung alle 60 Sekunden
// Funktion zum Erstellen der Datei 'w_offen.json' im Hauptverzeichnis, falls sie noch nicht existiert
function ensureWOffenFileExists() {
const filePath = path.join(__dirname, 'w_offen.json'); // Hauptverzeichnis
if (!fs.existsSync(filePath)) {
// Datei erstellen und leeres Array als Inhalt speichern
fs.writeFileSync(filePath, JSON.stringify([], null, 2), (err) => {
if (err) {
console.error(`Fehler beim Erstellen der Datei 'w_offen.json': ${err}`);
}
});
console.log(`Die Datei 'w_offen.json' wurde im Hauptverzeichnis erstellt.`);
} else {
console.log(`Die Datei 'w_offen.json' existiert bereits.`);
}
}
// Funktion zum Erstellen des Ordners 'wunsch', falls dieser noch nicht existiert
function ensureWunschFolderExists() {
const folderPath = path.join(__dirname, 'wunsch');
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath, { recursive: true });
console.log(`Ordner 'wunsch' wurde erstellt.`);
}
}
// Funktion zum Speichern eines Wunsches in der Datei 'wishes_<chatId>.json'
function saveWish(chatId, wish, type, fulfilled = false) {
ensureWunschFolderExists(); // Ordner sicherstellen
const filePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`);
const wishData = { type, wish, fulfilled };
fs.readFile(filePath, (err, data) => {
let wishes = [];
if (!err) {
wishes = JSON.parse(data); // Vorhandene Wünsche lesen
}
wishes.push(wishData); // Neuen Wunsch hinzufügen
fs.writeFile(filePath, JSON.stringify(wishes, null, 2), (err) => {
if (err) {
console.error(`Fehler beim Speichern des Wunsches: ${err}`);
} else {
console.log(`Wunsch von ${chatId} erfolgreich gespeichert.`);
}
});
});
}
// Funktion zum Erstellen des Inline-Keyboards 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 mit Inline-Buttons für 'Erfüllt' und 'Nicht erfüllt'
async function sendWish(wish, type, chatId) {
const message = `✨ Achtung! ✨\n\nEin neuer Wunsch ist eingegangen:\n\n🔹 Typ: ${type}\n\n🔹 Titel:\n${wish}`;
// Inline-Keyboard mit den zwei Buttons
const inlineKeyboard = {
reply_markup: JSON.stringify({
inline_keyboard: [
[
{ text: '✅ Wunsch erfüllt', callback_data: `wish_fulfilled_${chatId}` },
{ text: '❌ Wunsch nicht erfüllt', callback_data: `wish_not_fulfilled_${chatId}` }
]
]
})
};
try {
const [msg1, msg2] = await Promise.all([
bot.sendMessage(USER1_ID, message, inlineKeyboard),
bot.sendMessage(USER2_ID, message, inlineKeyboard),
]);
console.log(`Wunsch von Typ ${type} wurde an ${USER1_ID} und ${USER2_ID} gesendet.`);
return { messageId1: msg1.message_id, messageId2: msg2.message_id }; // Rückgabe der Nachricht IDs
} catch (error) {
console.error(`Fehler beim Senden des Wunsches: ${error.message}`);
}
// Speichern des Wunsches in der Datei
saveWish(chatId, wish, type);
}
// 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';
await bot.sendMessage(chatId, `Du hast ${type} ausgewählt. Bitte gib den Titel des ${type} ein.`)
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
// Lösche die Nachricht der Kategorieauswahl
await bot.deleteMessage(chatId, query.message.message_id).catch(error => console.error(`Fehler beim Löschen der Nachricht: ${error.message}`));
userStates[chatId] = { type, waitingForWish: true }; // Setze den Status auf "wartend auf Wunsch"
}
if (data.startsWith('wish_fulfilled_')) {
const userId = data.split('_')[2]; // Der Ersteller des Wunsches
const messageText = query.message.text; // Der Text des Wunsches
const wishTitle = messageText.split('Titel:\n')[1].trim(); // Titel korrekt extrahieren
await bot.sendMessage(userId, '🎉 Dein Wunsch wurde erfüllt!')
.catch(error => console.error(`Fehler beim Senden der Nachricht: ${error.message}`));
// Wunsch in der Datei 'wishes_<chatId>.json' als erfüllt markieren
const filePath = path.join(__dirname, 'wunsch', `wishes_${userId}.json`);
fs.readFile(filePath, (err, data) => {
if (!err) {
let wishes = JSON.parse(data);
// Suche den spezifischen Wunsch und markiere ihn als erfüllt
wishes = wishes.map(wish => {
if (wish.wish === wishTitle) {
return { ...wish, fulfilled: true }; // Nur den spezifischen Wunsch als erfüllt markieren
}
return wish; // Alle anderen Wünsche unverändert lassen
});
fs.writeFile(filePath, JSON.stringify(wishes, null, 2), (err) => {
if (err) {
console.error(`Fehler beim Aktualisieren des Wunsches: ${err}`);
}
});
}
});
// Lösche die Wunschnachricht
await bot.deleteMessage(chatId, query.message.message_id);
}
if (data.startsWith('wish_not_fulfilled_')) {
const userId = query.message.chat.id; // Nutze die Chat-ID des Nachrichtenautors
await bot.sendMessage(userId, '😢 Dein Wunsch wurde leider nicht erfüllt.')
.catch(error => console.error(`Fehler beim Senden der Nachricht: ${error.message}`));
// Wunsch in der Datei 'w_offen.json' speichern
const filePath = path.join(__dirname, 'w_offen.json');
const wishDetails = {
userId: userId, // Chat-ID als userId speichern
message: query.message.text, // Nachricht als Wunsch speichern
};
fs.readFile(filePath, (err, data) => {
let openWishes = [];
if (!err && data.length > 0) {
openWishes = JSON.parse(data); // Vorhandene offene Wünsche lesen
}
openWishes.push(wishDetails); // Neuen offenen Wunsch hinzufügen
fs.writeFile(filePath, JSON.stringify(openWishes, null, 2), (err) => {
if (err) {
console.error(`Fehler beim Speichern des offenen Wunsches: ${err}`);
} else {
console.log('Der nicht erfüllte Wunsch wurde in der Datei "w_offen.json" gespeichert.');
}
});
});
// Lösche die Wunschnachricht
await bot.deleteMessage(chatId, query.message.message_id);
}
bot.answerCallbackQuery(query.id).catch(error => {
console.error(`Fehler bei der Callback-Abfrage: ${error.message}`);
});
});
// Verarbeite eingehende Nachrichten
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const text = msg.text || ''; // Sicherstellen, dass text definiert ist
if (userStates[chatId] && userStates[chatId].waitingForWish) {
const wish = text.trim();
if (wish) {
const type = userStates[chatId].type;
await sendWish(wish, type, chatId);
await bot.sendMessage(chatId, `✅ Dein ${type}-Wunsch wurde übermittelt.`)
.catch(error => console.error(`Fehler bei der Bestätigungsnachricht: ${error.message}`));
userStates[chatId].waitingForWish = false;
} else {
await bot.sendMessage(chatId, `✍️ Bitte gib den Titel des ${userStates[chatId].type} ein.`)
.catch(error => console.error(`Fehler bei der Wunsch-Nachricht: ${error.message}`));
}
return;
}
if (text.startsWith('/wunsch')) {
await bot.sendMessage(chatId, '🎬 Möchtest du einen Film oder eine Serie wünschen? Wähle bitte eine Option:', getTypeKeyboard())
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
userStates[chatId] = { waitingForType: true };
}
if (text.startsWith('/w_list')) {
// Liste der Wünsche für den Benutzer anzeigen
const filePath = path.join(__dirname, 'wunsch', `wishes_${chatId}.json`);
fs.readFile(filePath, (err, data) => {
if (err) {
bot.sendMessage(chatId, 'Es wurden keine Wünsche gefunden.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
const wishes = JSON.parse(data);
let wishList = '📜 Deine Wünsche:\n\n';
wishes.forEach((wish, index) => {
const statusEmoji = wish.fulfilled ? '🟢' : '🔴';
wishList += `${index + 1}. ${statusEmoji} ${wish.type}: ${wish.wish} \n\n`;
});
bot.sendMessage(chatId, wishList)
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
}
});
// Objekt zur Verfolgung der Benutzer, die auf eine Eingabe warten
let waitingForWishIndex = {};
// API-Endpunkt für offene Wünsche
app.get('/api/wishes', (req, res) => {
fs.readFile('w_offen.json', 'utf8', (err, data) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Lesen der Wünsche' });
}
res.json(JSON.parse(data));
});
});
// Verarbeite die Auswahl des Inline-Buttons zum Markieren eines Wunsches als erfüllt
bot.on('callback_query', (query) => {
const chatId = query.message.chat.id;
const data = query.data;
if (data === 'mark_wish_fulfilled') {
bot.sendMessage(chatId, 'Bitte gib die Nummer des Wunsches ein, den du als erfüllt markieren möchtest:')
.then(() => {
bot.once('message', (msg) => {
const wishIndex = parseInt(msg.text.trim()) - 1; // Wunschindex basierend auf der eingegebenen Zahl
// Lese die Datei 'w_offen.json' aus
const filePath = path.join(__dirname, 'w_offen.json');
fs.readFile(filePath, (err, data) => {
if (!err) {
let openWishes = JSON.parse(data);
if (wishIndex >= 0 && wishIndex < openWishes.length) {
const fulfilledWish = openWishes[wishIndex];
// Log zur Überprüfung
console.log(`Markiere Wunsch: ${fulfilledWish.message} von User ID: ${fulfilledWish.userId}`);
// Wunsch als erfüllt markieren in 'wishes_<userId>.json'
const userWishFile = path.join(__dirname, 'wunsch', `wishes_${fulfilledWish.userId}.json`);
fs.readFile(userWishFile, (err, wishData) => {
if (!err) {
let userWishes = JSON.parse(wishData);
// Suche den spezifischen Wunsch und markiere ihn als erfüllt
let wishFound = false;
// Extrahiere den Titel aus der Wunschnachricht
const wishMatch = fulfilledWish.message.match(/🔹 Titel:\s*(.*)/);
const extractedTitle = wishMatch ? wishMatch[1].trim() : '';
userWishes.forEach(wish => {
// Entferne Leerzeichen und Zeilenumbrüche vor dem Vergleich
const normalizedFileWishText = wish.wish.trim().toLowerCase();
console.log(`Wunschtext aus der Datei: "${normalizedFileWishText}"`);
console.log(`Wunschtext aus der Eingabe: "${extractedTitle.toLowerCase()}"`);
if (normalizedFileWishText === extractedTitle.toLowerCase()) {
wishFound = true;
wish.fulfilled = true; // Setze fulfilled auf true
console.log(`Wunsch "${wish.wish}" wurde erfolgreich auf 'fulfilled: true' gesetzt.`);
}
});
if (!wishFound) {
console.log(`Wunsch "${fulfilledWish.message}" nicht gefunden.`);
bot.sendMessage(chatId, 'Wunsch konnte nicht gefunden werden.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
// Schreibe die aktualisierte Wunschliste zurück in die Datei
fs.writeFile(userWishFile, JSON.stringify(userWishes, null, 2), (err) => {
if (err) {
console.error(`Fehler beim Aktualisieren des Wunsches: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Aktualisieren des Wunsches.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
// Entferne den Wunsch aus 'w_offen.json'
openWishes.splice(wishIndex, 1);
fs.writeFile(filePath, JSON.stringify(openWishes, null, 2), (err) => {
if (err) {
console.error(`Fehler beim Aktualisieren von 'w_offen.json': ${err}`);
bot.sendMessage(chatId, 'Fehler beim Aktualisieren der offenen Wünsche.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
} else {
bot.sendMessage(chatId, `Der Wunsch von User ID ${fulfilledWish.userId} wurde als erfüllt markiert.`)
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
}
});
}
} else {
console.error(`Fehler beim Lesen der Datei ${userWishFile}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Lesen der Benutzerdaten.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
} else {
bot.sendMessage(chatId, 'Ungültige Wunschnummer.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
} else {
console.error(`Fehler beim Lesen der Datei ${filePath}: ${err}`);
bot.sendMessage(chatId, 'Fehler beim Lesen der offenen Wünsche.')
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
});
});
})
.catch(error => console.error(`Fehler bei der Nachricht: ${error.message}`));
}
bot.answerCallbackQuery(query.id).catch(error => {
console.error(`Fehler bei der Callback-Abfrage: ${error.message}`);
});
});
// /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}`;
// YouTube Trailer Link erstellen
const youtubeLink = `https://www.youtube.com/results?search_query=${encodeURIComponent(movieTitle + ' trailer')}`;
// Inline-Button für den Trailer hinzufügen
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Trailer ansehen",
url: youtubeLink,
}
]
]
}
};
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
bot.sendPhoto(chatId, movieThumb, { caption: message, reply_markup: options.reply_markup }).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, message, options).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;
}
}
// Funktion zum Abrufen des Trailers für einen bestimmten Film
async function fetchTrailerUrl(filmTitle) {
try {
// YouTube API URL für die Suche
const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=${encodeURIComponent(filmTitle + ' trailer')}&key=${process.env.YOUTUBE_API_KEY}`;
const response = await axios.get(url);
const videos = response.data.items;
// Überprüfen, ob Videos gefunden wurden
if (videos.length > 0) {
const videoId = videos[0].id.videoId; // ID des ersten gefundenen Trailers
return `https://www.youtube.com/watch?v=${videoId}`; // URL des Trailers
} else {
return null; // Kein Trailer gefunden
}
} catch (error) {
logError(`Error fetching trailer URL: ${error.message}`);
return null; // Fehler beim Abrufen des Trailers
}
}
// Funktion zum Kürzen der Zusammenfassung
function truncateSummary(summary, maxLength) {
if (summary.length > maxLength) {
return summary.slice(0, maxLength) + '...'; // Kürzen und "..." hinzufügen
}
return summary;
}
// Funktion zum Erstellen der Bildunterschrift
function createCaption(title, summary) {
// Initiale Bildunterschrift ohne Kürzung
let caption = `
Hier ist der empfohlene Film des Tages:
🎬 Titel: ${title || 'Unbekannt'}
📝 Zusammenfassung:
${summary || 'Keine Zusammenfassung verfügbar'}
`;
// Überprüfen, ob die Bildunterschrift zu lang ist
if (caption.length > MAX_CAPTION_LENGTH) {
// Berechnen der maximalen Länge für die Zusammenfassung
const maxSummaryLength = MAX_CAPTION_LENGTH - (caption.length - summary.length);
// Kürzen der Zusammenfassung auf die berechnete Länge
const truncatedSummary = truncateSummary(summary, maxSummaryLength);
// Neu zusammenstellen der Bildunterschrift mit der gekürzten Zusammenfassung
caption = `
Hier ist der empfohlene Film des Tages:
🎬 Titel: ${title || 'Unbekannt'}
📝 Zusammenfassung:
${truncatedSummary}
`;
}
return caption;
}
// /empfehlung-Befehl verarbeiten
bot.onText(/\/empfehlung/, async (msg) => {
const chatId = msg.chat.id;
try {
const dailyMovie = await fetchDailyRecommendation();
if (dailyMovie) {
// Film empfehlen
const movieTitle = dailyMovie.title || 'Unbekannt';
const movieSummary = dailyMovie.summary || 'Keine Zusammenfassung verfügbar';
const movieThumb = dailyMovie.thumb ? `${process.env.PLEX_DOMAIN}${dailyMovie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}` : '';
// Erstellen der Bildunterschrift und Kürzen, falls nötig
const message = createCaption(movieTitle, movieSummary);
// Trailer URL abrufen
const trailerUrl = await fetchTrailerUrl(movieTitle);
// Bild anzeigen, wenn vorhanden
if (movieThumb) {
const options = {
caption: message,
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{
text: "Trailer ansehen",
url: trailerUrl || "https://www.youtube.com", // Fallback-URL, falls kein Trailer gefunden wird
}
]
]
}
};
await bot.sendPhoto(chatId, movieThumb, options).catch(error => {
logError(`Error sending photo to chatId ${chatId}: ${error.message}`);
});
} else {
const options = {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{
text: "Trailer ansehen",
url: trailerUrl || "https://www.youtube.com", // Fallback-URL, falls kein Trailer gefunden wird
}
]
]
}
};
await bot.sendMessage(chatId, message, options).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 allgemeinen Hilfennachricht
function createHelpMessage() {
return `📜 *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` +
`⭐ /top\\_rated - Zeigt die am besten bewerteten Filme an.\n\n` +
`💭 /wunsch - Nutze diesen Befehl, um einen Filmwunsch zu äußern.\n\n` +
`🎬 /trailer - Fordere einen Trailer für einen bestimmten Film an. \n\n` +
`🔝 /empfehlung - Film Empfehlung des Tages.\n\n` +
`📰 /newsletter - zeigt die Newsletter Funktion an\n\n` +
`❓ /help - Zeigt diese Hilfennachricht an.\n\n`;
}
// Funktion zum Erstellen der weiteren Hilfennachricht
function createMoreHelpMessage() {
return `📜 *weitere Hilfe:*\n\n` +
`📝 /profil - Zeigt dein Profil an\n\n` +
`✨ /w\\_list - Zeigt dir deine Wünsche an.\n\n` +
`🔧 /dev - Funktionswunsch oder Bug melden.\n\n` +
`💬 /feedback - Gib Feedback zum Bot.\n\n` +
`❓ /faq - Häufig gestellte Fragen.\n\n` +
` /info - Anzahl Filme und Serien.\n\n` +
`🤖 /bot - Bot-Informationen.\n\n`;
}
// Funktion zum Erstellen der Admin-Hilfennachricht
function createAdminHelpMessage() {
return `*👨‍💻 Admin Befehle* \n\n` +
`🛠️ /admin - sendet eine Nachricht an alle Nutzer.\n\n` +
`🔒 /passwd - gibt dir das Aktuelle Passwort vom Frontend\n\n` +
`✨ /open\\_wishes - Zeigt alle offenen Wünsche an\n\n` +
`👤 /user - Zeigt Benutzerinformationen an.\n\n` +
`📰 /newsletter - Zeigt die Newsletter Funktion an\n\n` +
`📝 /logs - Zeigt die letzten Fehlermeldungen an.\n\n` +
`🗑️ /log\\_delete - Löscht Logs.\n\n` +
`📝 /f\\_log - Sendet das Feedback als .txt-Datei.\n\n` +
`❓ /add\\_faq - Fügt eine neue Frage zur FAQ hinzu.\n\n` +
`🗑️ /del\\_faq - Löscht eine FAQ.\n\n\n`+
`*👨‍💻 Dev Befehle* \n\n` +
`🔄 /update - Aktuallisiert die user.yml\n\n` +
`🗃️ /command\\_history - Zeigt eine Liste der zuletzt verwendeten Befehle an.\n\n` +
`💾 /backup - erstellt ein Backup und sendet es als zip\n\n` +
`🪧 /serverinfo - Zeigt Informationen über den Server\n\n` +
`🔍 /healthcheck - Überprüft den Bot\n\n` +
`🔄 /setdebug - Aktiviert oder deaktiviert den Debug-Modus\n\n` +
`🛠️ /support - Erstellt ein Support-Ticket an den Bot-Ersteller.\n\n`;
}
// /help-Befehl verarbeiten
bot.onText(/\/help/, (msg) => {
const chatId = msg.chat.id;
// Prüfen, ob der Benutzer ein Admin ist
const isAdmin = chatId.toString() === process.env.USER1_ID || chatId.toString() === process.env.USER2_ID;
if (!isAdmin) {
// Normale Benutzer: Hilfennachricht und "Mehr"-Button anzeigen
const helpMessage = createHelpMessage();
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Mehr",
callback_data: "more_help",
},
{
text: "Kontakt",
url: process.env.CONTACT_LINK,
}
]
]
},
parse_mode: 'Markdown'
};
bot.sendMessage(chatId, helpMessage, options).catch(error => {
console.log(`Error sending help message to chatId ${chatId}: ${error.message}`);
});
} else {
// Admin-Benutzer: Buttons "User Hilfe" und "Admin Hilfe" anzeigen
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "User Hilfe",
callback_data: "user_help"
},
{
text: "Admin Hilfe",
callback_data: "admin_help",
}
]
]
},
parse_mode: 'Markdown'
};
bot.sendMessage(chatId, "Bitte wähle eine Option:", options).catch(error => {
console.log(`Error sending admin help buttons to chatId ${chatId}: ${error.message}`);
});
}
});
// Callback für die Inline-Buttons verarbeiten
bot.on('callback_query', (callbackQuery) => {
const chatId = callbackQuery.message.chat.id;
const data = callbackQuery.data;
if (data === "user_help") {
const helpMessage = createHelpMessage();
// Inline-Button für "Mehr" und Kontakt hinzufügen
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Mehr",
callback_data: "more_help",
},
{
text: "Kontakt",
url: process.env.CONTACT_LINK,
}
]
]
},
parse_mode: 'Markdown'
};
bot.sendMessage(chatId, helpMessage, options).catch(error => {
console.log(`Error sending user help message to chatId ${chatId}: ${error.message}`);
});
} else if (data === "more_help") {
const moreHelpMessage = createMoreHelpMessage();
// Kontakt-Button hinzufügen
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Kontakt",
url: process.env.CONTACT_LINK,
}
]
]
},
parse_mode: 'Markdown'
};
bot.sendMessage(chatId, moreHelpMessage, options).catch(error => {
console.log(`Error sending more help message to chatId ${chatId}: ${error.message}`);
});
} else if (data === "admin_help") {
// Überprüfung, ob der Benutzer berechtigt ist
if (chatId.toString() === process.env.USER1_ID || chatId.toString() === process.env.USER2_ID) {
const adminHelpMessage = createAdminHelpMessage();
const options = {
reply_markup: {
inline_keyboard: [
[
{
text: "Kontakt",
url: process.env.CONTACT_LINK,
}
]
]
},
parse_mode: 'Markdown'
};
bot.sendMessage(chatId, adminHelpMessage, options).catch(error => {
console.log(`Error sending admin help message to chatId ${chatId}: ${error.message}`);
});
} else {
bot.sendMessage(chatId, "Du hast keine Berechtigung, diesen Befehl zu verwenden.", { parse_mode: 'Markdown' }).catch(error => {
console.log(`Error sending unauthorized message to chatId ${chatId}: ${error.message}`);
});
}
}
});
// 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);
}
});
app.use(express.json());
//Anfang für Frontend
// schnittstelle für Kontakt.html
app.get('/api/contact-info', (req, res) => {
res.json({
email: process.env.SMTP_USER,
telegram: process.env.CONTACT_LINK
});
});
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Route für Umgebungsvariablen
app.get('/api/env', (req, res) => {
res.json({
botAlias: process.env.BOT_ALIAS,
telegramLink: process.env.TELEGRAM_LINK,
PW_FRONT: process.env.PW_FRONT, //Frontend Passwort Abfrage
});
});
app.use(express.static('public'));
// Middleware
app.use(bodyParser.json());
app.use(express.static('public'));
// Funktion zum Laden der Abonnenten
function loadSubscribers() {
if (fs.existsSync(subscribersFilePath)) {
const data = fs.readFileSync(subscribersFilePath);
subscribers = JSON.parse(data);
}
}
// Abonnieren
app.post('/subscribe', (req, res) => {
const { email, chatId, username } = req.body;
if (!subscribers.find(subscriber => subscriber.chatId === chatId)) {
subscribers.push({ chatId, email, username });
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
sendConfirmationEmail(email); // Bestätigungs-E-Mail senden
res.status(200).send('Erfolgreich angemeldet!');
} else {
res.status(400).send('Bereits angemeldet!');
}
});
// Abmelden
app.post('/unsubscribe', (req, res) => {
const { chatId } = req.body;
subscribers = subscribers.filter(subscriber => subscriber.chatId !== chatId);
fs.writeFileSync(subscribersFilePath, JSON.stringify(subscribers, null, 2));
res.status(200).send('Erfolgreich abgemeldet!');
});
// Lade Abonnenten beim Start
loadSubscribers();
// API-Route für die neuesten Filme
app.get('/api/latest-movies', async (req, res) => {
try {
const response = await axios.get(`${process.env.PLEX_DOMAIN}/library/recentlyAdded?X-Plex-Token=${process.env.PLEX_TOKEN}`);
const movies = response.data.MediaContainer.Metadata.slice(0, 10).map(movie => ({
title: movie.title,
coverImage: `${process.env.PLEX_DOMAIN}${movie.thumb}?X-Plex-Token=${process.env.PLEX_TOKEN}`, // Coverbild-URL mit Token
summary: movie.summary // Hier fügst du die Zusammenfassung hinzu
}));
console.log(movies); // Überprüfung der Daten
res.json(movies);
} catch (error) {
console.error('Fehler beim Abrufen der neuesten Filme:', error);
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
app.get('/api/telegram-link', (req, res) => {
res.json({ link: process.env.TELEGRAM_LINK }); // Stelle den Link aus der .env bereit
});
app.get('/api/bot-version', (req, res) => {
res.json({ version: process.env.BOT_VERSION });
});
// API-Route, die den Wert von WEB_NAME bereitstellt
app.get('/api/web-name', (req, res) => {
res.json({ name: process.env.WEB_NAME });
});
// 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}`);
}
}
// Route für das Dashboard
app.get('/admin/dashboard', (req, res) => {
if (!req.session.user) { // Überprüfung, ob der Benutzer eingeloggt ist
return res.redirect('/login'); // Weiterleitung zur Login-Seite
}
res.sendFile(__dirname + '/views/admin-dashboard.html'); // Sende die HTML-Datei
});
// API-Endpunkt für Bot-Laufzeit
app.get('/api/bot-uptime', (req, res) => {
const uptime = process.uptime();
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
const seconds = Math.floor(uptime % 60);
res.json({ runtime: `${hours}h ${minutes}m ${seconds}s` });
});
// API-Endpunkt für Dateiprüfung
app.get('/api/file-check', (req, res) => {
const requiredFiles = ['user.yml', 'faq.json', 'subscribers.json', 'dev_reports.json', 'w_offen.json', 'feedback.log', 'command_history.json', 'error.log', 'Cache/cache-series.json', 'Cache/cache.json', 'Log/message.log', 'wunsch', 'backups'];
let fileStatus = requiredFiles.map(file => ({
file: file,
exists: fs.existsSync(file)
}));
res.json(fileStatus);
});
// API-Endpunkt für Serverinformationen
app.get('/api/server-info', (req, res) => {
const totalMemory = os.totalmem() / (1024 ** 3); // In GB
const freeMemory = os.freemem() / (1024 ** 3); // In GB
const serverInfo = {
platform: os.platform(),
architecture: os.arch(),
totalMemory: totalMemory.toFixed(2),
freeMemory: freeMemory.toFixed(2)
};
res.json(serverInfo);
});
// Route für das Fehlerprotokoll
app.get('/api/error-log', (req, res) => {
fs.readFile('./error.log', 'utf8', (err, data) => {
if (err) {
return res.status(500).send('Fehler beim Lesen des Fehlerprotokolls');
}
res.send(data);
});
});
// Route für die Kommando-Historie
app.get('/api/command-history', (req, res) => {
fs.readFile('./command_history.json', 'utf8', (err, data) => {
if (err) {
return res.status(500).send('Fehler beim Lesen der Kommando-Historie');
}
res.send(data);
});
});
// Route zum Leeren des Fehlerprotokolls
app.post('/api/clear-error-log', (req, res) => {
fs.writeFile('./error.log', '', (err) => {
if (err) {
return res.status(500).json({ success: false, message: 'Fehler beim Leeren des Fehlerprotokolls' });
}
res.json({ success: true });
});
});
// Route zum Leeren der Kommando-Historie
app.post('/api/clear-command-history', (req, res) => {
fs.writeFile('./command_history.json', '', (err) => {
if (err) {
return res.status(500).json({ success: false, message: 'Fehler beim Leeren der Kommando-Historie' });
}
res.json({ success: true });
});
});
// Route zum Abrufen der FAQs
app.get('/api/faqs', (req, res) => {
const faqs = loadFaqs();
res.json(faqs);
});
// Route zum Hinzufügen einer neuen FAQ
app.post('/api/add-faq', (req, res) => {
const faqs = loadFaqs();
const { question, answer } = req.body;
faqs.push({ question, answer });
saveFaqs(faqs);
res.json({ success: true });
});
// Route zum Löschen einer FAQ
app.delete('/api/delete-faq', (req, res) => {
const faqs = loadFaqs();
const index = req.body.index;
if (index >= 0 && index < faqs.length) {
faqs.splice(index, 1);
saveFaqs(faqs);
res.json({ success: true });
} else {
res.status(400).json({ success: false });
}
});
app.get('/api/wishes', (req, res) => {
fs.readFile('w_offen.json', 'utf8', (err, data) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Lesen der Wünsche' });
}
try {
// Hier kannst du die Logik zum Parsen der Datei manuell implementieren
const wishes = parseWishes(data);
res.json(wishes);
} catch (error) {
return res.status(500).json({ error: 'Fehler beim Verarbeiten der Wünsche' });
}
});
});
// Funktion zum manuellen Parsen der nicht-standardisierten Daten
function parseWishes(data) {
const wishes = [];
const lines = data.split('\n');
let currentWish = {};
for (let line of lines) {
if (line.startsWith('- userId:')) {
if (Object.keys(currentWish).length > 0) {
wishes.push(currentWish); // vorherigen Wunsch speichern
}
currentWish = { userId: parseInt(line.split(': ')[1]) }; // userId speichern
} else if (line.startsWith('message: |-')) {
// Nächste Zeile ist der Beginn der Nachricht
currentWish.message = '';
} else if (currentWish.message !== undefined) {
currentWish.message += line + '\n'; // Nachricht aufbauen
}
}
// Den letzten Wunsch hinzufügen
if (Object.keys(currentWish).length > 0) {
wishes.push(currentWish);
}
return wishes;
}
// Endpoint für das Feedback
app.get('/api/feedback', (req, res) => {
const feedbackFilePath = path.join(__dirname, 'feedback.log');
fs.readFile(feedbackFilePath, 'utf8', (err, data) => {
if (err) {
console.error('Fehler beim Lesen der feedback.log:', err);
return res.status(500).send('Fehler beim Laden des Feedbacks.');
}
res.send(data);
});
});
// Endpunkt /api/users, um die user.yml-Datei zu lesen und die Daten im JSON-Format zurückzugeben
app.get('/api/users', (req, res) => {
try {
// Pfad zur user.yml-Datei
const filePath = path.join(__dirname, 'user.yml');
// YAML-Datei laden
const file = fs.readFileSync(filePath, 'utf8');
// YAML in ein JSON-Objekt konvertieren
const data = yaml.parse(file); // 'parse' Funktion verwenden
// Benutzerobjekte in ein Array umwandeln
const usersArray = Object.values(data).map(user => ({
userId: user.userId,
username: user.username,
notifications: user.notifications,
firstUsed: user.firstUsed,
favoriteGenre: user.favoriteGenre,
commandCount: user.commandCount || 0, // Default auf 0, wenn nicht vorhanden
userLevel: user.userLevel || 'Nicht festgelegt', // Default-Wert
nightMode: user.nightMode || {} // Optionales Feld
}));
// JSON-Daten zurückgeben
res.json(usersArray);
} catch (err) {
console.error('Fehler beim Laden der YAML-Datei:', err);
res.status(500).json({ message: 'Fehler beim Laden der Benutzerdaten' });
}
});
// Endpunkt zum Löschen eines Benutzers
app.delete('/api/users/:userId', (req, res) => {
const userId = req.params.userId;
try {
// Pfad zur user.yml-Datei
const filePath = path.join(__dirname, 'user.yml');
// YAML-Datei laden
const file = fs.readFileSync(filePath, 'utf8');
const data = yaml.parse(file); // YAML in ein JSON-Objekt konvertieren
// Überprüfe, ob der Benutzer existiert
if (!data[userId]) {
return res.status(404).json({ message: 'Benutzer nicht gefunden' });
}
// Benutzer aus den Daten entfernen
delete data[userId];
// Aktualisiere die YAML-Datei mit den neuen Daten
fs.writeFileSync(filePath, yaml.stringify(data), 'utf8');
res.json({ message: 'Benutzer erfolgreich gelöscht' });
} catch (err) {
console.error('Fehler beim Löschen des Benutzers:', err);
res.status(500).json({ message: 'Fehler beim Löschen des Benutzers' });
}
});
let lastRestart = new Date(); // Speichere den aktuellen Zeitpunkt als letzten Neustart
// Funktion zum Formatieren des Datums
const formatLastRestartDate = (date) => {
const options = {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false // 24-Stunden-Format
};
return date.toLocaleString('de-DE', options);
};
// Beispiel: Funktion, die beim Neustart des Bots aufgerufen wird
function onBotRestart() {
lastRestart = new Date(); // Aktualisiere den letzten Neustart
}
// Endpunkt für den letzten Neustart
app.get('/api/last-restart', (req, res) => {
// Hier ist der letzte Neustart korrekt referenziert
res.json({ lastRestart: formatLastRestartDate(lastRestart) });
});
// Beispiel: Rufe die Funktion auf, wenn der Bot neu gestartet wird
onBotRestart();
app.post('/api/send-message', async (req, res) => {
const { message } = req.body;
// Überprüfen, ob die Nachricht leer ist
if (!message) {
return res.status(400).json({ success: false, error: 'Nachricht darf nicht leer sein.' });
}
try {
const users = yaml.load(USER_YML_PATH);
const sendMessages = Object.keys(users).map(userChatId => {
return bot.sendMessage(userChatId, `Systemnachricht\n\n"${message}"`).catch(error => {
logError(`Fehler beim Senden der Systemnachricht an chatId ${userChatId}: ${error.message}`);
});
}).filter(promise => promise !== undefined);
await Promise.all(sendMessages);
res.json({ success: true });
} catch (error) {
console.error('Fehler beim Senden der Nachricht an alle Benutzer:', error);
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/api/media-count', async (req, res) => {
try {
const mediaData = await fetchAllMedia(); // Stelle sicher, dass diese Funktion existiert
const { movieCount, showCount } = mediaData; // Zieht die Anzahl der Filme und Serien
res.json({ movieCount, showCount });
} catch (error) {
console.error('Fehler beim Abrufen der Medienanzahl:', error);
res.status(500).json({ error: 'Fehler beim Abrufen der Medienanzahl.' });
}
});
// Express-Endpunkt zum Empfangen des Wunsches
app.post('/api/telegram-wunsch', (req, res) => {
const { wunsch, type } = req.body;
// Überprüfe, ob req.user vorhanden ist und ob chatId existiert, andernfalls Dummy-ID verwenden
const chatId = req.user && req.user.chatId ? req.user.chatId : '123456789'; // Dummy chatId
sendWish(wunsch, type, chatId)
.then(() => {
res.json({ message: 'Dein Wunsch wurde erfolgreich gesendet!' });
})
.catch(err => {
console.error('Fehler beim Senden des Wunsches:', err);
res.status(500).json({ message: 'Fehler beim Senden deines Wunsches.' });
});
});
app.get('/api/admin-password', (req, res) => {
res.json({ password: process.env.ADMIN_PW });
});
const { exec } = require('child_process');
const path7zip = require('7zip-bin').path7za; // Pfad zu 7zip
const BACKUP_DIR = path.join(__dirname, 'backups');
const ZIP_PASSWORD = process.env.ZIP_PW; // Passwort aus der .env-Datei
// Sicherstellen, dass der Backup-Ordner existiert
if (!fs.existsSync(BACKUP_DIR)) {
fs.mkdirSync(BACKUP_DIR, { recursive: true });
}
// Middleware für statische Dateien
app.use('/backups', express.static(BACKUP_DIR));
// API-Endpunkt für das Erstellen eines Backups
app.post('/api/create-backup', (req, res) => {
const backupFileName = `backup_${Date.now()}.zip`;
const backupFilePath = path.join(BACKUP_DIR, backupFileName);
// Erstelle das Backup als ZIP mit Passwort
const command = `"${path7zip}" a -tzip "${backupFilePath}" * -p${ZIP_PASSWORD} -xr!backups -xr!node_modules`;
exec(command, { cwd: __dirname }, (err, stdout, stderr) => {
if (err) {
console.error('Fehler beim Erstellen des Backups:', err);
return res.status(500).json({ success: false, error: 'Fehler beim Erstellen des Backups' });
}
console.log(`Backup erfolgreich erstellt: ${backupFileName}`);
checkBackupCount(); // Überprüfe die Anzahl der Backups
res.json({ success: true, fileName: backupFileName });
});
});
// API-Endpunkt für das Abrufen der Backups
app.get('/api/backups', (req, res) => {
fs.readdir(BACKUP_DIR, (err, files) => {
if (err) {
console.error('Fehler beim Lesen des Backup-Verzeichnisses:', err);
return res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Backups' });
}
const backups = files.map(file => ({
name: file,
date: fs.statSync(path.join(BACKUP_DIR, file)).mtime,
}));
res.json({ success: true, backups });
});
});
// API-Endpunkt für das Löschen eines Backups
app.post('/api/delete-backup', (req, res) => {
const { backupName } = req.body;
fs.unlink(path.join(BACKUP_DIR, backupName), (err) => {
if (err) {
console.error('Fehler beim Löschen des Backups:', err);
return res.status(500).json({ success: false, error: 'Fehler beim Löschen des Backups' });
}
console.log(`Backup gelöscht: ${backupName}`);
res.json({ success: true });
});
});
// API-Endpunkt zum Herunterladen eines Backups
app.get('/api/download-backup/:backupName', (req, res) => {
const { backupName } = req.params;
const filePath = path.join(BACKUP_DIR, backupName);
// Überprüfen, ob die Datei existiert
if (fs.existsSync(filePath)) {
res.download(filePath, backupName, (err) => {
if (err) {
console.error('Fehler beim Herunterladen des Backups:', err);
res.status(500).json({ success: false, error: 'Fehler beim Herunterladen des Backups' });
}
});
} else {
res.status(404).json({ success: false, error: 'Backup nicht gefunden' });
}
});
// Funktion zur Überprüfung der Anzahl der Backups und ggf. Löschen älterer Backups
function checkBackupCount() {
const maxBackupCount = 5; // Maximale Anzahl an Backups
fs.readdir(BACKUP_DIR, (err, files) => {
if (err) {
return console.error('Fehler beim Überprüfen der Backups:', err);
}
if (files.length > maxBackupCount) {
const sortedFiles = files
.map(file => ({
name: file,
time: fs.statSync(path.join(BACKUP_DIR, file)).mtime.getTime(),
}))
.sort((a, b) => a.time - b.time);
// Lösche die ältesten Backups, um die Anzahl zu reduzieren
const filesToDelete = sortedFiles.slice(0, files.length - maxBackupCount);
filesToDelete.forEach(file => {
fs.unlink(path.join(BACKUP_DIR, file.name), (err) => {
if (err) {
console.error('Fehler beim Löschen alter Backups:', err);
} else {
console.log(`Altes Backup gelöscht: ${file.name}`);
}
});
});
}
});
}
app.post('/api/toggle-debug', (req, res) => {
debugMode = req.body.debugMode;
console.log(`Debug-Modus wurde ${debugMode ? 'aktiviert' : 'deaktiviert'}`);
res.json({ success: true, debugMode });
});
app.get('/api/debug-status', (req, res) => {
res.json({ debugMode });
});
// Beispiel-Endpoint für den Backup-Download
app.post('/api/download-backup', (req, res) => {
const { backupName, password } = req.body;
// Überprüfe das Passwort
if (password !== process.env.ADMIN_PW) {
return res.status(403).json({ success: false, error: 'Falsches Passwort' });
}
// Der Download-Link oder die Logik für den Backup-Download
const backupPath = `path/to/backups/${backupName}`;
if (fs.existsSync(backupPath)) {
res.json({ success: true, downloadUrl: `/backups/${backupName}` });
} else {
res.status(404).json({ success: false, error: 'Backup nicht gefunden' });
}
});
// API-Endpunkt zum Abrufen der Entwicklerberichte
app.get('/api/dev-reports', (req, res) => {
try {
const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH));
res.json(reports);
} catch (error) {
console.error('Fehler beim Laden der Entwicklerberichte:', error);
res.status(500).json({ message: 'Fehler beim Laden der Entwicklerberichte.' });
}
});
// Route zum Löschen eines Dev Reports
app.delete('/api/dev-reports', (req, res) => {
const reportId = parseInt(req.query.id, 10);
try {
const reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH));
const updatedReports = reports.filter(report => report.id !== reportId); // Lösche den Bericht
fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(updatedReports, null, 2)); // Datei aktualisieren
res.status(204).send(); // 204 No Content
} catch (error) {
console.error('Fehler beim Löschen des Berichts:', error);
res.status(500).send('Interner Serverfehler');
}
});
app.use(bodyParser.json());
// API zum Empfangen der Berichte von der HTML-Seite
app.post('/api/submit-report', (req, res) => {
const { type, user, message } = req.body;
// Falls keine Chat-ID vorhanden ist, generiere eine zufällige ID
const chatId = user.id || Math.floor(Math.random() * 1000000);
const newReport = {
id: Date.now(), // Verwende die aktuelle Zeit als eindeutige ID
type,
user: {
name: user.name || 'Anonym',
id: chatId
},
message,
timestamp: new Date().toISOString()
};
try {
// Berichte aus der Datei laden oder ein leeres Array verwenden
let reports = [];
if (fs.existsSync(DEV_REPORTS_FILE_PATH)) {
reports = JSON.parse(fs.readFileSync(DEV_REPORTS_FILE_PATH, 'utf-8'));
}
// Füge den neuen Bericht hinzu
reports.push(newReport);
// Datei aktualisieren
fs.writeFileSync(DEV_REPORTS_FILE_PATH, JSON.stringify(reports, null, 2));
// Optional: Senden des Berichts an Telegram
sendToTelegram(newReport);
res.status(200).json({ message: 'Bericht erfolgreich übermittelt.' });
} catch (error) {
console.error('Fehler beim Schreiben des Berichts:', error);
res.status(500).json({ message: 'Fehler beim Schreiben des Berichts.' });
}
});
function sendToTelegram(report) {
const messageTemplate = `📩 ${report.type}\n\nvon: ${report.user.name} (${report.user.id})\n\n"${report.message}"`;
// Telegram API URL
const telegramApiUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`;
// Sende die Nachricht
axios.post(telegramApiUrl, {
chat_id: DEV_CHAT_ID, // Sende an die in der .env gespeicherte Dev Chat ID
text: messageTemplate,
parse_mode: 'Markdown' // Formatierung der Nachricht
})
.then(response => {
console.log('Nachricht erfolgreich an Telegram gesendet:', response.data);
})
.catch(error => {
console.error('Fehler beim Senden der Nachricht an Telegram:', error);
});
}
// Ende Frontend
/// Definition der logDebug-Funktion
function logDebug(message) {
console.log(`${new Date().toISOString()} - DEBUG: ${message}`);
}
// 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...');