This commit is contained in:
Slipstream 2025-04-25 14:21:15 -06:00
parent 46edfad5d5
commit 7dcbfc2be2
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
3 changed files with 3 additions and 435 deletions

3
.gitignore vendored
View File

@ -6,6 +6,9 @@ venv/
certs/
data/
.env
IMG/
OUTPUT/
SOUND/
# C extensions
*.so

View File

@ -1,106 +0,0 @@
# Discord Sync Integration
This document explains how to set up the Discord OAuth integration between your Flutter app and Discord bot.
## Overview
The integration allows users to:
1. Log in with their Discord account
2. Sync conversations between the Flutter app and Discord bot
3. Import conversations from Discord to the Flutter app
4. Export conversations from the Flutter app to Discord
## Setup Instructions
### 1. Discord Developer Portal Setup
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
2. Click "New Application" and give it a name (e.g., "OpenRouter GUI")
3. Go to the "OAuth2" section
4. Add a redirect URL: `openroutergui://auth`
5. Copy the "Client ID" - you'll need this for the Flutter app
### 2. Flutter App Setup
1. Open `lib/services/discord_oauth_service.dart`
2. Replace `YOUR_DISCORD_CLIENT_ID` with the Client ID from the Discord Developer Portal:
```dart
static const String clientId = 'YOUR_DISCORD_CLIENT_ID';
```
3. Open `lib/services/sync_service.dart`
4. Replace `YOUR_BOT_API_URL` with the URL where your Discord bot's API will be running:
```dart
static const String botApiUrl = 'YOUR_BOT_API_URL';
```
### 3. Discord Bot Setup
1. Copy the `discord_bot_sync_api.py` file to your Discord bot project
2. Install the required dependencies:
```bash
pip install fastapi uvicorn pydantic
```
3. Add the following code to your main bot file (e.g., `bot.py`):
```python
import threading
import uvicorn
def run_api():
uvicorn.run("discord_bot_sync_api:app", host="0.0.0.0", port=8000)
# Start the API in a separate thread
api_thread = threading.Thread(target=run_api)
api_thread.daemon = True
api_thread.start()
```
4. Modify your `ai_cog.py` file to integrate with the sync API:
```python
from discord_bot_sync_api import save_discord_conversation, load_conversations, user_conversations
# In your _get_ai_response method, after getting the response:
messages = conversation_history[user_id]
save_discord_conversation(str(user_id), messages, settings["model"])
# Add a command to view sync status:
@commands.command(name="aisync")
async def ai_sync_status(self, ctx: commands.Context):
user_id = str(ctx.author.id)
if user_id not in user_conversations or not user_conversations[user_id]:
await ctx.reply("You don't have any synced conversations.")
return
synced_count = len(user_conversations[user_id])
await ctx.reply(f"You have {synced_count} synced conversations that can be accessed from the Flutter app.")
```
### 4. Network Configuration
1. Make sure your Discord bot's API is accessible from the internet
2. You can use a service like [ngrok](https://ngrok.com/) for testing:
```bash
ngrok http 8000
```
3. Use the ngrok URL as your `YOUR_BOT_API_URL` in the Flutter app
## Usage
1. In the Flutter app, go to Settings > Discord Integration
2. Click "Login with Discord" to authenticate
3. Use the "Sync Conversations" button to sync conversations
4. Use the "Import from Discord" button to import conversations from Discord
## Troubleshooting
- **Authentication Issues**: Make sure the Client ID is correct and the redirect URL is properly configured
- **Sync Issues**: Check that the bot API URL is accessible and the API is running
- **Import/Export Issues**: Verify that the Discord bot has saved conversations to sync
## Security Considerations
- The integration uses Discord OAuth for authentication, ensuring only authorized users can access their conversations
- All API requests require a valid Discord token
- The API verifies the token with Discord for each request
- Consider adding rate limiting and additional security measures for production use

View File

@ -1,329 +0,0 @@
import os
import json
import asyncio
import datetime
from typing import Dict, List, Optional, Any, Union
from fastapi import FastAPI, HTTPException, Depends, Header, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
import discord
from discord.ext import commands
import aiohttp
# This file contains the API endpoints for syncing conversations between
# the Flutter app and the Discord bot.
# Add this code to your Discord bot project and import it in your main bot file.
# ============= Models =============
class SyncedMessage(BaseModel):
content: str
role: str # "user", "assistant", or "system"
timestamp: datetime.datetime
reasoning: Optional[str] = None
usage_data: Optional[Dict[str, Any]] = None
class SyncedConversation(BaseModel):
id: str
title: str
messages: List[SyncedMessage]
created_at: datetime.datetime
updated_at: datetime.datetime
model_id: str
sync_source: str = "discord" # "discord" or "flutter"
class SyncRequest(BaseModel):
conversations: List[SyncedConversation]
last_sync_time: Optional[datetime.datetime] = None
class SyncResponse(BaseModel):
success: bool
message: str
conversations: List[SyncedConversation] = []
# ============= Storage =============
# File to store synced conversations
SYNC_DATA_FILE = "synced_conversations.json"
# In-memory storage for conversations
user_conversations: Dict[str, List[SyncedConversation]] = {}
# Load conversations from file
def load_conversations():
global user_conversations
if os.path.exists(SYNC_DATA_FILE):
try:
with open(SYNC_DATA_FILE, "r") as f:
data = json.load(f)
# Convert string keys (user IDs) back to strings
user_conversations = {k: [SyncedConversation.parse_obj(conv) for conv in v]
for k, v in data.items()}
print(f"Loaded synced conversations for {len(user_conversations)} users")
except Exception as e:
print(f"Error loading synced conversations: {e}")
user_conversations = {}
# Save conversations to file
def save_conversations():
try:
# Convert to JSON-serializable format
serializable_data = {
user_id: [conv.dict() for conv in convs]
for user_id, convs in user_conversations.items()
}
with open(SYNC_DATA_FILE, "w") as f:
json.dump(serializable_data, f, indent=2, default=str)
except Exception as e:
print(f"Error saving synced conversations: {e}")
# ============= Discord OAuth Verification =============
async def verify_discord_token(authorization: str = Header(None)) -> str:
"""Verify the Discord token and return the user ID"""
if not authorization:
raise HTTPException(status_code=401, detail="Authorization header missing")
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid authorization format")
token = authorization.replace("Bearer ", "")
# Verify the token with Discord
async with aiohttp.ClientSession() as session:
headers = {"Authorization": f"Bearer {token}"}
async with session.get("https://discord.com/api/v10/users/@me", headers=headers) as resp:
if resp.status != 200:
raise HTTPException(status_code=401, detail="Invalid Discord token")
user_data = await resp.json()
return user_data["id"]
# ============= API Setup =============
app = FastAPI(title="Discord Bot Sync API")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Adjust this in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize by loading saved data
@app.on_event("startup")
async def startup_event():
load_conversations()
# ============= API Endpoints =============
@app.get("/")
async def root():
return {"message": "Discord Bot Sync API is running"}
@app.get("/conversations")
async def get_conversations(user_id: str = Depends(verify_discord_token)):
"""Get all conversations for a user"""
if user_id not in user_conversations:
return {"conversations": []}
return {"conversations": user_conversations[user_id]}
@app.post("/sync")
async def sync_conversations(
sync_request: SyncRequest,
user_id: str = Depends(verify_discord_token)
):
"""Sync conversations between the Flutter app and Discord bot"""
# Get existing conversations for this user
existing_conversations = user_conversations.get(user_id, [])
# Process incoming conversations
updated_conversations = []
for incoming_conv in sync_request.conversations:
# Check if this conversation already exists
existing_conv = next((conv for conv in existing_conversations
if conv.id == incoming_conv.id), None)
if existing_conv:
# If the incoming conversation is newer, update it
if incoming_conv.updated_at > existing_conv.updated_at:
# Replace the existing conversation
existing_conversations = [conv for conv in existing_conversations
if conv.id != incoming_conv.id]
existing_conversations.append(incoming_conv)
updated_conversations.append(incoming_conv)
else:
# This is a new conversation, add it
existing_conversations.append(incoming_conv)
updated_conversations.append(incoming_conv)
# Update the storage
user_conversations[user_id] = existing_conversations
save_conversations()
return SyncResponse(
success=True,
message=f"Synced {len(updated_conversations)} conversations",
conversations=existing_conversations
)
@app.delete("/conversations/{conversation_id}")
async def delete_conversation(
conversation_id: str,
user_id: str = Depends(verify_discord_token)
):
"""Delete a conversation"""
if user_id not in user_conversations:
raise HTTPException(status_code=404, detail="No conversations found for this user")
# Filter out the conversation to delete
original_count = len(user_conversations[user_id])
user_conversations[user_id] = [conv for conv in user_conversations[user_id]
if conv.id != conversation_id]
# Check if any conversation was deleted
if len(user_conversations[user_id]) == original_count:
raise HTTPException(status_code=404, detail="Conversation not found")
save_conversations()
return {"success": True, "message": "Conversation deleted"}
# ============= Discord Bot Integration =============
# This function should be called from your Discord bot's AI cog
# to convert AI conversation history to the synced format
def convert_ai_history_to_synced(user_id: str, conversation_history: Dict[int, List[Dict[str, Any]]]):
"""Convert the AI conversation history to the synced format"""
synced_conversations = []
# Process each conversation in the history
for discord_user_id, messages in conversation_history.items():
if str(discord_user_id) != user_id:
continue
# Create a unique ID for this conversation
conv_id = f"discord_{discord_user_id}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}"
# Convert messages to the synced format
synced_messages = []
for msg in messages:
role = msg.get("role", "")
if role not in ["user", "assistant", "system"]:
continue
synced_messages.append(SyncedMessage(
content=msg.get("content", ""),
role=role,
timestamp=datetime.datetime.now(), # Use current time as we don't have the original timestamp
reasoning=None, # Discord bot doesn't store reasoning
usage_data=None # Discord bot doesn't store usage data
))
# Create the synced conversation
synced_conversations.append(SyncedConversation(
id=conv_id,
title="Discord Conversation", # Default title
messages=synced_messages,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
model_id="openai/gpt-3.5-turbo", # Default model
sync_source="discord"
))
return synced_conversations
# This function should be called from your Discord bot's AI cog
# to save a new conversation from Discord
def save_discord_conversation(user_id: str, messages: List[Dict[str, Any]], model_id: str = "openai/gpt-3.5-turbo"):
"""Save a conversation from Discord to the synced storage"""
# Convert messages to the synced format
synced_messages = []
for msg in messages:
role = msg.get("role", "")
if role not in ["user", "assistant", "system"]:
continue
synced_messages.append(SyncedMessage(
content=msg.get("content", ""),
role=role,
timestamp=datetime.datetime.now(),
reasoning=None,
usage_data=None
))
# Create a unique ID for this conversation
conv_id = f"discord_{user_id}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}"
# Create the synced conversation
synced_conv = SyncedConversation(
id=conv_id,
title="Discord Conversation",
messages=synced_messages,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
model_id=model_id,
sync_source="discord"
)
# Add to storage
if user_id not in user_conversations:
user_conversations[user_id] = []
user_conversations[user_id].append(synced_conv)
save_conversations()
return synced_conv
# ============= Integration with AI Cog =============
# Add these functions to your AI cog to integrate with the sync API
"""
# In your ai_cog.py file, add these imports:
from discord_bot_sync_api import save_discord_conversation, load_conversations, user_conversations
# Then modify your _get_ai_response method to save conversations after getting a response:
async def _get_ai_response(self, user_id: int, prompt: str, system_prompt: str = None) -> str:
# ... existing code ...
# After getting the response and updating conversation_history:
# Convert the conversation to the synced format and save it
messages = conversation_history[user_id]
save_discord_conversation(str(user_id), messages, settings["model"])
return final_response
# You can also add a command to view synced conversations:
@commands.command(name="aisync")
async def ai_sync_status(self, ctx: commands.Context):
user_id = str(ctx.author.id)
if user_id not in user_conversations or not user_conversations[user_id]:
await ctx.reply("You don't have any synced conversations.")
return
synced_count = len(user_conversations[user_id])
await ctx.reply(f"You have {synced_count} synced conversations that can be accessed from the Flutter app.")
"""
# ============= Run the API =============
# To run this API with your Discord bot, you need to use uvicorn
# You can start it in a separate thread or process
"""
# In your main bot file, add:
import threading
import uvicorn
def run_api():
uvicorn.run("discord_bot_sync_api:app", host="0.0.0.0", port=8000)
# Start the API in a separate thread
api_thread = threading.Thread(target=run_api)
api_thread.daemon = True
api_thread.start()
"""