Spaces:
Sleeping
Sleeping
File size: 14,126 Bytes
fb34f66 5fa78e9 dee826f 4e8c61d 8c6838f 4dec526 a3d43fe c55d9c8 fb34f66 20beebd 539396e d20ca74 8c6838f 4eaff6c a3f8221 4e6bb7f e9dbaf1 2f7bbec 8494288 fb34f66 e9dbaf1 fb34f66 332e56d fb34f66 539396e fb34f66 dc38658 fb34f66 20beebd dc38658 160e4e6 dee826f dc38658 160e4e6 cc7ee24 160e4e6 cc7ee24 160e4e6 cc7ee24 160e4e6 cc7ee24 160e4e6 fb34f66 4e8c61d fb34f66 4e8c61d d749538 539396e 8494288 d749538 20beebd d749538 20beebd d749538 022aac0 4dec526 022aac0 fb34f66 160e4e6 539396e 160e4e6 539396e 160e4e6 8c6838f 160e4e6 d749538 8c6838f d749538 8c6838f 539396e f7798ea 539396e dc38658 fb34f66 21b29de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | from flask import Flask, request
import requests
import json
import os
import uuid
from db import fetch_json_from_github
from qwen import get_qwen_response
from upload import upload_image_to_cdn, upload_audio_to_cdn
from admin_messages import extract_admin_message, save_admin_message
app = Flask(__name__)
# Cache for user profiles to reduce API calls
user_profile_cache = {}
chat_histories = {}
BASE_URL = "https://chat.qwen.ai"
AUTH_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRjOGQyYzY4LWZjNmEtNDEwYy05NWZjLWQ5MDBmNTM4ZTMwMiIsImV4cCI6MTc0NjM1OTExMH0.ms8Agbit2ObnTMJyeW_dUbk-tV_ON_dc-RjDKCDLAwA"
AI_SERVICE_URL = "https://aoamrnuwara.pythonanywhere.com/send-message"
AI_SERVICE_TOKEN = "123400"
# Proxy API endpoint URL
PROXY_API_URL = "https://aoamrnuwara.pythonanywhere.com/get-user-profile"
@app.route("/webhook", methods=["GET"])
def verify_webhook():
mode = request.args.get("hub.mode")
token = request.args.get("hub.verify_token")
challenge = request.args.get("hub.challenge")
if mode == "subscribe" and token == AI_SERVICE_TOKEN:
return challenge
else:
print(f"Verification failed: mode={mode}, token={token}") # Debug log
return "Verification failed", 403
def get_user_profile(sender_id, page_token):
"""Get user profile with fallback mechanisms"""
# Check cache first
cache_key = f"{sender_id}_{page_token[:10]}" # Use first 10 chars of token for cache key
if cache_key in user_profile_cache:
print(f"Using cached profile for {sender_id}")
return user_profile_cache[cache_key]
# Default fallback name
user_full_name = "User"
try:
proxy_params = {
"sender_id": sender_id,
"page_token": page_token
}
# Add timeout to prevent hanging
proxy_response = requests.get(PROXY_API_URL, params=proxy_params, timeout=5)
# Even if the request fails with non-200 status, continue with default name
if proxy_response.status_code == 200:
proxy_data = proxy_response.json()
if proxy_data.get("success", False):
user_full_name = proxy_data.get("full_name", "User")
# Cache the successful result
user_profile_cache[cache_key] = user_full_name
else:
print(f"Proxy API returned error: {proxy_data.get('error', 'Unknown error')}")
else:
print(f"Proxy API returned status code: {proxy_response.status_code}")
except requests.exceptions.Timeout:
print(f"Timeout when calling proxy API for user {sender_id}")
except requests.exceptions.RequestException as e:
print(f"Error calling proxy API: {str(e)}")
except Exception as e:
print(f"Unexpected error getting user profile: {str(e)}")
return user_full_name
@app.route("/webhook", methods=["POST"])
def handle_message():
body = request.get_json()
# Fetch the latest JSON data from GitHub for dynamic updates
try:
github_response = fetch_json_from_github()
if github_response["success"]:
PAGE_CREDENTIALS = {page['page_id']: page for page in github_response["data"]["pages"]}
print("Successfully loaded page credentials from GitHub.")
else:
print(f"Error loading page credentials: {github_response['message']}")
PAGE_CREDENTIALS = {}
except Exception as e:
print(f"Unexpected error while fetching credentials: {e}")
PAGE_CREDENTIALS = {}
for entry in body["entry"]:
page_id = entry["id"]
page_config = PAGE_CREDENTIALS.get(page_id, {})
page_token = page_config.get('page_token')
# Check if page exists in credentials
if not page_token:
print(f"Page ID {page_id} not found in credentials. Skipping...")
continue
# Check page status - if OFF, skip processing
page_status = page_config.get('status', 'OFF')
if page_status != "ON":
print(f"Page ID {page_id} is turned OFF. Skipping...")
continue
# Check subscription status
subscription = page_config.get('subscription', 'NONE')
expiration = page_config.get('expiration', 'NONE')
# Check expiration date for any subscription type that has one
if subscription in ["Basic", "Plus"] and expiration != "NONE":
from datetime import datetime
try:
expiration_date = datetime.strptime(expiration, "%Y-%m-%d")
current_date = datetime.now()
if current_date.date() > expiration_date.date():
print(f"Page ID {page_id} has an expired {subscription} subscription. Skipping...")
continue
except Exception as e:
print(f"Error checking expiration date: {str(e)}")
# If there's an error parsing the date, we should skip processing
continue
# If no subscription, skip processing
if subscription == "NONE":
print(f"Page ID {page_id} has no subscription. Skipping...")
continue
# Fetch the system prompt dynamically for each request
system_prompt = page_config.get('system', 'Default system prompt')
for messaging_event in entry["messaging"]:
if "message" in messaging_event:
sender_id = messaging_event["sender"]["id"]
message = messaging_event["message"]
# Get user profile with improved error handling
user_full_name = get_user_profile(sender_id, page_token)
# Get or create chat history for this user
user_history = chat_histories.setdefault(sender_id, {
'history': [],
'system_prompt': system_prompt # Dynamically update system prompt
})
# Update the system prompt dynamically
user_history['system_prompt'] = system_prompt
image_cdn_urls = []
audio_cdn_urls = []
attachments = message.get("attachments", [])
text_content = message.get("text", "")
# Process attachments based on subscription level
if subscription == "Plus":
# Plus subscribers can send images and audio
for attachment in attachments:
if attachment["type"] == "image":
image_url = attachment["payload"]["url"]
try:
img_response = requests.get(image_url, timeout=10)
img_response.raise_for_status()
temp_path = f"temp_{uuid.uuid4()}.jpg"
with open(temp_path, "wb") as f:
f.write(img_response.content)
cdn_url = upload_image_to_cdn(BASE_URL, AUTH_TOKEN, temp_path)
if cdn_url:
image_cdn_urls.append(cdn_url)
print(f"Image CDN URL: {cdn_url}")
except Exception as e:
print(f"Image processing error: {str(e)}")
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
elif attachment["type"] == "audio":
audio_url = attachment["payload"]["url"]
try:
audio_response = requests.get(audio_url, timeout=10)
audio_response.raise_for_status()
# Extract file extension from the original filename
original_filename = attachment.get("name", "audio.mp3")
file_extension = os.path.splitext(original_filename)[1].lower()
if not file_extension:
file_extension = ".mp3" # Default to MP3
temp_path = f"temp_{uuid.uuid4()}{file_extension}"
with open(temp_path, "wb") as f:
f.write(audio_response.content)
cdn_url = upload_audio_to_cdn(BASE_URL, AUTH_TOKEN, temp_path)
if cdn_url:
audio_cdn_urls.append(cdn_url)
print(f"Audio CDN URL: {cdn_url}")
except Exception as e:
print(f"Audio processing error: {str(e)}")
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
elif subscription == "Basic":
# Basic subscribers can only send text
if attachments:
print(f"Basic subscriber tried to send attachments. Skipping...")
continue
# Create content based on subscription level
content = []
if subscription == "Plus":
if image_cdn_urls:
content.append({"type": "text", "text": f"{user_full_name} sent an image"})
for cdn_url in image_cdn_urls:
content.append({"type": "image", "image": cdn_url})
elif audio_cdn_urls:
content.append({"type": "text", "text": f"{user_full_name} sent an audio file"})
for cdn_url in audio_cdn_urls:
content.append({"type": "audio", "audio": cdn_url})
elif text_content:
content.append({"type": "text", "text": f"{user_full_name}: {text_content}"})
else:
continue
elif subscription == "Basic":
if text_content:
content.append({"type": "text", "text": f"{user_full_name}: {text_content}"})
else:
continue
# Append the user's message to the chat history
user_history['history'].append({
"role": "user",
"content": content
})
# Ensure only the last 5 messages are retained in the chat history
while len(user_history['history']) > 5:
user_history['history'].pop(0)
try:
# Generate AI response using the dynamically updated system prompt
ai_response = get_qwen_response(
system_prompt=user_history['system_prompt'],
chat_history=user_history['history']
)
# Check if the AI response contains an admin message
admin_message, cleaned_response = extract_admin_message(ai_response)
# Always add the original response to chat history (with admin tags if present)
user_history['history'].append({
"role": "assistant",
"content": [{"type": "text", "text": ai_response}]
})
# If there's an admin message, save it to the GitHub JSON file
if admin_message:
print(f"Admin message detected: {admin_message}")
save_admin_message(page_id, admin_message, sender_id, user_full_name)
# Send the cleaned response (without admin tags) to the user
response_to_send = cleaned_response
else:
# No admin message, send the original response
response_to_send = ai_response
# Send the appropriate response back to the user
try:
headers = {
"Authorization": f"Bearer {AI_SERVICE_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"recipient_id": sender_id,
"message_text": response_to_send,
"page_access_token": page_token
}
send_response = requests.post(AI_SERVICE_URL, json=payload, headers=headers, timeout=10)
send_response.raise_for_status()
print(f"Response sent successfully to {sender_id}")
except Exception as e:
print(f"Error sending response to user: {str(e)}")
except Exception as e:
print(f"Error generating AI response: {str(e)}")
# Send a fallback message if AI response generation fails
try:
headers = {
"Authorization": f"Bearer {AI_SERVICE_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"recipient_id": sender_id,
"message_text": "I'm sorry, I'm having trouble processing your request right now. Please try again later.",
"page_access_token": page_token
}
requests.post(AI_SERVICE_URL, json=payload, headers=headers, timeout=10)
except Exception as send_err:
print(f"Error sending fallback message: {str(send_err)}")
return "OK", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860, debug=True) |