a
This commit is contained in:
parent
46edfad5d5
commit
7dcbfc2be2
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,6 +6,9 @@ venv/
|
||||
certs/
|
||||
data/
|
||||
.env
|
||||
IMG/
|
||||
OUTPUT/
|
||||
SOUND/
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
@ -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
|
@ -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()
|
||||
"""
|
Loading…
x
Reference in New Issue
Block a user