feat: Add markdown server for legal documents

Integrate and start a new markdown server to host the Terms of Service (TOS) and Privacy Policy documents. This server runs in a separate thread on port 5006, making these legal documents accessible via HTTP.
This commit is contained in:
Slipstream 2025-05-21 13:34:16 -06:00
parent d98a69769b
commit 0386b9f4ff
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
4 changed files with 420 additions and 0 deletions

74
PRIVACY_POLICY.md Normal file
View File

@ -0,0 +1,74 @@
# Privacy Policy
## 1. Introduction
This Privacy Policy explains how our Discord bot collects, uses, and protects your data. We are committed to ensuring the privacy and security of all users.
## 2. Information We Collect
### 2.1 Automatically Collected Information
- **Discord User ID**: Used to identify users across sessions
- **Server ID**: Used to apply server-specific settings
- **Channel ID**: Used for command context and conversation tracking
- **Message Content**: Processed for command execution and AI responses
- **Message Metadata**: Timestamps and message IDs for functionality
### 2.2 Optional Information
- **Conversation History**: Stored to maintain context in AI conversations
- **User Settings**: Preferences for bot interactions
- **Economy & Leveling Data**: Progress in bot game systems
- **Command Usage**: Cooldowns and usage statistics
## 3. How We Use Your Information
We use the collected information solely for the purpose of providing bot functionality:
- Processing and responding to commands
- Maintaining conversation context for AI features
- Storing game progress and economy data
- Applying user-specific settings and preferences
- Implementing moderation features when enabled
## 4. Data Storage and Security
- **Local Storage**: Most data is stored locally on secure servers
- **Databases**: We use SQLite, PostgreSQL, and ChromaDB for data storage
- **Redis**: Used for caching to improve performance
- **Security Measures**: We implement reasonable security measures to protect your data
## 5. Third-Party Services
Our bot may use the following third-party services:
- **OpenRouter/AI API Providers**: For AI conversation features
- **Tavily**: For web search functionality when enabled
- **Discord API**: For core bot functionality
Each third-party service has its own Privacy Policy governing the data they process.
## 6. Data Retention
- **Conversation Data**: Retained to maintain context but may be deleted upon request
- **User Settings**: Retained until you remove the bot or request deletion
- **Game/Economy Data**: Retained until you remove the bot or request deletion
- **Command Logs**: Typically retained for a limited time for debugging purposes
## 7. Your Rights
You have the right to:
- Access the personal data we hold about you
- Request deletion of your data
- Opt out of certain features that collect data
- Contact us with privacy concerns
## 8. Children's Privacy
Our bot is not directed at children under 13. We do not knowingly collect personal information from children under 13.
## 9. Changes to This Policy
We may update this Privacy Policy from time to time. We will notify users of any significant changes.
## 10. Contact
If you have questions about this Privacy Policy, please contact the bot owner through Discord.
Last Updated: May 21, 2025

57
TOS.md Normal file
View File

@ -0,0 +1,57 @@
# Terms of Service
## 1. Acceptance of Terms
By adding this bot to your Discord server or using any of its features, you agree to these Terms of Service. If you do not agree to these terms, please do not use the bot.
## 2. Description of Service
This Discord bot provides various features including but not limited to:
- AI-powered conversation and roleplay
- Economy and leveling systems
- Games and entertainment commands
- Moderation and logging tools
- Image generation and manipulation
- Utility commands
## 3. User Conduct
You agree not to:
- Use the bot for any illegal purposes
- Attempt to exploit, abuse, or bypass any limitations of the bot
- Use the bot to harass, threaten, or impersonate others
- Interfere with the proper functioning of the bot
- Access the bot through automated means (except through Discord's official API)
## 4. Content Responsibility
You are solely responsible for any content you create, share, or generate through the bot. This includes:
- Messages sent to or through the bot
- Images created or modified using the bot
- Any other content generated through interaction with the bot
## 5. Intellectual Property
All rights, title, and interest in and to the bot, including all intellectual property rights, are and will remain the exclusive property of the bot owner. The bot may contain third-party software or services that are subject to their own terms and conditions.
## 6. Limitation of Liability
The bot is provided "as is" without warranties of any kind. In no event shall the bot owner be liable for any damages arising out of or in connection with the use or inability to use the bot.
## 7. Service Availability
The bot owner reserves the right to modify, suspend, or discontinue the bot or any part thereof at any time without notice. The bot may experience downtime for maintenance or updates.
## 8. Changes to Terms
These Terms of Service may be updated from time to time. Continued use of the bot after any such changes constitutes your consent to such changes.
## 9. Termination
The bot owner reserves the right to terminate your access to the bot for violation of these terms or for any other reason at their sole discretion.
## 10. Contact
If you have any questions about these Terms of Service, please contact the bot owner through Discord.
Last Updated: May 21, 2025

12
main.py
View File

@ -30,6 +30,9 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from run_unified_api import start_api_in_thread
import discord_bot_sync_api # Import the module to set the cog instance
# Import the markdown server
from run_markdown_server import start_markdown_server_in_thread
# Check if API dependencies are available
try:
import uvicorn
@ -543,6 +546,15 @@ async def main(args): # Pass parsed args
except Exception as e:
print(f"Failed to start unified API service: {e}")
# Start the markdown server for TOS and Privacy Policy
markdown_thread = None
try:
print("Starting markdown server for TOS and Privacy Policy...")
markdown_thread = start_markdown_server_in_thread(host="0.0.0.0", port=5006)
print("Markdown server started successfully. TOS available at: http://localhost:5006/tos")
except Exception as e:
print(f"Failed to start markdown server: {e}")
# Configure OAuth settings from environment variables
oauth_host = os.getenv("OAUTH_HOST", "0.0.0.0")
oauth_port = int(os.getenv("OAUTH_PORT", "8080"))

277
run_markdown_server.py Normal file
View File

@ -0,0 +1,277 @@
import os
import sys
import subprocess
import time
import signal
import atexit
import threading
import logging
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
log = logging.getLogger(__name__)
# Try to import markdown, but provide a fallback if it's not available
try:
import markdown
import markdown.extensions.fenced_code
import markdown.extensions.tables
MARKDOWN_AVAILABLE = True
except ImportError:
MARKDOWN_AVAILABLE = False
log.warning("markdown package not available. Will serve raw markdown files.")
# Create the FastAPI app
app = FastAPI(title="Markdown Server", docs_url=None, redoc_url=None)
# Define the HTML template for rendering markdown
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
border-bottom: 2px solid #eaecef;
padding-bottom: 0.3em;
color: #24292e;
}
h2 {
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
color: #24292e;
}
code {
background-color: #f6f8fa;
padding: 0.2em 0.4em;
border-radius: 3px;
font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
}
pre {
background-color: #f6f8fa;
padding: 16px;
border-radius: 6px;
overflow: auto;
}
pre code {
background-color: transparent;
padding: 0;
}
a {
color: #0366d6;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 16px;
}
table, th, td {
border: 1px solid #dfe2e5;
}
th, td {
padding: 8px 16px;
text-align: left;
}
th {
background-color: #f6f8fa;
}
tr:nth-child(even) {
background-color: #f6f8fa;
}
</style>
</head>
<body>
{content}
</body>
</html>
"""
# Function to read and convert markdown to HTML
def render_markdown(file_path, title):
try:
with open(file_path, 'r', encoding='utf-8') as f:
md_content = f.read()
if MARKDOWN_AVAILABLE:
# Convert markdown to HTML with extensions
html_content = markdown.markdown(
md_content,
extensions=[
'markdown.extensions.fenced_code',
'markdown.extensions.tables',
'markdown.extensions.toc'
]
)
else:
# Simple fallback if markdown package is not available
# Just wrap the content in <pre> tags to preserve formatting
html_content = f"<pre style='white-space: pre-wrap;'>{md_content}</pre>"
# Insert the HTML content into the template
return HTML_TEMPLATE.format(title=title, content=html_content)
except Exception as e:
return HTML_TEMPLATE.format(
title="Error",
content=f"<h1>Error</h1><p>Failed to render markdown: {str(e)}</p>"
)
# Routes for TOS and Privacy Policy
@app.get("/tos", response_class=HTMLResponse)
async def get_tos(request: Request):
return render_markdown("TOS.md", "Terms of Service")
@app.get("/privacy", response_class=HTMLResponse)
async def get_privacy(request: Request):
return render_markdown("PRIVACY_POLICY.md", "Privacy Policy")
# Root route that redirects to TOS
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
return """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bot Legal Documents</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
h1 {
margin-bottom: 30px;
}
.links {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 30px;
}
.link-button {
display: inline-block;
padding: 10px 20px;
background-color: #0366d6;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
}
.link-button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<h1>Discord Bot Legal Documents</h1>
<p>Please review our Terms of Service and Privacy Policy.</p>
<div class="links">
<a href="/tos" class="link-button">Terms of Service</a>
<a href="/privacy" class="link-button">Privacy Policy</a>
</div>
</body>
</html>
"""
# Function to start the server in a thread
def start_markdown_server_in_thread(host="0.0.0.0", port=5006):
"""Start the markdown server in a separate thread."""
log.info(f"Starting markdown server on {host}:{port}...")
def run_server():
try:
uvicorn.run(app, host=host, port=port)
except Exception as e:
log.exception(f"Error running markdown server: {e}")
# Start the server in a daemon thread
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
log.info(f"Markdown server thread started. TOS available at: http://{host}:{port}/tos")
return server_thread
def start_server():
"""Start the markdown server as a background process (legacy method)."""
print("Starting markdown server on port 5006...")
# Get the directory of this script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Start the server as a subprocess
server_process = subprocess.Popen(
[sys.executable, os.path.join(script_dir, "markdown_server.py")],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True
)
# Register a function to terminate the server when this script exits
def cleanup():
if server_process.poll() is None: # If process is still running
print("Stopping markdown server...")
server_process.terminate()
try:
server_process.wait(timeout=5)
except subprocess.TimeoutExpired:
print("Server didn't terminate gracefully, forcing...")
server_process.kill()
atexit.register(cleanup)
# Handle signals
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, lambda signum, frame: sys.exit(0))
# Wait a moment for the server to start
time.sleep(2)
# Check if the server started successfully
if server_process.poll() is not None:
print("Failed to start server. Exit code:", server_process.returncode)
output, _ = server_process.communicate()
print("Server output:", output)
return False
print(f"Markdown server running on http://localhost:5006")
print("TOS available at: http://localhost:5006/tos")
print("Privacy Policy available at: http://localhost:5006/privacy")
return True
def run_as_daemon():
"""Run the server as a daemon process."""
if start_server():
# Keep the script running to maintain the server
try:
while True:
time.sleep(60) # Sleep to reduce CPU usage
except KeyboardInterrupt:
print("Received keyboard interrupt. Shutting down...")
sys.exit(0)
if __name__ == "__main__":
# If run directly, start the server in the main thread
log.info("Starting markdown server on port 5006...")
uvicorn.run(app, host="0.0.0.0", port=5006)