From ae5767302994c504641f611455e3236bcc70f76d Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:42:16 +0200 Subject: [PATCH] modified: app.py modified: bot.py modified: templates/navigation.html modified: templates/privacy_policy.html new file: templates/user_contact.html --- app.py | 117 ++++++++++ bot.py | 371 +++++++++++++++++++++++++++++++ templates/navigation.html | 5 + templates/privacy_policy.html | 118 ++++++++-- templates/user_contact.html | 407 ++++++++++++++++++++++++++++++++++ 5 files changed, 999 insertions(+), 19 deletions(-) create mode 100644 templates/user_contact.html diff --git a/app.py b/app.py index f160afa..bc83c44 100644 --- a/app.py +++ b/app.py @@ -898,6 +898,123 @@ def terms_of_service(): """Zeigt die Terms of Service Seite an.""" return render_template("terms_of_service.html") +@app.route("/user-contact", methods=["GET", "POST"]) +def user_contact(): + """Kontaktformular fΓΌr eingeloggte Benutzer mit automatischer Discord DM""" + if not g.user_info: + flash("Please log in to access the contact form.", "warning") + return redirect(url_for("login")) + + if request.method == "POST": + try: + # Form data sammeln + subject = request.form.get("subject", "").strip() + category = request.form.get("category", "").strip() + priority = request.form.get("priority", "").strip() + message = request.form.get("message", "").strip() + server_context = request.form.get("server_context", "").strip() + + # Validierung + if not all([subject, category, priority, message]): + flash("Please fill in all required fields.", "danger") + return render_template("user_contact.html", user_info=g.user_info) + + # Discord User Info + user_info = g.user_info + + # Priority emoji mapping + priority_emojis = { + "low": "🟒", + "medium": "🟑", + "high": "🟠", + "urgent": "πŸ”΄" + } + + # Category emoji mapping + category_emojis = { + "bug_report": "πŸ›", + "feature_request": "πŸ’‘", + "account_issue": "πŸ‘€", + "moderation": "πŸ›‘οΈ", + "giveaway": "🎁", + "privacy": "πŸ”’", + "technical": "βš™οΈ", + "other": "❓" + } + + # Discord Message erstellen + embed_content = f"""**πŸ“¨ New Contact Form Submission** + +**User Information:** +β€’ **Name:** {user_info.get('global_name', user_info.get('username', 'Unknown'))} +β€’ **Username:** {user_info.get('username', 'Unknown')}#{user_info.get('discriminator', '0000')} +β€’ **Discord ID:** `{user_info.get('id', 'Unknown')}` +β€’ **Avatar:** {user_info.get('avatar_url', 'No avatar')} + +**Message Details:** +β€’ **Subject:** {subject} +β€’ **Category:** {category_emojis.get(category, '❓')} {category.replace('_', ' ').title()} +β€’ **Priority:** {priority_emojis.get(priority, 'βšͺ')} {priority.upper()} +{f"β€’ **Server Context:** {server_context}" if server_context else ""} + +**Message:** +``` +{message} +``` + +**Submitted at:** +**From:** Multus Bot Web Panel""" + + # Discord DM via Bot senden + import requests + import time + + # Ihre Discord User ID hier eintragen + YOUR_DISCORD_ID = "253922739709018114" # Ersetzen Sie dies mit Ihrer Discord ID + + try: + # Versuche die Nachricht ΓΌber den Bot zu senden + connection = get_db_connection() + cursor = connection.cursor() + + # In Datenbank speichern + cursor.execute(""" + INSERT INTO contact_messages ( + user_id, username, subject, category, priority, message, + server_context, submitted_at, status + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) + """, ( + user_info.get('id'), + f"{user_info.get('username')}#{user_info.get('discriminator')}", + subject, + category, + priority, + message, + server_context, + int(time.time()), + 'pending' + )) + connection.commit() + + flash("Your message has been submitted successfully! We'll process it and get back to you within 15 minutes via Discord DM.", "success") + + cursor.close() + connection.close() + + except Exception as e: + print(f"Error saving contact message: {e}") + flash("There was an error submitting your message. Please try again or contact us directly on Discord.", "danger") + + except Exception as e: + print(f"Error saving contact message: {e}") + flash("There was an error submitting your message. Please try again or contact us directly on Discord.", "danger") + + except Exception as e: + print(f"Error processing contact form: {e}") + flash("An unexpected error occurred. Please try again.", "danger") + + return render_template("user_contact.html", user_info=g.user_info) + @app.route("/privacy-policy") def privacy_policy(): """Zeigt die Privacy Policy Seite an.""" diff --git a/bot.py b/bot.py index 81fe745..2c1c458 100644 --- a/bot.py +++ b/bot.py @@ -687,6 +687,81 @@ async def process_manager(): except Exception as e: logger.error(f"Error in process manager: {e}") +# Contact Messages Task +@tasks.loop(minutes=15) +async def check_contact_messages(): + """Automatically checks for pending contact messages every 15 minutes""" + try: + connection = connect_to_database() + cursor = connection.cursor() + + # Hole alle pending Nachrichten + cursor.execute(""" + SELECT id, user_id, username, subject, category, priority, message, + server_context, submitted_at + FROM contact_messages + WHERE status = 'pending' + ORDER BY submitted_at ASC + """) + + pending_messages = cursor.fetchall() + + if pending_messages: + logger.info(f"Found {len(pending_messages)} pending contact messages") + + processed_count = 0 + for msg in pending_messages: + msg_id, user_id, username, subject, category, priority, message, server_context, submitted_at = msg + + try: + # Hole User-Informationen von Discord + user = client.get_user(int(user_id)) + if not user: + try: + user = await client.fetch_user(int(user_id)) + except: + user = None + + message_data = { + 'user_id': user_id, + 'user_name': user.display_name if user else username.split('#')[0], + 'username': username, + 'avatar_url': user.avatar.url if user and user.avatar else None, + 'subject': subject, + 'category': category, + 'priority': priority, + 'message': message, + 'server_context': server_context + } + + # Sende an Admin + success = await send_contact_message_to_admin(message_data) + + if success: + # Markiere als versendet + cursor.execute(""" + UPDATE contact_messages + SET status = 'sent', responded_at = %s + WHERE id = %s + """, (int(time.time()), msg_id)) + processed_count += 1 + logger.info(f"Sent contact message {msg_id} to admin") + + except Exception as e: + logger.error(f"Error processing contact message {msg_id}: {e}") + continue + + connection.commit() + + if processed_count > 0: + logger.info(f"Automatically processed {processed_count} contact messages") + + cursor.close() + close_database_connection(connection) + + except Exception as e: + logger.error(f"Error in check_contact_messages task: {e}") + async def handle_expired_process(process_uuid, process_type, data): """Handles different types of expired processes""" try: @@ -1775,6 +1850,11 @@ async def on_ready(): if not process_manager.is_running(): process_manager.start() + # Start the contact messages checker + if not check_contact_messages.is_running(): + check_contact_messages.start() + logger.info("Contact messages checker started (15-minute intervals)") + logger.info("Bot is ready!") logger.info(f"Logged in as: {client.user.name}") logger.info(f"Client ID: {client.user.id}") @@ -2883,6 +2963,137 @@ def create_warnings_table(): if connection: close_database_connection(connection) +def create_contact_messages_table(): + """Creates the contact_messages table if it doesn't exist""" + connection = None + cursor = None + try: + connection = connect_to_database() + cursor = connection.cursor() + + create_table_query = """ + CREATE TABLE IF NOT EXISTS contact_messages ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id VARCHAR(50) NOT NULL, + username VARCHAR(100) NOT NULL, + subject VARCHAR(100) NOT NULL, + category VARCHAR(50) NOT NULL, + priority VARCHAR(20) NOT NULL, + message TEXT NOT NULL, + server_context VARCHAR(200), + submitted_at BIGINT NOT NULL, + status VARCHAR(20) DEFAULT 'pending', + responded_at BIGINT, + response TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX idx_user_id (user_id), + INDEX idx_status (status), + INDEX idx_submitted_at (submitted_at) + ) + """ + + cursor.execute(create_table_query) + connection.commit() + logger.info("Contact messages table checked/created successfully") + + except Exception as e: + logger.error(f"Error creating contact messages table: {e}") + finally: + if cursor: + cursor.close() + if connection: + close_database_connection(connection) + +async def send_contact_message_to_admin(message_data): + """Sends a contact message to the admin via Discord DM""" + try: + # Your Discord User ID + ADMIN_DISCORD_ID = 253922739709018114 + + admin_user = client.get_user(ADMIN_DISCORD_ID) + if not admin_user: + admin_user = await client.fetch_user(ADMIN_DISCORD_ID) + + if admin_user: + # Priority emoji mapping + priority_emojis = { + "low": "🟒", + "medium": "🟑", + "high": "🟠", + "urgent": "πŸ”΄" + } + + # Category emoji mapping + category_emojis = { + "bug_report": "πŸ›", + "feature_request": "πŸ’‘", + "account_issue": "πŸ‘€", + "moderation": "πŸ›‘οΈ", + "giveaway": "🎁", + "privacy": "πŸ”’", + "technical": "βš™οΈ", + "other": "❓" + } + + # Create Discord Embed + embed = discord.Embed( + title="πŸ“¨ New Contact Form Submission", + color=0x667eea, + timestamp=datetime.utcnow() + ) + + # User Information + embed.add_field( + name="πŸ‘€ User Information", + value=f"**Name:** {message_data.get('user_name', 'Unknown')}\n" + f"**Username:** {message_data.get('username', 'Unknown')}\n" + f"**Discord ID:** `{message_data.get('user_id', 'Unknown')}`", + inline=False + ) + + # Message Details + embed.add_field( + name="πŸ“‹ Message Details", + value=f"**Subject:** {message_data.get('subject', 'No subject')}\n" + f"**Category:** {category_emojis.get(message_data.get('category', 'other'), '❓')} {message_data.get('category', 'other').replace('_', ' ').title()}\n" + f"**Priority:** {priority_emojis.get(message_data.get('priority', 'low'), 'βšͺ')} {message_data.get('priority', 'low').upper()}", + inline=False + ) + + # Server Context (if provided) + if message_data.get('server_context'): + embed.add_field( + name="πŸ–₯️ Server Context", + value=message_data.get('server_context'), + inline=False + ) + + # Message Content + message_content = message_data.get('message', 'No message content') + if len(message_content) > 1024: + message_content = message_content[:1021] + "..." + + embed.add_field( + name="πŸ’¬ Message", + value=f"```{message_content}```", + inline=False + ) + + # Footer + embed.set_footer(text="Multus Bot Web Panel Contact Form") + + # Set avatar thumbnail if available + if message_data.get('avatar_url'): + embed.set_thumbnail(url=message_data.get('avatar_url')) + + await admin_user.send(embed=embed) + logger.info(f"Contact message sent to admin for user {message_data.get('user_id')}") + return True + + except Exception as e: + logger.error(f"Error sending contact message to admin: {e}") + return False + async def get_user_warnings(user_id, guild_id, active_only=True): """Retrieves warning records for a user @@ -5263,9 +5474,169 @@ async def delnotes(ctx): else: await send_response(f"No notes found for user {ctx.author.name}.") +@client.command() +async def process_contact_messages(ctx): + """Prozessiert ausstehende Kontaktnachrichten (Admin only)""" + if ctx.author.id != 253922739709018114: # Ihre Discord ID + await ctx.send("You don't have permission to use this command.") + return + + try: + connection = connect_to_database() + cursor = connection.cursor() + + # Hole alle pending Nachrichten + cursor.execute(""" + SELECT id, user_id, username, subject, category, priority, message, + server_context, submitted_at + FROM contact_messages + WHERE status = 'pending' + ORDER BY submitted_at ASC + """) + + pending_messages = cursor.fetchall() + + if not pending_messages: + await ctx.send("No pending contact messages found.") + cursor.close() + close_database_connection(connection) + return + + processed_count = 0 + for msg in pending_messages: + msg_id, user_id, username, subject, category, priority, message, server_context, submitted_at = msg + + try: + # Hole User-Informationen von Discord + user = client.get_user(int(user_id)) + if not user: + user = await client.fetch_user(int(user_id)) + + message_data = { + 'user_id': user_id, + 'user_name': user.display_name if user else username.split('#')[0], + 'username': username, + 'avatar_url': user.avatar.url if user and user.avatar else None, + 'subject': subject, + 'category': category, + 'priority': priority, + 'message': message, + 'server_context': server_context + } + + # Sende an Admin + success = await send_contact_message_to_admin(message_data) + + if success: + # Markiere als versendet + cursor.execute(""" + UPDATE contact_messages + SET status = 'sent', responded_at = %s + WHERE id = %s + """, (int(time.time()), msg_id)) + processed_count += 1 + + except Exception as e: + logger.error(f"Error processing contact message {msg_id}: {e}") + continue + + connection.commit() + cursor.close() + close_database_connection(connection) + + await ctx.send(f"Processed {processed_count}/{len(pending_messages)} contact messages successfully.") + + except Exception as e: + await ctx.send(f"Error processing contact messages: {e}") + logger.error(f"Error in process_contact_messages: {e}") + +@client.command() +async def contact_status(ctx): + """Zeigt den Status der Kontaktnachrichten-Überwachung (Admin only)""" + if ctx.author.id != 253922739709018114: # Ihre Discord ID + await ctx.send("You don't have permission to use this command.") + return + + try: + connection = connect_to_database() + cursor = connection.cursor() + + # Statistiken abrufen + cursor.execute("SELECT COUNT(*) FROM contact_messages WHERE status = 'pending'") + pending_count = cursor.fetchone()[0] + + cursor.execute("SELECT COUNT(*) FROM contact_messages WHERE status = 'sent'") + sent_count = cursor.fetchone()[0] + + cursor.execute("SELECT COUNT(*) FROM contact_messages") + total_count = cursor.fetchone()[0] + + # Letzte Nachricht info + cursor.execute(""" + SELECT subject, submitted_at FROM contact_messages + ORDER BY submitted_at DESC LIMIT 1 + """) + last_message = cursor.fetchone() + + cursor.close() + close_database_connection(connection) + + # Status des Task Loops + task_status = "🟒 Running" if check_contact_messages.is_running() else "πŸ”΄ Stopped" + + # NΓ€chste AusfΓΌhrung berechnen + if check_contact_messages.is_running(): + next_run = check_contact_messages.next_iteration + if next_run: + next_run_str = f"" + else: + next_run_str = "Soon" + else: + next_run_str = "Task not running" + + embed = discord.Embed( + title="πŸ“Š Contact Messages Status", + color=0x667eea, + timestamp=datetime.utcnow() + ) + + embed.add_field( + name="πŸ“‹ Message Statistics", + value=f"**Pending:** {pending_count}\n" + f"**Processed:** {sent_count}\n" + f"**Total:** {total_count}", + inline=True + ) + + embed.add_field( + name="⏰ Task Status", + value=f"**Status:** {task_status}\n" + f"**Interval:** Every 15 minutes\n" + f"**Next Check:** {next_run_str}", + inline=True + ) + + if last_message: + last_subject = last_message[0][:50] + "..." if len(last_message[0]) > 50 else last_message[0] + embed.add_field( + name="πŸ“ Latest Message", + value=f"**Subject:** {last_subject}\n" + f"**Submitted:** ", + inline=False + ) + + embed.set_footer(text="Contact Messages Auto-Check System") + + await ctx.send(embed=embed) + + except Exception as e: + await ctx.send(f"Error getting contact status: {e}") + logger.error(f"Error in contact_status: {e}") + try: # Initialize database tables create_warnings_table() + create_contact_messages_table() logger.info("Database tables initialized successfully") loop.run_until_complete(client.start(TOKEN)) diff --git a/templates/navigation.html b/templates/navigation.html index 751c6df..2bc83dc 100644 --- a/templates/navigation.html +++ b/templates/navigation.html @@ -52,6 +52,11 @@ Server Selection + {% if g.is_admin %}
  • Service Operation: To provide moderation, management, and administrative features
  • User Experience: To personalize your experience and maintain user preferences
  • Moderation: To enforce server rules and maintain community safety
  • -
  • Analytics: To understand usage patterns and improve service quality
  • +
  • Analytics: To understand usage patterns and improve service quality (anonymized where possible)
  • Communication: To send important updates and respond to support requests
  • Security: To detect and prevent abuse, spam, and security threats
  • @@ -290,12 +313,24 @@ We do not sell, trade, or rent your personal information to third parties. We may share your information only in the following circumstances:

      -
    • Discord Platform: As required for bot functionality through Discord's API
    • -
    • Server Administrators: Moderation data may be visible to server moderators and administrators
    • +
    • Discord Platform: As required for bot functionality through Discord's API (Discord Inc., USA - adequate protection under Privacy Shield successor)
    • +
    • Server Administrators: Moderation data may be visible to server moderators and administrators within your Discord server
    • Legal Requirements: When required by law, court order, or government regulation
    • -
    • Service Providers: With trusted third-party services that help us operate (hosting, analytics)
    • +
    • Service Providers: With trusted third-party services that help us operate (hosting providers within EU/EEA)
    • Safety and Security: To protect the rights, property, or safety of our users or others
    + +
    +

    International Data Transfers

    +

    + Some of our service providers may be located outside the European Economic Area (EEA). In such cases: +

    +
      +
    • We ensure adequate protection through approved mechanisms (adequacy decisions, standard contractual clauses)
    • +
    • Discord Inc. (USA) is covered by appropriate safeguards for international transfers
    • +
    • We minimize data transfers outside the EEA where possible
    • +
    +
    @@ -325,18 +360,37 @@ 7. Your Rights and Choices

    - You have the following rights regarding your personal information: + Under the General Data Protection Regulation (GDPR), you have the following rights regarding your personal information:

    -
      -
    • Access: Request information about what personal data we have about you
    • -
    • Correction: Request correction of inaccurate or incomplete data
    • -
    • Deletion: Request deletion of your personal data (subject to retention requirements)
    • -
    • Portability: Request a copy of your data in a machine-readable format
    • -
    • Objection: Object to processing of your data for certain purposes
    • -
    • Restriction: Request restriction of processing under certain circumstances
    • -
    + +
    +

    Your GDPR Rights

    +
      +
    • Right of Access (Art. 15): Request information about what personal data we have about you
    • +
    • Right to Rectification (Art. 16): Request correction of inaccurate or incomplete data
    • +
    • Right to Erasure (Art. 17): Request deletion of your personal data ("right to be forgotten")
    • +
    • Right to Restrict Processing (Art. 18): Request restriction of processing under certain circumstances
    • +
    • Right to Data Portability (Art. 20): Request a copy of your data in a machine-readable format
    • +
    • Right to Object (Art. 21): Object to processing of your data for certain purposes
    • +
    • Right to Withdraw Consent (Art. 7): Withdraw consent at any time where processing is based on consent
    • +
    +
    + +
    +

    How to Exercise Your Rights

    +

    + To exercise these rights, please contact us through our contact form + or Discord (@simolzimol). We will respond to your request within 30 days as required by GDPR. +

    +

    + Right to Lodge a Complaint: You have the right to lodge a complaint with your local data protection authority + if you believe we have not adequately addressed your concerns. +

    +
    +

    - To exercise these rights, please contact us through our contact form. + Note: Some rights may be limited by applicable law or necessary for the legitimate operation of our service. + We will inform you of any such limitations when responding to your request.

    @@ -379,19 +433,45 @@ 10. Changes to This Privacy Policy

    - We may update this Privacy Policy from time to time. We will notify you of any material changes by: + We may update this Privacy Policy from time to time to reflect changes in our practices or applicable law. We will notify you of any material changes by:

      -
    • Posting the new Privacy Policy on this page
    • -
    • Updating the "Last Updated" date at the top of this policy
    • -
    • Providing notice through our service or other communication methods
    • +
    • Posting the new Privacy Policy on this page with an updated "Last Updated" date
    • +
    • Providing prominent notice through our service for significant changes
    • +
    • Sending direct notification where we have your contact information (for material changes affecting your rights)

    Your continued use of the Service after the effective date of the revised Privacy Policy - constitutes acceptance of the revised policy. + constitutes acceptance of the revised policy. If you do not agree to the changes, please stop using our service.

    +
    +

    + + 11. Legal Compliance and Supervisory Authority +

    +
    +

    GDPR Compliance

    +

    + This service is operated in compliance with the General Data Protection Regulation (EU) 2016/679. + As a service primarily targeting EU users, we adhere to GDPR requirements regardless of your location. +

    +
    + +
    +

    Supervisory Authority Contact

    +

    + If you believe we have not adequately addressed your data protection concerns, you have the right to lodge a complaint with: +

    +
      +
    • Your local data protection authority in your EU member state
    • +
    • The lead supervisory authority where our main establishment is located
    • +
    • You can find your local DPA contact information at: European Data Protection Board
    • +
    +
    +
    +

    diff --git a/templates/user_contact.html b/templates/user_contact.html new file mode 100644 index 0000000..e1ba814 --- /dev/null +++ b/templates/user_contact.html @@ -0,0 +1,407 @@ + + + + + + Contact Support - Multus Bot + + + + + + + {% include 'navigation.html' %} + +
    +
    +
    +

    + + Contact Support +

    +

    Need help? Send us a message and we'll get back to you soon!

    +
    + + {% if user_info %} + + {% endif %} + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
    + + {{ message }} +
    + {% endfor %} + {% endif %} + {% endwith %} + +
    +
    How it works
    +
      +
    • Your Discord information is automatically included with your message
    • +
    • Messages are sent directly to the bot administrator via Discord DM
    • +
    • We typically respond within 24 hours
    • +
    • For urgent issues, please join our Discord server for faster support
    • +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + +
    + +
    +
    +
    + Low
    + General inquiry +
    +
    +
    + Medium
    + Need assistance +
    +
    +
    + High
    + Important issue +
    +
    +
    + Urgent
    + Critical problem +
    +
    + +
    + +
    + + + Maximum 2000 characters +
    + +
    + + +
    + +
    + +
    +
    +
    +
    + + {% include 'footer.html' %} + + + +