seawolf2357 commited on
Commit
564f515
Β·
verified Β·
1 Parent(s): 2303740

Update app_routes.py

Browse files
Files changed (1) hide show
  1. app_routes.py +21 -21
app_routes.py CHANGED
@@ -1,7 +1,7 @@
1
  from fastapi import APIRouter, Request
2
  from fastapi.responses import StreamingResponse
3
  import aiosqlite, asyncio, random, json, logging
4
- from npc_core import GroqAIClient
5
  from npc_trading import (
6
  ALL_TICKERS, TRADING_STRATEGIES, IDENTITY_STRATEGY_MAP,
7
  get_all_prices, get_ticker_positions, get_trading_leaderboard, get_trading_stats,
@@ -67,7 +67,7 @@ async def api_market_indices():
67
  return {"indices": indices}
68
  @router.get("/api/intelligence/screening/{ticker}")
69
  async def api_screening_data(ticker: str):
70
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
71
  await db.execute("PRAGMA busy_timeout=30000")
72
  try:
73
  cursor = await db.execute("SELECT ticker, price, change_pct, volume, market_cap, rsi, pe_ratio, high_52w, low_52w, from_high, from_low FROM market_prices WHERE ticker=?", (ticker,))
@@ -77,7 +77,7 @@ async def api_screening_data(ticker: str):
77
  return {"ticker": ticker, "error": "no data"}
78
  @router.get("/api/intelligence/target/{ticker}")
79
  async def api_target_price(ticker: str):
80
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
81
  await db.execute("PRAGMA busy_timeout=30000")
82
  try:
83
  cursor = await db.execute("SELECT price, rsi, pe_ratio, from_high, from_low FROM market_prices WHERE ticker=?", (ticker,))
@@ -94,7 +94,7 @@ async def api_target_price(ticker: str):
94
  async def api_news_feed(ticker: str = None, limit: int = 50):
95
  news = await load_news_from_db(_DB_PATH, ticker, limit)
96
  try:
97
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
98
  await db.execute("PRAGMA busy_timeout=30000")
99
  for n in news:
100
  nid = n.get('id')
@@ -109,7 +109,7 @@ async def api_news_react(request: Request):
109
  body = await request.json(); email = body.get("email", ""); news_id = body.get("news_id"); emoji = body.get("emoji", "")
110
  if not email or not news_id or emoji not in ('πŸ‘','πŸ”₯','😱','πŸ’€','πŸš€','πŸ˜‚'): return {"error": "Invalid reaction"}
111
  try:
112
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
113
  await db.execute("PRAGMA busy_timeout=30000"); await db.execute("INSERT OR IGNORE INTO news_reactions (news_id, user_email, emoji) VALUES (?,?,?)", (news_id, email, emoji)); await db.commit()
114
  rc = await db.execute("SELECT emoji, COUNT(*) FROM news_reactions WHERE news_id=? GROUP BY emoji", (news_id,))
115
  return {"success": True, "reactions": {r[0]: r[1] for r in await rc.fetchall()}}
@@ -150,7 +150,7 @@ async def api_deep_analysis(ticker: str):
150
  @router.get("/api/analysis/all/summary")
151
  async def api_all_analyses():
152
  analyses = await load_all_analyses_from_db(_DB_PATH)
153
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
154
  await db.execute("PRAGMA busy_timeout=30000"); col_cursor = await db.execute("PRAGMA table_info(market_prices)")
155
  existing_cols = {r[1] for r in await col_cursor.fetchall()}
156
  has_rsi = 'rsi' in existing_cols; has_pe = 'pe_ratio' in existing_cols; has_52w = 'high_52w' in existing_cols; has_from = 'from_high' in existing_cols
@@ -219,13 +219,13 @@ async def api_all_analyses():
219
  @router.get("/api/npc/search")
220
  async def api_npc_search(q: str = ""):
221
  if len(q) < 1: return {"npcs": []}
222
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
223
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT agent_id, username, ai_identity FROM npc_agents WHERE username LIKE ? AND is_active=1 LIMIT 10", (f'%{q}%',))
224
  return {"npcs": [{"agent_id": r[0], "username": r[1], "identity": r[2]} for r in await cursor.fetchall()]}
225
  @router.get("/api/npc/profile/{agent_id}")
226
  async def api_npc_profile(agent_id: str):
227
  try:
228
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
229
  await db.execute("PRAGMA busy_timeout=30000")
230
  cursor = await db.execute("SELECT agent_id, username, ai_identity, mbti, gpu_dollars, created_at FROM npc_agents WHERE agent_id=?", (agent_id,))
231
  row = await cursor.fetchone()
@@ -302,7 +302,7 @@ async def api_sec_dashboard():
302
  return {"stats": {"total_violations": 0, "total_fines_gpu": 0, "active_suspensions": 0, "pending_reports": 0}, "announcements": [], "top_violators": [], "recent_reports": []}
303
  @router.get("/api/sec/violations/{agent_id}")
304
  async def api_sec_violations(agent_id: str):
305
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
306
  await db.execute("PRAGMA busy_timeout=30000")
307
  cursor = await db.execute("SELECT id, violation_type, severity, description, penalty_type, gpu_fine, suspend_until, status, created_at FROM sec_violations WHERE agent_id=? ORDER BY created_at DESC LIMIT 20", (agent_id,))
308
  violations = [{'id': r[0], 'type': r[1], 'severity': r[2], 'description': r[3], 'penalty': r[4], 'fine': r[5], 'suspend_until': r[6], 'status': r[7], 'created_at': r[8]} for r in await cursor.fetchall()]
@@ -316,7 +316,7 @@ async def api_sec_report(request: Request):
316
  if not reporter or not target or not reason: return {"error": "reporter, target, reason required"}
317
  if reporter == target: return {"error": "Cannot report yourself"}
318
  reporter_id = f"user_{reporter}" if '@' in reporter else reporter
319
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
320
  await db.execute("PRAGMA busy_timeout=30000")
321
  cursor = await db.execute("SELECT id FROM sec_reports WHERE reporter_agent_id=? AND target_agent_id=? AND created_at > datetime('now', '-12 hours')", (reporter_id, target))
322
  if await cursor.fetchone(): return {"error": "Already reported this NPC recently (12h cooldown)"}
@@ -334,7 +334,7 @@ async def api_sec_seed():
334
  ("Front-running detected β€” trading ahead of own analysis publications", "front_running", "medium", "fine", 300, 0),
335
  ("Coordinated short attack with fake news β€” colluding with other NPCs", "collusion", "critical", "suspension", 1500, 96)]
336
  try:
337
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
338
  await db.execute("PRAGMA busy_timeout=30000"); vc = await db.execute("SELECT COUNT(*) FROM sec_violations"); cnt = (await vc.fetchone())[0]
339
  if cnt > 0: return {"message": f"Already have {cnt} violations, skipping seed"}
340
  cursor = await db.execute("SELECT agent_id, username FROM npc_agents WHERE is_active=1 ORDER BY RANDOM() LIMIT 6"); npcs = await cursor.fetchall()
@@ -352,13 +352,13 @@ async def api_sec_seed():
352
  except Exception as e: return {"error": str(e)}
353
  @router.get("/api/sec/announcements")
354
  async def api_sec_announcements(limit: int = 30):
355
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
356
  await db.execute("PRAGMA busy_timeout=30000")
357
  cursor = await db.execute("SELECT id, announcement_type, target_username, violation_type, penalty_type, gpu_fine, suspend_hours, title, content, created_at FROM sec_announcements ORDER BY created_at DESC LIMIT ?", (min(limit, 50),))
358
  return {"announcements": [{'id': r[0], 'type': r[1], 'target': r[2], 'violation': r[3], 'penalty': r[4], 'fine': r[5], 'hours': r[6], 'title': r[7], 'content': r[8], 'created_at': r[9]} for r in await cursor.fetchall()]}
359
  @router.get("/api/sec/suspended")
360
  async def api_sec_suspended():
361
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
362
  await db.execute("PRAGMA busy_timeout=30000")
363
  cursor = await db.execute("SELECT s.agent_id, n.username, s.reason, s.suspended_until, s.created_at FROM sec_suspensions s JOIN npc_agents n ON s.agent_id = n.agent_id WHERE s.suspended_until > datetime('now') ORDER BY s.suspended_until DESC")
364
  suspended = [{'agent_id': r[0], 'username': r[1], 'reason': r[2], 'until': r[3], 'since': r[4]} for r in await cursor.fetchall()]
@@ -369,7 +369,7 @@ async def api_tip_npc(request: Request):
369
  user_email = body.get("email", ""); target_agent = body.get("target_agent_id", ""); amount = int(body.get("amount", 0)); message = body.get("message", "").strip()[:200]
370
  if not user_email or not target_agent or amount < 10: return {"error": "email, target_agent_id, amount(>=10) required"}
371
  if amount > 1000: return {"error": "Max tip: 1,000 GPU"}
372
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
373
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT gpu_dollars, username FROM user_profiles WHERE email=?", (user_email,))
374
  user = await cursor.fetchone()
375
  if not user or user[0] < amount: return {"error": "Insufficient GPU"}
@@ -392,7 +392,7 @@ async def api_influence_npc(request: Request):
392
  body = await request.json()
393
  user_email = body.get("email", ""); target_agent = body.get("target_agent_id", ""); ticker = body.get("ticker", "").upper(); stance = body.get("stance", "").lower()
394
  if not all([user_email, target_agent, ticker, stance]) or stance not in ('bullish', 'bearish'): return {"error": "email, target_agent_id, ticker, stance(bullish/bearish) required"}
395
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
396
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT username FROM user_profiles WHERE email=?", (user_email,))
397
  user = await cursor.fetchone(); cursor2 = await db.execute("SELECT username, ai_identity FROM npc_agents WHERE agent_id=?", (target_agent,))
398
  npc = await cursor2.fetchone()
@@ -415,7 +415,7 @@ async def api_influence_npc(request: Request):
415
  return {"status": "success", "influenced": influenced, "message": response_msg}
416
  @router.get("/api/swarm/trending")
417
  async def api_swarm_trending():
418
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
419
  await db.execute("PRAGMA busy_timeout=30000")
420
  cursor = await db.execute("SELECT p.title || ' ' || p.content FROM posts p WHERE p.created_at > datetime('now', '-6 hours') ORDER BY p.likes_count DESC LIMIT 50")
421
  posts = [r[0] for r in await cursor.fetchall()]
@@ -430,7 +430,7 @@ async def api_swarm_trending():
430
  @router.get("/api/chat/messages")
431
  async def api_chat_messages(limit: int = 50, after_id: int = 0):
432
  try:
433
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
434
  await db.execute("PRAGMA busy_timeout=30000")
435
  if after_id > 0:
436
  cursor = await db.execute("SELECT id, agent_id, username, identity, mbti, message, msg_type, ticker, reply_to, created_at FROM npc_live_chat WHERE id > ? ORDER BY id ASC LIMIT ?", (after_id, limit))
@@ -443,7 +443,7 @@ async def api_chat_messages(limit: int = 50, after_id: int = 0):
443
  @router.get("/api/chat/stats")
444
  async def api_chat_stats():
445
  try:
446
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
447
  await db.execute("PRAGMA busy_timeout=30000"); c1 = await db.execute("SELECT COUNT(*) FROM npc_live_chat"); total = (await c1.fetchone())[0]
448
  c2 = await db.execute("SELECT COUNT(*) FROM npc_live_chat WHERE created_at > datetime('now', '-1 hour')"); recent = (await c2.fetchone())[0]
449
  c3 = await db.execute("SELECT COUNT(DISTINCT agent_id) FROM npc_live_chat WHERE created_at > datetime('now', '-1 hour')"); active = (await c3.fetchone())[0]
@@ -455,7 +455,7 @@ async def api_chat_send(request: Request):
455
  body = await request.json(); email = body.get("email", ""); message = body.get("message", "").strip()
456
  if not email or not message: return {"success": False, "error": "Email and message required"}
457
  if len(message) > 500: message = message[:500]
458
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
459
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT username FROM user_profiles WHERE email=?", (email,))
460
  row = await cursor.fetchone()
461
  if not row: return {"success": False, "error": "User not found"}
@@ -471,7 +471,7 @@ async def api_chat_send(request: Request):
471
  async def _generate_npc_replies_to_user(user_message: str, user_username: str, user_msg_id: int, npcs: list):
472
  try:
473
  ai = GroqAIClient(_GROQ_KEY)
474
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
475
  await db.execute("PRAGMA busy_timeout=30000")
476
  for npc in npcs:
477
  agent_id, npc_username, identity, mbti = npc
@@ -503,7 +503,7 @@ async def api_sse_stream():
503
  return StreamingResponse(event_generator(), media_type="text/event-stream", headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
504
  @router.get("/api/events/recent")
505
  async def api_recent_events(limit: int = 20):
506
- async with aiosqlite.connect(_DB_PATH, timeout=30.0) as db:
507
  await db.execute("PRAGMA busy_timeout=30000")
508
  cursor = await db.execute("SELECT p.agent_id, n.username, n.ai_identity, p.ticker, p.direction, p.gpu_bet, p.leverage, p.opened_at FROM npc_positions p JOIN npc_agents n ON p.agent_id = n.agent_id WHERE p.opened_at > datetime('now', '-1 hour') ORDER BY p.opened_at DESC LIMIT ?", (limit,))
509
  trades = [{'user': r[1], 'identity': r[2], 'ticker': r[3], 'dir': r[4], 'gpu': r[5], 'leverage': r[6], 'time': r[7]} for r in await cursor.fetchall()]
 
1
  from fastapi import APIRouter, Request
2
  from fastapi.responses import StreamingResponse
3
  import aiosqlite, asyncio, random, json, logging
4
+ from npc_core import GroqAIClient, get_db, get_db_read
5
  from npc_trading import (
6
  ALL_TICKERS, TRADING_STRATEGIES, IDENTITY_STRATEGY_MAP,
7
  get_all_prices, get_ticker_positions, get_trading_leaderboard, get_trading_stats,
 
67
  return {"indices": indices}
68
  @router.get("/api/intelligence/screening/{ticker}")
69
  async def api_screening_data(ticker: str):
70
+ async with get_db(_DB_PATH) as db:
71
  await db.execute("PRAGMA busy_timeout=30000")
72
  try:
73
  cursor = await db.execute("SELECT ticker, price, change_pct, volume, market_cap, rsi, pe_ratio, high_52w, low_52w, from_high, from_low FROM market_prices WHERE ticker=?", (ticker,))
 
77
  return {"ticker": ticker, "error": "no data"}
78
  @router.get("/api/intelligence/target/{ticker}")
79
  async def api_target_price(ticker: str):
80
+ async with get_db(_DB_PATH) as db:
81
  await db.execute("PRAGMA busy_timeout=30000")
82
  try:
83
  cursor = await db.execute("SELECT price, rsi, pe_ratio, from_high, from_low FROM market_prices WHERE ticker=?", (ticker,))
 
94
  async def api_news_feed(ticker: str = None, limit: int = 50):
95
  news = await load_news_from_db(_DB_PATH, ticker, limit)
96
  try:
97
+ async with get_db(_DB_PATH) as db:
98
  await db.execute("PRAGMA busy_timeout=30000")
99
  for n in news:
100
  nid = n.get('id')
 
109
  body = await request.json(); email = body.get("email", ""); news_id = body.get("news_id"); emoji = body.get("emoji", "")
110
  if not email or not news_id or emoji not in ('πŸ‘','πŸ”₯','😱','πŸ’€','πŸš€','πŸ˜‚'): return {"error": "Invalid reaction"}
111
  try:
112
+ async with get_db(_DB_PATH) as db:
113
  await db.execute("PRAGMA busy_timeout=30000"); await db.execute("INSERT OR IGNORE INTO news_reactions (news_id, user_email, emoji) VALUES (?,?,?)", (news_id, email, emoji)); await db.commit()
114
  rc = await db.execute("SELECT emoji, COUNT(*) FROM news_reactions WHERE news_id=? GROUP BY emoji", (news_id,))
115
  return {"success": True, "reactions": {r[0]: r[1] for r in await rc.fetchall()}}
 
150
  @router.get("/api/analysis/all/summary")
151
  async def api_all_analyses():
152
  analyses = await load_all_analyses_from_db(_DB_PATH)
153
+ async with get_db(_DB_PATH) as db:
154
  await db.execute("PRAGMA busy_timeout=30000"); col_cursor = await db.execute("PRAGMA table_info(market_prices)")
155
  existing_cols = {r[1] for r in await col_cursor.fetchall()}
156
  has_rsi = 'rsi' in existing_cols; has_pe = 'pe_ratio' in existing_cols; has_52w = 'high_52w' in existing_cols; has_from = 'from_high' in existing_cols
 
219
  @router.get("/api/npc/search")
220
  async def api_npc_search(q: str = ""):
221
  if len(q) < 1: return {"npcs": []}
222
+ async with get_db(_DB_PATH) as db:
223
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT agent_id, username, ai_identity FROM npc_agents WHERE username LIKE ? AND is_active=1 LIMIT 10", (f'%{q}%',))
224
  return {"npcs": [{"agent_id": r[0], "username": r[1], "identity": r[2]} for r in await cursor.fetchall()]}
225
  @router.get("/api/npc/profile/{agent_id}")
226
  async def api_npc_profile(agent_id: str):
227
  try:
228
+ async with get_db(_DB_PATH) as db:
229
  await db.execute("PRAGMA busy_timeout=30000")
230
  cursor = await db.execute("SELECT agent_id, username, ai_identity, mbti, gpu_dollars, created_at FROM npc_agents WHERE agent_id=?", (agent_id,))
231
  row = await cursor.fetchone()
 
302
  return {"stats": {"total_violations": 0, "total_fines_gpu": 0, "active_suspensions": 0, "pending_reports": 0}, "announcements": [], "top_violators": [], "recent_reports": []}
303
  @router.get("/api/sec/violations/{agent_id}")
304
  async def api_sec_violations(agent_id: str):
305
+ async with get_db(_DB_PATH) as db:
306
  await db.execute("PRAGMA busy_timeout=30000")
307
  cursor = await db.execute("SELECT id, violation_type, severity, description, penalty_type, gpu_fine, suspend_until, status, created_at FROM sec_violations WHERE agent_id=? ORDER BY created_at DESC LIMIT 20", (agent_id,))
308
  violations = [{'id': r[0], 'type': r[1], 'severity': r[2], 'description': r[3], 'penalty': r[4], 'fine': r[5], 'suspend_until': r[6], 'status': r[7], 'created_at': r[8]} for r in await cursor.fetchall()]
 
316
  if not reporter or not target or not reason: return {"error": "reporter, target, reason required"}
317
  if reporter == target: return {"error": "Cannot report yourself"}
318
  reporter_id = f"user_{reporter}" if '@' in reporter else reporter
319
+ async with get_db(_DB_PATH) as db:
320
  await db.execute("PRAGMA busy_timeout=30000")
321
  cursor = await db.execute("SELECT id FROM sec_reports WHERE reporter_agent_id=? AND target_agent_id=? AND created_at > datetime('now', '-12 hours')", (reporter_id, target))
322
  if await cursor.fetchone(): return {"error": "Already reported this NPC recently (12h cooldown)"}
 
334
  ("Front-running detected β€” trading ahead of own analysis publications", "front_running", "medium", "fine", 300, 0),
335
  ("Coordinated short attack with fake news β€” colluding with other NPCs", "collusion", "critical", "suspension", 1500, 96)]
336
  try:
337
+ async with get_db(_DB_PATH) as db:
338
  await db.execute("PRAGMA busy_timeout=30000"); vc = await db.execute("SELECT COUNT(*) FROM sec_violations"); cnt = (await vc.fetchone())[0]
339
  if cnt > 0: return {"message": f"Already have {cnt} violations, skipping seed"}
340
  cursor = await db.execute("SELECT agent_id, username FROM npc_agents WHERE is_active=1 ORDER BY RANDOM() LIMIT 6"); npcs = await cursor.fetchall()
 
352
  except Exception as e: return {"error": str(e)}
353
  @router.get("/api/sec/announcements")
354
  async def api_sec_announcements(limit: int = 30):
355
+ async with get_db(_DB_PATH) as db:
356
  await db.execute("PRAGMA busy_timeout=30000")
357
  cursor = await db.execute("SELECT id, announcement_type, target_username, violation_type, penalty_type, gpu_fine, suspend_hours, title, content, created_at FROM sec_announcements ORDER BY created_at DESC LIMIT ?", (min(limit, 50),))
358
  return {"announcements": [{'id': r[0], 'type': r[1], 'target': r[2], 'violation': r[3], 'penalty': r[4], 'fine': r[5], 'hours': r[6], 'title': r[7], 'content': r[8], 'created_at': r[9]} for r in await cursor.fetchall()]}
359
  @router.get("/api/sec/suspended")
360
  async def api_sec_suspended():
361
+ async with get_db(_DB_PATH) as db:
362
  await db.execute("PRAGMA busy_timeout=30000")
363
  cursor = await db.execute("SELECT s.agent_id, n.username, s.reason, s.suspended_until, s.created_at FROM sec_suspensions s JOIN npc_agents n ON s.agent_id = n.agent_id WHERE s.suspended_until > datetime('now') ORDER BY s.suspended_until DESC")
364
  suspended = [{'agent_id': r[0], 'username': r[1], 'reason': r[2], 'until': r[3], 'since': r[4]} for r in await cursor.fetchall()]
 
369
  user_email = body.get("email", ""); target_agent = body.get("target_agent_id", ""); amount = int(body.get("amount", 0)); message = body.get("message", "").strip()[:200]
370
  if not user_email or not target_agent or amount < 10: return {"error": "email, target_agent_id, amount(>=10) required"}
371
  if amount > 1000: return {"error": "Max tip: 1,000 GPU"}
372
+ async with get_db(_DB_PATH) as db:
373
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT gpu_dollars, username FROM user_profiles WHERE email=?", (user_email,))
374
  user = await cursor.fetchone()
375
  if not user or user[0] < amount: return {"error": "Insufficient GPU"}
 
392
  body = await request.json()
393
  user_email = body.get("email", ""); target_agent = body.get("target_agent_id", ""); ticker = body.get("ticker", "").upper(); stance = body.get("stance", "").lower()
394
  if not all([user_email, target_agent, ticker, stance]) or stance not in ('bullish', 'bearish'): return {"error": "email, target_agent_id, ticker, stance(bullish/bearish) required"}
395
+ async with get_db(_DB_PATH) as db:
396
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT username FROM user_profiles WHERE email=?", (user_email,))
397
  user = await cursor.fetchone(); cursor2 = await db.execute("SELECT username, ai_identity FROM npc_agents WHERE agent_id=?", (target_agent,))
398
  npc = await cursor2.fetchone()
 
415
  return {"status": "success", "influenced": influenced, "message": response_msg}
416
  @router.get("/api/swarm/trending")
417
  async def api_swarm_trending():
418
+ async with get_db(_DB_PATH) as db:
419
  await db.execute("PRAGMA busy_timeout=30000")
420
  cursor = await db.execute("SELECT p.title || ' ' || p.content FROM posts p WHERE p.created_at > datetime('now', '-6 hours') ORDER BY p.likes_count DESC LIMIT 50")
421
  posts = [r[0] for r in await cursor.fetchall()]
 
430
  @router.get("/api/chat/messages")
431
  async def api_chat_messages(limit: int = 50, after_id: int = 0):
432
  try:
433
+ async with get_db(_DB_PATH) as db:
434
  await db.execute("PRAGMA busy_timeout=30000")
435
  if after_id > 0:
436
  cursor = await db.execute("SELECT id, agent_id, username, identity, mbti, message, msg_type, ticker, reply_to, created_at FROM npc_live_chat WHERE id > ? ORDER BY id ASC LIMIT ?", (after_id, limit))
 
443
  @router.get("/api/chat/stats")
444
  async def api_chat_stats():
445
  try:
446
+ async with get_db(_DB_PATH) as db:
447
  await db.execute("PRAGMA busy_timeout=30000"); c1 = await db.execute("SELECT COUNT(*) FROM npc_live_chat"); total = (await c1.fetchone())[0]
448
  c2 = await db.execute("SELECT COUNT(*) FROM npc_live_chat WHERE created_at > datetime('now', '-1 hour')"); recent = (await c2.fetchone())[0]
449
  c3 = await db.execute("SELECT COUNT(DISTINCT agent_id) FROM npc_live_chat WHERE created_at > datetime('now', '-1 hour')"); active = (await c3.fetchone())[0]
 
455
  body = await request.json(); email = body.get("email", ""); message = body.get("message", "").strip()
456
  if not email or not message: return {"success": False, "error": "Email and message required"}
457
  if len(message) > 500: message = message[:500]
458
+ async with get_db(_DB_PATH) as db:
459
  await db.execute("PRAGMA busy_timeout=30000"); cursor = await db.execute("SELECT username FROM user_profiles WHERE email=?", (email,))
460
  row = await cursor.fetchone()
461
  if not row: return {"success": False, "error": "User not found"}
 
471
  async def _generate_npc_replies_to_user(user_message: str, user_username: str, user_msg_id: int, npcs: list):
472
  try:
473
  ai = GroqAIClient(_GROQ_KEY)
474
+ async with get_db(_DB_PATH) as db:
475
  await db.execute("PRAGMA busy_timeout=30000")
476
  for npc in npcs:
477
  agent_id, npc_username, identity, mbti = npc
 
503
  return StreamingResponse(event_generator(), media_type="text/event-stream", headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
504
  @router.get("/api/events/recent")
505
  async def api_recent_events(limit: int = 20):
506
+ async with get_db(_DB_PATH) as db:
507
  await db.execute("PRAGMA busy_timeout=30000")
508
  cursor = await db.execute("SELECT p.agent_id, n.username, n.ai_identity, p.ticker, p.direction, p.gpu_bet, p.leverage, p.opened_at FROM npc_positions p JOIN npc_agents n ON p.agent_id = n.agent_id WHERE p.opened_at > datetime('now', '-1 hour') ORDER BY p.opened_at DESC LIMIT ?", (limit,))
509
  trades = [{'user': r[1], 'identity': r[2], 'ticker': r[3], 'dir': r[4], 'gpu': r[5], 'leverage': r[6], 'time': r[7]} for r in await cursor.fetchall()]