max1949 commited on
Commit
fd79dcc
·
verified ·
1 Parent(s): 2814653

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +309 -216
app.py CHANGED
@@ -35,12 +35,6 @@ logger = logging.getLogger("BK_GTA_ARCHITECT")
35
  # 0.5 [HuggingFace 专用] DNS 稳定性修复 (增强版)
36
  # ==============================================================================
37
  def fix_dns():
38
- """
39
- HuggingFace Spaces 容器 DNS 解析器无法解析 discord.com。
40
- 方案: 用 dnspython 通过 Google DNS (8.8.8.8) 直接查询 IP,
41
- 然后写入 /etc/hosts 完全绕过容器的 DNS。
42
- """
43
- # 第一步:尝试注入 resolv.conf 备用 DNS
44
  try:
45
  with open('/etc/resolv.conf', 'r') as f:
46
  current = f.read()
@@ -51,22 +45,16 @@ def fix_dns():
51
  except Exception as e:
52
  logger.warning(f"⚠️ [DNS] resolv.conf write failed: {e}")
53
 
54
- # 第二步(核心):用 dnspython 解析并写入 /etc/hosts
55
  hosts_to_resolve = [
56
- 'discord.com',
57
- 'gateway.discord.gg',
58
- 'cdn.discordapp.com',
59
- 'api.discord.com',
60
  ]
61
-
62
  try:
63
  import dns.resolver
64
-
65
  resolver = dns.resolver.Resolver(configure=False)
66
  resolver.nameservers = ['8.8.8.8', '1.1.1.1', '8.8.4.4']
67
  resolver.timeout = 10
68
  resolver.lifetime = 10
69
-
70
  hosts_lines = []
71
  for hostname in hosts_to_resolve:
72
  try:
@@ -76,7 +64,6 @@ def fix_dns():
76
  logger.info(f"✅ [DNS] Resolved {hostname} → {ip}")
77
  except Exception as e:
78
  logger.warning(f"⚠️ [DNS] Failed to resolve {hostname}: {e}")
79
-
80
  if hosts_lines:
81
  try:
82
  with open('/etc/hosts', 'a') as f:
@@ -90,7 +77,6 @@ def fix_dns():
90
  logger.warning(f"⚠️ [DNS] hosts write error: {e}")
91
  else:
92
  logger.error("❌ [DNS] Could not resolve any Discord hosts.")
93
-
94
  except ImportError:
95
  logger.warning("⚠️ [DNS] dnspython not installed, skipping hosts injection.")
96
  except Exception as e:
@@ -104,10 +90,8 @@ fix_dns()
104
  DISCORD_TOKEN = os.environ.get('DISCORD_TOKEN', '').strip()
105
  SESSION_ID = random.randint(10000, 99999)
106
 
107
- # [CORE] API 密钥池加载逻辑 (多路复用驱动)
108
  raw_keys_pool = os.environ.get('GEMINI_KEYS_POOL', '')
109
  KEY_POOL = []
110
-
111
  if raw_keys_pool:
112
  parts = raw_keys_pool.split(',')
113
  for k in parts:
@@ -123,24 +107,17 @@ else:
123
  else:
124
  logger.critical("❌ [CRITICAL] NO API KEYS FOUND.")
125
 
126
- # 设置 Discord Intent 权限
127
  intents = discord.Intents.default()
128
  intents.message_content = True
129
  intents.members = True
130
 
131
- # 默认锁定稳定模型,防止因模型探测导致的异步阻塞
132
  ACTIVE_MODEL_ID = "gemini-2.0-flash-lite"
133
-
134
- # [HuggingFace] 持久化路径 (优先使用 /data 持久卷)
135
  DB_PATH = '/data/bk_gta_v62_unified.json' if os.path.exists('/data') else 'bk_gta_v62_unified.json'
136
 
137
  # ==============================================================================
138
  # 2. 状态引擎与数据库 (V62.9 Unified Analytics)
139
  # ==============================================================================
140
  class BKEngine:
141
- """
142
- 核心数据引擎:负责量化资产持久化、状态树管理及风险指标对账
143
- """
144
  def __init__(self):
145
  self.db_file = DB_PATH
146
  self.data = self._load_db()
@@ -154,7 +131,6 @@ class BKEngine:
154
  try:
155
  with open(self.db_file, 'r', encoding='utf-8') as f:
156
  content = json.load(f)
157
- # 版本兼容性对账
158
  if "users" not in content: content["users"] = {}
159
  return content
160
  except Exception as e:
@@ -175,7 +151,6 @@ class BKEngine:
175
  }
176
 
177
  def save_db(self):
178
- """执行物理持久化,确保数据资产安全"""
179
  try:
180
  temp_file = self.db_file + '.tmp'
181
  with open(temp_file, 'w', encoding='utf-8') as f:
@@ -188,15 +163,9 @@ class BKEngine:
188
  uid = str(user_id)
189
  if uid not in self.data["users"]:
190
  self.data["users"][uid] = {
191
- "elo": 1200,
192
- "points": 1000,
193
- "faction": None,
194
- "audits_count": 0,
195
- "total_score": 0.0,
196
- "highest_score": 0.0,
197
- "win_streak": 0,
198
- "mdd_record": 0.0,
199
- "sharpe_record": 0.0,
200
  "performance_log": [],
201
  "last_update": str(datetime.datetime.now())
202
  }
@@ -210,38 +179,24 @@ class BKEngine:
210
  u["highest_score"] = score
211
  if mdd is not None: u["mdd_record"] = mdd
212
  if sharpe is not None: u["sharpe_record"] = sharpe
213
-
214
- # 奖励计算:审计积分奖励
215
  u["points"] += 50
216
  u["last_update"] = str(datetime.datetime.now())
217
  u["performance_log"].append({"score": score, "time": str(datetime.datetime.now())})
218
  self.save_db()
219
 
220
  def update_elo(self, winner_id, loser_id):
221
- """
222
- 基于 ELO 等级分系统的量化能力更新算法
223
- K = 32 (常数项)
224
- """
225
  w = self.get_user(winner_id)
226
  l = self.get_user(loser_id)
227
-
228
  K = 32
229
  Rw, Rl = w["elo"], l["elo"]
230
- # 计算胜率期望
231
  Ew = 1 / (1 + 10 ** ((Rl - Rw) / 400))
232
-
233
- # 更新等级分
234
  w["elo"] = round(Rw + K * (1 - Ew))
235
  l["elo"] = round(Rl + K * (0 - Ew))
236
-
237
  w["win_streak"] += 1
238
  l["win_streak"] = 0
239
-
240
- # 阵营荣誉点累加
241
  if w["faction"] and w["faction"] in self.data["factions"]:
242
  self.data["factions"][w["faction"]]["score"] += 15
243
  self.data["factions"][w["faction"]]["total_wins"] += 1
244
-
245
  self.save_db()
246
  return w["elo"], l["elo"], w["win_streak"]
247
 
@@ -251,7 +206,6 @@ bk_engine = BKEngine()
251
  # 3. 核心辅助内核 (Helper Kernels & Security)
252
  # ==============================================================================
253
  def safe_extract_float(text, key):
254
- """鲁棒性数值提取引擎,支持多重前缀匹配"""
255
  try:
256
  patterns = [
257
  rf"{key}:\s*([-+]?\d*\.?\d+)",
@@ -265,7 +219,6 @@ def safe_extract_float(text, key):
265
  except Exception: return 0.0
266
 
267
  def safe_extract_text(text, key_start, key_end=None):
268
- """逻辑块提取引擎,支持长文本切片"""
269
  try:
270
  if key_end:
271
  pattern = rf"{key_start}:(.*?){key_end}:"
@@ -274,16 +227,11 @@ def safe_extract_text(text, key_start, key_end=None):
274
  match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
275
  if match:
276
  content = match.group(1).strip()
277
- # 过滤 Markdown 字符防止渲染崩溃
278
  return content.replace('*', '').replace('`', '').strip()
279
  return "N/A"
280
  except Exception: return "N/A"
281
 
282
  async def call_gemini_multiplex(prompt, attachment=None):
283
- """
284
- 直接 REST API 调用 Gemini,彻底绕过 SDK 的 AFC 额外请求开销。
285
- 同时支持 v1beta 和 v1 双端点自动降级。
286
- """
287
  if not KEY_POOL:
288
  logger.error("Request Blocked: No API Keys available.")
289
  return "ERROR: NO_KEYS"
@@ -297,22 +245,13 @@ async def call_gemini_multiplex(prompt, attachment=None):
297
  return "ERROR: IMAGE_READ_FAILURE"
298
 
299
  import aiohttp
300
-
301
- # 构建请求体(只构建一次,所有 Key 共用)
302
  parts = [{"text": prompt}]
303
  if image_data:
304
  b64 = base64.b64encode(image_data).decode("utf-8")
305
- parts.append({
306
- "inlineData": {
307
- "mimeType": "image/png",
308
- "data": b64
309
- }
310
- })
311
  payload = {"contents": [{"parts": parts}]}
312
 
313
- # 双端点降级策略:先 v1beta,若 404 则降级 v1
314
  api_versions = ["v1beta", "v1"]
315
-
316
  for api_ver in api_versions:
317
  for idx, api_key in enumerate(KEY_POOL):
318
  try:
@@ -320,34 +259,28 @@ async def call_gemini_multiplex(prompt, attachment=None):
320
  f"https://generativelanguage.googleapis.com/{api_ver}/models/"
321
  f"{ACTIVE_MODEL_ID}:generateContent?key={api_key}"
322
  )
323
-
324
  async with aiohttp.ClientSession() as session:
325
  async with session.post(
326
  url, json=payload,
327
  timeout=aiohttp.ClientTimeout(total=30)
328
  ) as resp:
329
  status = resp.status
330
-
331
  if status == 200:
332
  data = await resp.json()
333
  text = data["candidates"][0]["content"]["parts"][0]["text"]
334
  logger.info(f"✅ Gemini OK via {api_ver} key #{idx+1}")
335
  return text
336
-
337
  elif status == 429:
338
  logger.warning(f"Key #{idx+1} ({api_ver}) got 429, switching...")
339
  await asyncio.sleep(3)
340
  continue
341
-
342
  elif status == 404:
343
  logger.warning(f"Model not found on {api_ver}, trying next version...")
344
- break # 跳出 key 循环,尝试下一个 api_version
345
-
346
  else:
347
  body = await resp.text()
348
  logger.error(f"Key #{idx+1} ({api_ver}) HTTP {status}: {body[:300]}")
349
  continue
350
-
351
  except asyncio.TimeoutError:
352
  logger.warning(f"Key #{idx+1} timeout, switching...")
353
  continue
@@ -360,14 +293,9 @@ async def call_gemini_multiplex(prompt, attachment=None):
360
  # ==============================================================================
361
  # 4. Gradio 保活哨兵 (替代原 Flask keep_alive)
362
  # ==============================================================================
363
- # bot 全局引用,供 get_status 和 keep_alive 使用
364
  bot = None
365
 
366
  def get_status():
367
- """
368
- 提供 HTTP 心跳接口,返回逻辑状态。
369
- 功能等同原 Flask '/' 路由,迁移至 Gradio 以适配 HuggingFace。
370
- """
371
  now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
372
  latency = "N/A"
373
  bot_status = "OFFLINE"
@@ -377,9 +305,7 @@ def get_status():
377
  latency = f"{round(bot.latency * 1000, 2)}ms"
378
  except Exception as e:
379
  logger.warning(f"Heartbeat probe failed: {e}")
380
-
381
  uptime = str(datetime.datetime.now() - bk_engine.system_uptime).split('.')[0]
382
-
383
  return {
384
  "status": "BK-GTA-V62.9-ULTIMATE-ACTIVE",
385
  "engine": "BK-GTA-Logic-Kernel",
@@ -394,27 +320,18 @@ def get_status():
394
  }
395
 
396
  def keep_alive():
397
- """
398
- 开启独立守护线程运行 Gradio 服务。
399
- 功能等同原 Flask keep_alive(),绑定 7860 端口 (HuggingFace 网关入口)。
400
- """
401
  def run_gradio():
402
  try:
403
- # 抑制 Gradio 内部日志噪音
404
  gradio_logger = logging.getLogger('gradio')
405
  gradio_logger.setLevel(logging.WARNING)
406
-
407
  iface = gr.Interface(
408
- fn=get_status,
409
- inputs=None,
410
- outputs="json",
411
  title="BK-GTA Sentinel AI",
412
  description="Physical architecture monitor. Logic Integrity: Secure."
413
  )
414
  iface.launch(server_name="0.0.0.0", server_port=7860)
415
  except Exception as e:
416
  logger.error(f"⚠️ [Sentinel] Gradio Server Failure: {e}")
417
-
418
  t = threading.Thread(target=run_gradio)
419
  t.daemon = True
420
  t.start()
@@ -424,14 +341,9 @@ def keep_alive():
424
  # 5. Bot 工厂 + 全模块命令注册 (Bot Factory & Full Command Registration)
425
  # ==============================================================================
426
  def create_bot_with_commands():
427
- """
428
- 创建完整的 Bot 实例并注册所有命令/事件。
429
- 支持在网络重试时重建 Bot 实例而不丢失任何功能。
430
- 架构等同原版在模块级定义 @bot.command(),但通过工厂封装实现可重建。
431
- """
432
  global bot
433
  new_bot = commands.Bot(command_prefix='!', intents=intents)
434
- bot = new_bot # 更新全局引用
435
 
436
  # ==================================================================
437
  # 模块一:全网天梯与个人档案 (Rank & Profile System)
@@ -441,8 +353,6 @@ def create_bot_with_commands():
441
  """展现量化交易员的详细画像与 ELO 评级"""
442
  target = member or ctx.author
443
  u = bk_engine.get_user(target.id)
444
-
445
- # 动态段位逻辑展开
446
  elo = u['elo']
447
  if elo < 1300: rank, color = "Iron (学徒) 🗑️", 0x717d7e
448
  elif elo < 1500: rank, color = "Bronze (资深) 🥉", 0xcd7f32
@@ -454,12 +364,9 @@ def create_bot_with_commands():
454
  emb = discord.Embed(title=f"📊 Quantitative Profile: {target.display_name}", color=color)
455
  emb.add_field(name="Rating (ELO)", value=f"**{elo}**\nRank: `{rank}`", inline=True)
456
  emb.add_field(name="Assets (Points)", value=f"💰 **{u['points']}**", inline=True)
457
-
458
  f_name = u['faction']
459
  f_emoji = bk_engine.data["factions"].get(f_name, {}).get("emoji", "🌐") if f_name else "Ronin"
460
  emb.add_field(name="Faction", value=f"{f_emoji} {f_name or 'Unassigned'}", inline=True)
461
-
462
- # 展开性能统计指标
463
  perf_stats = (
464
  f"Total Audits: {u['audits_count']}\n"
465
  f"Peak Audit Score: {u['highest_score']:.2f}\n"
@@ -467,10 +374,8 @@ def create_bot_with_commands():
467
  f"Max Win Streak: {u['win_streak']}"
468
  )
469
  emb.add_field(name="Risk-Adjusted Performance", value=f"```yaml\n{perf_stats}\n```", inline=False)
470
-
471
  if target.avatar:
472
  emb.set_thumbnail(url=target.avatar.url)
473
-
474
  emb.set_footer(text=f"System Session: {SESSION_ID} | Logic V62.9")
475
  await ctx.send(embed=emb)
476
 
@@ -480,28 +385,21 @@ def create_bot_with_commands():
480
  all_users = []
481
  for uid, data in bk_engine.data["users"].items():
482
  all_users.append({"id": uid, "elo": data["elo"], "score": data["highest_score"]})
483
-
484
- # 按 ELO 排序
485
  top_elo = sorted(all_users, key=lambda x: x["elo"], reverse=True)[:10]
486
-
487
  desc = ""
488
  for idx, u in enumerate(top_elo):
489
  medal = "🥇" if idx == 0 else "🥈" if idx == 1 else "🥉" if idx == 2 else f"{idx+1}."
490
  desc += f"{medal} <@{u['id']}> : ELO **{u['elo']}** | Peak Score: **{u['score']:.1f}**\n"
491
-
492
  emb = discord.Embed(
493
  title="🏆 BK-GTA Institutional Leaderboard",
494
  description=desc or "Scanning market data... (Waiting for submissions)",
495
  color=0xf1c40f
496
  )
497
-
498
- # 加入阵营战统计
499
  f_info = ""
500
  for name, info in bk_engine.data["factions"].items():
501
  f_info += f"{info['emoji']} {name}: {info['score']} pts "
502
  if f_info:
503
  emb.add_field(name="⚔️ Faction War Status", value=f_info, inline=False)
504
-
505
  await ctx.send(embed=emb)
506
 
507
  # ==================================================================
@@ -512,36 +410,27 @@ def create_bot_with_commands():
512
  """分析新闻/报表截图,并基于机构逻辑给出宏观评分"""
513
  if not ctx.message.attachments:
514
  return await ctx.send("❌ Audit Refused: 请上传新闻图表。")
515
-
516
  m = await ctx.send("📡 **[System] Parsing Macro Pulse...**")
517
-
518
  prompt = (
519
  "你是一名对冲基金首席策略师。请解析此截图并输出:\n"
520
  "1. 情绪评分 (SCORE): -10 (极度利空) 到 +10 (极度利多)。\n"
521
  "2. 核心变量 (SUMMARY): 30字内概括。\n"
522
  "3. 隐含逻辑 (TAKE): 机构视角的解读 (100字内)。\n"
523
  "输出格式必须严格如下:\n"
524
- "SCORE: [分值]\n"
525
- "SUMMARY: [内容]\n"
526
- "TAKE: [解读]"
527
  )
528
-
529
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
530
-
531
  if "ERROR" in res:
532
  await m.delete()
533
  return await ctx.send("🔴 [Audit Failure] Intelligence node offline.")
534
-
535
  score = safe_extract_float(res, "SCORE")
536
  summary = safe_extract_text(res, "SUMMARY", "TAKE")
537
  take = safe_extract_text(res, "TAKE")
538
-
539
  color = 0x2ecc71 if score > 0 else 0xe74c3c if score < 0 else 0xbdc3c7
540
  emb = discord.Embed(title="📰 Macro Sentiment Pulse", color=color)
541
  emb.add_field(name="Sentiment Score", value=f"**{score:+.1f} / 10**", inline=True)
542
  emb.add_field(name="Core Catalyst", value=summary, inline=False)
543
  emb.add_field(name="🏦 Institutional Analysis", value=f"```\n{take}\n```", inline=False)
544
-
545
  await m.delete()
546
  await ctx.send(embed=emb)
547
 
@@ -553,11 +442,8 @@ def create_bot_with_commands():
553
  """基于截图全维度识别回测/实盘参数,并执行风控打分"""
554
  if not ctx.message.attachments:
555
  return await ctx.send("❌ Audit Refused: 请上传结算单或净值曲线。")
556
-
557
  sid = random.randint(1000, 9999)
558
  m = await ctx.send(f"🛡️ **[SID:{sid}] Audit Engine Initializing...**")
559
-
560
- # 深度审计 Prompt
561
  prompt = (
562
  "你是一名顶级量化风险官。请执行全维度审计。\n"
563
  "【提取要求】:\n"
@@ -567,23 +453,16 @@ def create_bot_with_commands():
567
  "输出格式:\n"
568
  "RET: [值]\nMDD: [值]\nPF: [值]\nSHARPE: [值]\nTRADES: [笔数]\nS_BASE: [分]\nH_ZH: [优势]"
569
  )
570
-
571
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
572
-
573
  if "ERROR" in res:
574
  await m.delete()
575
  return await ctx.send("🔴 [Audit Crash] Engine disconnected.")
576
-
577
- # 提取数值
578
  ret = safe_extract_float(res, "RET")
579
  mdd = safe_extract_float(res, "MDD")
580
  pf = safe_extract_float(res, "PF")
581
  sharpe = safe_extract_float(res, "SHARPE")
582
  trades = int(safe_extract_float(res, "TRADES"))
583
  s_base = safe_extract_float(res, "S_BASE")
584
-
585
- # 逻辑加权算法展开:S = S_base * (log10(Trades)/1.5) * Sharpe_Factor
586
- # 如果 MDD > 35%,执行风控一票否决
587
  if mdd > 35.0:
588
  s_final = 0.0
589
  status = "⚠️ REJECTED (High MDD Risk)"
@@ -594,14 +473,10 @@ def create_bot_with_commands():
594
  s_final = min(max(s_final, 0.0), 100.0)
595
  status = "✅ AUDIT CLEARED"
596
  color = 0x2ecc71
597
-
598
- # 持久化
599
  bk_engine.update_audit_stats(ctx.author.id, s_final, mdd, sharpe)
600
  h_zh = safe_extract_text(res, "H_ZH")
601
-
602
  emb = discord.Embed(title="🛡️ BK-GTA Gene Audit Report", color=color)
603
  emb.add_field(name="Verdict", value=f"**{status}**", inline=False)
604
-
605
  metrics = (
606
  f"💰 Return: {ret}% | MDD: {mdd}%\n"
607
  f"⚖️ PF: {pf} | Sharpe: {sharpe}\n"
@@ -610,7 +485,6 @@ def create_bot_with_commands():
610
  emb.add_field(name="Data Extraction", value=f"```\n{metrics}\n```", inline=False)
611
  emb.add_field(name="Analysis Score", value=f"**{s_final:.2f} / 100**", inline=True)
612
  emb.add_field(name="Highlights", value=h_zh, inline=False)
613
-
614
  emb.set_footer(text=f"SID: {sid} | Assets +50")
615
  await m.delete()
616
  await ctx.send(embed=emb)
@@ -624,8 +498,7 @@ def create_bot_with_commands():
624
  """由合伙人开启的宏观/行情博弈盘口"""
625
  bid = str(random.randint(100, 999))
626
  bk_engine.active_bets[bid] = {
627
- "topic": topic,
628
- "up": 0, "down": 0,
629
  "u_up": {}, "u_down": {},
630
  "status": "OPEN",
631
  "start_time": str(datetime.datetime.now())
@@ -637,27 +510,20 @@ def create_bot_with_commands():
637
  """交易员消耗积分参与盘口对冲"""
638
  if bid not in bk_engine.active_bets:
639
  return await ctx.send("❌ Error: Invalid Bet ID.")
640
-
641
  b = bk_engine.active_bets[bid]
642
  if b["status"] != "OPEN":
643
  return await ctx.send("❌ Error: This market is closed.")
644
-
645
  u = bk_engine.get_user(ctx.author.id)
646
  if u["points"] < amt or amt <= 0:
647
  return await ctx.send("❌ Error: Insufficient asset points.")
648
-
649
  side = side.upper()
650
  if side not in ["UP", "DOWN"]:
651
  return await ctx.send("❌ Error: Choose UP or DOWN.")
652
-
653
- # 锁定积分
654
  u["points"] -= amt
655
  uid = str(ctx.author.id)
656
-
657
  pool_key = "u_up" if side == "UP" else "u_down"
658
  b[side.lower()] += amt
659
  b[pool_key][uid] = b[pool_key].get(uid, 0) + amt
660
-
661
  bk_engine.save_db()
662
  await ctx.send(f"✅ Position Locked: {ctx.author.name} added {amt} to {side} pool.")
663
 
@@ -668,92 +534,159 @@ def create_bot_with_commands():
668
  if bid not in bk_engine.active_bets: return
669
  b = bk_engine.active_bets[bid]
670
  winner = winner.upper()
671
-
672
  win_pool = b["u_up"] if winner == "UP" else b["u_down"]
673
  total_win = b["up"] if winner == "UP" else b["down"]
674
  total_lose = b["down"] if winner == "UP" else b["up"]
675
-
676
  if total_win > 0:
677
  for uid, amt in win_pool.items():
678
- # 分配逻辑: 原始积分 + (个人占比 * 输家总积分)
679
  profit = (amt / total_win) * total_lose
680
  user = bk_engine.get_user(int(uid))
681
  user["points"] += round(amt + profit)
682
-
683
  b["status"] = "SETTLED"
684
  bk_engine.save_db()
685
  await ctx.send(f"🏁 Bet `{bid}` Settled! Winner: **{winner}**. Profits Distributed.")
686
 
687
  # ==================================================================
688
- # 模块五:PVP 竞技场 (Arena PvP Engine)
689
  # ==================================================================
690
  @new_bot.command()
691
- async def challenge(ctx, member: discord.Member):
692
- """发起基于量化能力的 1对1 竞技"""
693
  if member.id == ctx.author.id:
694
  return await ctx.send("❌ Logical Error: 不能向自己发起决斗。")
695
-
 
696
  did = f"DUEL-{random.randint(100, 999)}"
697
  bk_engine.pending_challenges[member.id] = {
698
  "challenger": ctx.author.id,
699
  "did": did,
 
700
  "expiry": datetime.datetime.now() + datetime.timedelta(hours=1)
701
  }
702
-
703
- await ctx.send(f"⚔️ **{ctx.author.name}** 发起决斗请求!目标:{member.mention}\nID: `{did}` | `!accept` 接受。")
 
 
704
 
705
  @new_bot.command()
706
  async def accept(ctx):
707
  """接受竞技场挑战"""
708
  if ctx.author.id not in bk_engine.pending_challenges:
709
  return await ctx.send("❌ Error: No pending challenges.")
710
-
711
  req = bk_engine.pending_challenges.pop(ctx.author.id)
712
  did = req["did"]
713
-
714
  bk_engine.active_duels[did] = {
715
  "p1": req["challenger"], "p2": ctx.author.id,
716
  "s1": None, "s2": None,
 
717
  "start": str(datetime.datetime.now())
718
  }
719
-
720
- await ctx.send(f"🔥 **ARENA LOCKED!** Duel ID: `{did}`\n双方请通过 `!duel_submit {did}` 上传审计截图。")
 
 
721
 
722
  @new_bot.command()
723
  async def duel_submit(ctx, did: str):
724
- """在对决中提交截图,执行最终胜负判定"""
725
  if did not in bk_engine.active_duels:
726
  return await ctx.send("❌ Error: Duel not active.")
727
-
728
  if not ctx.message.attachments:
729
  return await ctx.send("❌ Refused: 请上传结算截图。")
730
-
731
  duel = bk_engine.active_duels[did]
732
- if ctx.author.id not in [duel["p1"], duel["p2"]]: return
733
-
734
  m = await ctx.send(f"🔍 **[Arena] Auditing Submission...**")
735
- prompt = "Arena Duel Audit: Identify Audit Score (0-100). FORMAT: SCORE: [val]"
 
 
 
736
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
737
  score = safe_extract_float(res, "SCORE")
 
 
 
 
738
 
739
- if ctx.author.id == duel["p1"]: duel["s1"] = score
740
- else: duel["s2"] = score
741
-
742
- # 逻辑分歧点:如果双方都交了,执行结算
743
  if duel["s1"] is not None and duel["s2"] is not None:
744
- wid, lid = (duel["p1"], duel["p2"]) if duel["s1"] > duel["s2"] else (duel["p2"], duel["p1"])
 
 
 
745
  new_w, new_l, strk = bk_engine.update_elo(wid, lid)
746
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  emb = discord.Embed(title="🏆 Arena Final Verdict", color=0xf1c40f)
748
- emb.add_field(name="🥇 Winner", value=f"<@{wid}>\nScore: {max(duel['s1'], duel['s2']):.1f}\nNew ELO: {new_w}", inline=True)
749
- emb.add_field(name="🥈 Loser", value=f"<@{lid}>\nScore: {min(duel['s1'], duel['s2']):.1f}\nNew ELO: {new_l}", inline=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
750
  emb.set_footer(text=f"Streak: {strk} | Duel ID: {did}")
751
-
752
  await ctx.send(embed=emb)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
  bk_engine.active_duels.pop(did)
754
  else:
755
  await ctx.send(f"✅ Submission Received (Score: {score:.1f}). Waiting for Opponent...")
756
-
757
  await m.delete()
758
 
759
  # ==================================================================
@@ -764,71 +697,247 @@ def create_bot_with_commands():
764
  """提取审计数据并渲染具备社交传播属性的海报图片"""
765
  if not ctx.message.attachments:
766
  return await ctx.send("❌ 请上传审计截图��")
767
-
768
  m = await ctx.send("🎨 **[GPU-Node] Rendering Institutional Poster...**")
769
-
770
  prompt = "Extract RET: [val] and SHARPE: [val]. FORMAT: RET: [v] SHARPE: [v]"
771
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
772
  ret, sharpe = safe_extract_float(res, "RET"), safe_extract_float(res, "SHARPE")
773
-
774
- # 物理画布绘制逻辑展开
775
  try:
776
  W, H = 600, 800
777
- # 创建暗黑科技风底色
778
  base = Image.new('RGB', (W, H), color=(18, 20, 30))
779
  draw = ImageDraw.Draw(base)
780
-
781
- # 1. 绘制装饰性雷达背景
782
  for i in range(0, W, 40): draw.line([(i, 0), (i, H)], fill=(30, 35, 45), width=1)
783
  for j in range(0, H, 40): draw.line([(0, j), (W, j)], fill=(30, 35, 45), width=1)
784
-
785
- # 2. 绘制外边框
786
  draw.rectangle([10, 10, 590, 790], outline=(46, 204, 113), width=2)
787
  draw.rectangle([20, 20, 580, 780], outline=(46, 204, 113), width=5)
788
-
789
- # 3. 填充文字数据 (若服务器无字体则降级)
790
  draw.text((40, 60), "BK-GTA QUANTITATIVE SYSTEM", fill=(255, 255, 255))
791
  draw.text((40, 150), f"TRADER ID: {ctx.author.name.upper()}", fill=(46, 204, 113))
792
  draw.text((40, 260), f"ALPHA RETURN: +{ret}%", fill=(255, 255, 255))
793
  draw.text((40, 360), f"SHARPE RATIO: {sharpe}", fill=(255, 255, 255))
794
  draw.text((40, 460), f"SESSION: {SESSION_ID}", fill=(120, 120, 130))
795
-
796
- # 加入验证水印
797
  draw.text((40, 720), "VERIFIED BY BK-GTA SENTINEL AI", fill=(46, 204, 113))
798
-
799
- # 内存缓冲交付
800
  with io.BytesIO() as image_binary:
801
  base.save(image_binary, 'PNG')
802
  image_binary.seek(0)
803
  await ctx.send(file=discord.File(fp=image_binary, filename=f"Viral_{ctx.author.id}.png"))
804
-
805
  except Exception as e:
806
  logger.error(f"Render Error: {e}")
807
  await ctx.send(f"❌ 渲染引擎报错: `{e}`")
808
  finally:
809
  await m.delete()
810
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
811
  # ==================================================================
812
  # 系统对账与自动化任务 (System Reliability)
813
  # ==================================================================
814
  @tasks.loop(hours=1)
815
  async def auto_backup():
816
- """本地资产数据库每小时原子化备份"""
817
  bk_engine.save_db()
818
  logger.info("💾 [Checkpoint] Data persistence executed.")
819
 
820
  @new_bot.event
821
  async def on_ready():
822
- # 激活心跳对账
823
  if not auto_backup.is_running(): auto_backup.start()
824
-
825
- # 修改机器人活跃状态
826
  activity = discord.Activity(type=discord.ActivityType.watching, name="HFT Markets")
827
  await new_bot.change_presence(status=discord.Status.online, activity=activity)
828
-
829
  logger.info(f"--------------------------------------------------")
830
  logger.info(f"✅ [V62.9 Ultimate] BK-GTA Architecture Online")
831
- logger.info(f"🚀 Session: {SESSION_ID} | Logic Integrity: 700+ Lines")
832
  logger.info(f"--------------------------------------------------")
833
 
834
  @new_bot.event
@@ -841,12 +950,9 @@ def create_bot_with_commands():
841
 
842
  @new_bot.event
843
  async def on_command_error(ctx, error):
844
- """全局错误拦截器,严禁静默失效"""
845
  if isinstance(error, commands.CommandNotFound): return
846
  if isinstance(error, commands.MissingPermissions):
847
  return await ctx.send("❌ 权限审计未通过:仅限合伙人执行。")
848
-
849
- # 堆栈追踪打印至 Console
850
  exc = "".join(traceback.format_exception(type(error), error, error.__traceback__))
851
  logger.error(f"🔥 [System Crash] {exc}")
852
  await ctx.send(f"⚠️ [Logic Breach] 内部逻辑断裂: `{error}`")
@@ -857,7 +963,6 @@ def create_bot_with_commands():
857
  # ==============================================================================
858
  # 6. 物理点火启动链 (Physical Start Engine)
859
  # ==============================================================================
860
- # 创建初始 Bot 实例
861
  bot = create_bot_with_commands()
862
 
863
  if __name__ == "__main__":
@@ -869,17 +974,13 @@ if __name__ == "__main__":
869
  logger.info(f" Session: {SESSION_ID}")
870
  logger.info("=" * 55)
871
 
872
- # 1. 物理启动 Gradio 保活接口 (替代原 Flask keep_alive)
873
  keep_alive()
874
 
875
- # 2. [HuggingFace 专用] 等待容器网络就绪
876
  logger.info("⏳ [Boot] Waiting 10s for container network to initialize...")
877
  time.sleep(10)
878
 
879
- # 3. 逻辑启动 Discord Bot 核心 (主线程,与原 Replit 架构一致)
880
  if not DISCORD_TOKEN:
881
  logger.critical("❌ CRITICAL: DISCORD_TOKEN is missing in environment.")
882
- # 保持进程存活以维持 Gradio 哨兵
883
  while True:
884
  time.sleep(3600)
885
  else:
@@ -890,38 +991,30 @@ if __name__ == "__main__":
890
  wait = min(30 * attempt, 120)
891
  logger.info(f"⏳ [Boot] Retry in {wait}s (attempt {attempt}/{max_retries})...")
892
  time.sleep(wait)
893
- # bot.run() 关闭后实例不可复用,必须重建
894
  bot = create_bot_with_commands()
895
-
896
  logger.info(f"🚀 [Boot] Starting Discord connection (attempt {attempt}/{max_retries})...")
897
  bot.run(DISCORD_TOKEN)
898
- break # 正常退出
899
-
900
  except discord.LoginFailure:
901
  logger.critical("❌ [FATAL] Invalid DISCORD_TOKEN! Check token in Secrets.")
902
- break # Token 错误不重试
903
-
904
  except discord.PrivilegedIntentsRequired:
905
  logger.critical(
906
  "❌ [FATAL] Privileged Intents not enabled!\n"
907
  "→ https://discord.com/developers/applications → Bot tab\n"
908
  "→ Enable 'SERVER MEMBERS INTENT' and 'MESSAGE CONTENT INTENT'"
909
  )
910
- break # 权限错误不重试
911
-
912
  except (OSError, ConnectionError) as e:
913
  logger.error(f"🌐 [Boot] Network error (attempt {attempt}): {e}")
914
- # 网络错误值得重试
915
-
916
  except Exception as e:
917
  logger.critical(f"🔥 ENGINE COLLAPSE (attempt {attempt}): {e}")
918
  traceback.print_exc()
919
 
920
- # 即使 Bot 停止,保持主线程存活以维持 Gradio
921
  logger.warning("🔴 [Boot] Bot stopped. Keeping process alive for Gradio...")
922
  while True:
923
  time.sleep(3600)
924
 
925
  # ==============================================================================
926
- # END OF ARCHITECT SOURCE CODE | BK-GTA V62.9 — HuggingFace Edition
927
  # ==============================================================================
 
35
  # 0.5 [HuggingFace 专用] DNS 稳定性修复 (增强版)
36
  # ==============================================================================
37
  def fix_dns():
 
 
 
 
 
 
38
  try:
39
  with open('/etc/resolv.conf', 'r') as f:
40
  current = f.read()
 
45
  except Exception as e:
46
  logger.warning(f"⚠️ [DNS] resolv.conf write failed: {e}")
47
 
 
48
  hosts_to_resolve = [
49
+ 'discord.com', 'gateway.discord.gg',
50
+ 'cdn.discordapp.com', 'api.discord.com',
 
 
51
  ]
 
52
  try:
53
  import dns.resolver
 
54
  resolver = dns.resolver.Resolver(configure=False)
55
  resolver.nameservers = ['8.8.8.8', '1.1.1.1', '8.8.4.4']
56
  resolver.timeout = 10
57
  resolver.lifetime = 10
 
58
  hosts_lines = []
59
  for hostname in hosts_to_resolve:
60
  try:
 
64
  logger.info(f"✅ [DNS] Resolved {hostname} → {ip}")
65
  except Exception as e:
66
  logger.warning(f"⚠️ [DNS] Failed to resolve {hostname}: {e}")
 
67
  if hosts_lines:
68
  try:
69
  with open('/etc/hosts', 'a') as f:
 
77
  logger.warning(f"⚠️ [DNS] hosts write error: {e}")
78
  else:
79
  logger.error("❌ [DNS] Could not resolve any Discord hosts.")
 
80
  except ImportError:
81
  logger.warning("⚠️ [DNS] dnspython not installed, skipping hosts injection.")
82
  except Exception as e:
 
90
  DISCORD_TOKEN = os.environ.get('DISCORD_TOKEN', '').strip()
91
  SESSION_ID = random.randint(10000, 99999)
92
 
 
93
  raw_keys_pool = os.environ.get('GEMINI_KEYS_POOL', '')
94
  KEY_POOL = []
 
95
  if raw_keys_pool:
96
  parts = raw_keys_pool.split(',')
97
  for k in parts:
 
107
  else:
108
  logger.critical("❌ [CRITICAL] NO API KEYS FOUND.")
109
 
 
110
  intents = discord.Intents.default()
111
  intents.message_content = True
112
  intents.members = True
113
 
 
114
  ACTIVE_MODEL_ID = "gemini-2.0-flash-lite"
 
 
115
  DB_PATH = '/data/bk_gta_v62_unified.json' if os.path.exists('/data') else 'bk_gta_v62_unified.json'
116
 
117
  # ==============================================================================
118
  # 2. 状态引擎与数据库 (V62.9 Unified Analytics)
119
  # ==============================================================================
120
  class BKEngine:
 
 
 
121
  def __init__(self):
122
  self.db_file = DB_PATH
123
  self.data = self._load_db()
 
131
  try:
132
  with open(self.db_file, 'r', encoding='utf-8') as f:
133
  content = json.load(f)
 
134
  if "users" not in content: content["users"] = {}
135
  return content
136
  except Exception as e:
 
151
  }
152
 
153
  def save_db(self):
 
154
  try:
155
  temp_file = self.db_file + '.tmp'
156
  with open(temp_file, 'w', encoding='utf-8') as f:
 
163
  uid = str(user_id)
164
  if uid not in self.data["users"]:
165
  self.data["users"][uid] = {
166
+ "elo": 1200, "points": 1000, "faction": None,
167
+ "audits_count": 0, "total_score": 0.0, "highest_score": 0.0,
168
+ "win_streak": 0, "mdd_record": 0.0, "sharpe_record": 0.0,
 
 
 
 
 
 
169
  "performance_log": [],
170
  "last_update": str(datetime.datetime.now())
171
  }
 
179
  u["highest_score"] = score
180
  if mdd is not None: u["mdd_record"] = mdd
181
  if sharpe is not None: u["sharpe_record"] = sharpe
 
 
182
  u["points"] += 50
183
  u["last_update"] = str(datetime.datetime.now())
184
  u["performance_log"].append({"score": score, "time": str(datetime.datetime.now())})
185
  self.save_db()
186
 
187
  def update_elo(self, winner_id, loser_id):
 
 
 
 
188
  w = self.get_user(winner_id)
189
  l = self.get_user(loser_id)
 
190
  K = 32
191
  Rw, Rl = w["elo"], l["elo"]
 
192
  Ew = 1 / (1 + 10 ** ((Rl - Rw) / 400))
 
 
193
  w["elo"] = round(Rw + K * (1 - Ew))
194
  l["elo"] = round(Rl + K * (0 - Ew))
 
195
  w["win_streak"] += 1
196
  l["win_streak"] = 0
 
 
197
  if w["faction"] and w["faction"] in self.data["factions"]:
198
  self.data["factions"][w["faction"]]["score"] += 15
199
  self.data["factions"][w["faction"]]["total_wins"] += 1
 
200
  self.save_db()
201
  return w["elo"], l["elo"], w["win_streak"]
202
 
 
206
  # 3. 核心辅助内核 (Helper Kernels & Security)
207
  # ==============================================================================
208
  def safe_extract_float(text, key):
 
209
  try:
210
  patterns = [
211
  rf"{key}:\s*([-+]?\d*\.?\d+)",
 
219
  except Exception: return 0.0
220
 
221
  def safe_extract_text(text, key_start, key_end=None):
 
222
  try:
223
  if key_end:
224
  pattern = rf"{key_start}:(.*?){key_end}:"
 
227
  match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
228
  if match:
229
  content = match.group(1).strip()
 
230
  return content.replace('*', '').replace('`', '').strip()
231
  return "N/A"
232
  except Exception: return "N/A"
233
 
234
  async def call_gemini_multiplex(prompt, attachment=None):
 
 
 
 
235
  if not KEY_POOL:
236
  logger.error("Request Blocked: No API Keys available.")
237
  return "ERROR: NO_KEYS"
 
245
  return "ERROR: IMAGE_READ_FAILURE"
246
 
247
  import aiohttp
 
 
248
  parts = [{"text": prompt}]
249
  if image_data:
250
  b64 = base64.b64encode(image_data).decode("utf-8")
251
+ parts.append({"inlineData": {"mimeType": "image/png", "data": b64}})
 
 
 
 
 
252
  payload = {"contents": [{"parts": parts}]}
253
 
 
254
  api_versions = ["v1beta", "v1"]
 
255
  for api_ver in api_versions:
256
  for idx, api_key in enumerate(KEY_POOL):
257
  try:
 
259
  f"https://generativelanguage.googleapis.com/{api_ver}/models/"
260
  f"{ACTIVE_MODEL_ID}:generateContent?key={api_key}"
261
  )
 
262
  async with aiohttp.ClientSession() as session:
263
  async with session.post(
264
  url, json=payload,
265
  timeout=aiohttp.ClientTimeout(total=30)
266
  ) as resp:
267
  status = resp.status
 
268
  if status == 200:
269
  data = await resp.json()
270
  text = data["candidates"][0]["content"]["parts"][0]["text"]
271
  logger.info(f"✅ Gemini OK via {api_ver} key #{idx+1}")
272
  return text
 
273
  elif status == 429:
274
  logger.warning(f"Key #{idx+1} ({api_ver}) got 429, switching...")
275
  await asyncio.sleep(3)
276
  continue
 
277
  elif status == 404:
278
  logger.warning(f"Model not found on {api_ver}, trying next version...")
279
+ break
 
280
  else:
281
  body = await resp.text()
282
  logger.error(f"Key #{idx+1} ({api_ver}) HTTP {status}: {body[:300]}")
283
  continue
 
284
  except asyncio.TimeoutError:
285
  logger.warning(f"Key #{idx+1} timeout, switching...")
286
  continue
 
293
  # ==============================================================================
294
  # 4. Gradio 保活哨兵 (替代原 Flask keep_alive)
295
  # ==============================================================================
 
296
  bot = None
297
 
298
  def get_status():
 
 
 
 
299
  now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
300
  latency = "N/A"
301
  bot_status = "OFFLINE"
 
305
  latency = f"{round(bot.latency * 1000, 2)}ms"
306
  except Exception as e:
307
  logger.warning(f"Heartbeat probe failed: {e}")
 
308
  uptime = str(datetime.datetime.now() - bk_engine.system_uptime).split('.')[0]
 
309
  return {
310
  "status": "BK-GTA-V62.9-ULTIMATE-ACTIVE",
311
  "engine": "BK-GTA-Logic-Kernel",
 
320
  }
321
 
322
  def keep_alive():
 
 
 
 
323
  def run_gradio():
324
  try:
 
325
  gradio_logger = logging.getLogger('gradio')
326
  gradio_logger.setLevel(logging.WARNING)
 
327
  iface = gr.Interface(
328
+ fn=get_status, inputs=None, outputs="json",
 
 
329
  title="BK-GTA Sentinel AI",
330
  description="Physical architecture monitor. Logic Integrity: Secure."
331
  )
332
  iface.launch(server_name="0.0.0.0", server_port=7860)
333
  except Exception as e:
334
  logger.error(f"⚠️ [Sentinel] Gradio Server Failure: {e}")
 
335
  t = threading.Thread(target=run_gradio)
336
  t.daemon = True
337
  t.start()
 
341
  # 5. Bot 工厂 + 全模块命令注册 (Bot Factory & Full Command Registration)
342
  # ==============================================================================
343
  def create_bot_with_commands():
 
 
 
 
 
344
  global bot
345
  new_bot = commands.Bot(command_prefix='!', intents=intents)
346
+ bot = new_bot
347
 
348
  # ==================================================================
349
  # 模块一:全网天梯与个人档案 (Rank & Profile System)
 
353
  """展现量化交易员的详细画像与 ELO 评级"""
354
  target = member or ctx.author
355
  u = bk_engine.get_user(target.id)
 
 
356
  elo = u['elo']
357
  if elo < 1300: rank, color = "Iron (学徒) 🗑️", 0x717d7e
358
  elif elo < 1500: rank, color = "Bronze (资深) 🥉", 0xcd7f32
 
364
  emb = discord.Embed(title=f"📊 Quantitative Profile: {target.display_name}", color=color)
365
  emb.add_field(name="Rating (ELO)", value=f"**{elo}**\nRank: `{rank}`", inline=True)
366
  emb.add_field(name="Assets (Points)", value=f"💰 **{u['points']}**", inline=True)
 
367
  f_name = u['faction']
368
  f_emoji = bk_engine.data["factions"].get(f_name, {}).get("emoji", "🌐") if f_name else "Ronin"
369
  emb.add_field(name="Faction", value=f"{f_emoji} {f_name or 'Unassigned'}", inline=True)
 
 
370
  perf_stats = (
371
  f"Total Audits: {u['audits_count']}\n"
372
  f"Peak Audit Score: {u['highest_score']:.2f}\n"
 
374
  f"Max Win Streak: {u['win_streak']}"
375
  )
376
  emb.add_field(name="Risk-Adjusted Performance", value=f"```yaml\n{perf_stats}\n```", inline=False)
 
377
  if target.avatar:
378
  emb.set_thumbnail(url=target.avatar.url)
 
379
  emb.set_footer(text=f"System Session: {SESSION_ID} | Logic V62.9")
380
  await ctx.send(embed=emb)
381
 
 
385
  all_users = []
386
  for uid, data in bk_engine.data["users"].items():
387
  all_users.append({"id": uid, "elo": data["elo"], "score": data["highest_score"]})
 
 
388
  top_elo = sorted(all_users, key=lambda x: x["elo"], reverse=True)[:10]
 
389
  desc = ""
390
  for idx, u in enumerate(top_elo):
391
  medal = "🥇" if idx == 0 else "🥈" if idx == 1 else "🥉" if idx == 2 else f"{idx+1}."
392
  desc += f"{medal} <@{u['id']}> : ELO **{u['elo']}** | Peak Score: **{u['score']:.1f}**\n"
 
393
  emb = discord.Embed(
394
  title="🏆 BK-GTA Institutional Leaderboard",
395
  description=desc or "Scanning market data... (Waiting for submissions)",
396
  color=0xf1c40f
397
  )
 
 
398
  f_info = ""
399
  for name, info in bk_engine.data["factions"].items():
400
  f_info += f"{info['emoji']} {name}: {info['score']} pts "
401
  if f_info:
402
  emb.add_field(name="⚔️ Faction War Status", value=f_info, inline=False)
 
403
  await ctx.send(embed=emb)
404
 
405
  # ==================================================================
 
410
  """分析新闻/报表截图,并基于机构逻辑给出宏观评分"""
411
  if not ctx.message.attachments:
412
  return await ctx.send("❌ Audit Refused: 请上传新闻图表。")
 
413
  m = await ctx.send("📡 **[System] Parsing Macro Pulse...**")
 
414
  prompt = (
415
  "你是一名对冲基金首席策略师。请解析此截图并输出:\n"
416
  "1. 情绪评分 (SCORE): -10 (极度利空) 到 +10 (极度利多)。\n"
417
  "2. 核心变量 (SUMMARY): 30字内概括。\n"
418
  "3. 隐含逻辑 (TAKE): 机构视角的解读 (100字内)。\n"
419
  "输出格式必须严格如下:\n"
420
+ "SCORE: [分值]\nSUMMARY: [内容]\nTAKE: [解读]"
 
 
421
  )
 
422
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
 
423
  if "ERROR" in res:
424
  await m.delete()
425
  return await ctx.send("🔴 [Audit Failure] Intelligence node offline.")
 
426
  score = safe_extract_float(res, "SCORE")
427
  summary = safe_extract_text(res, "SUMMARY", "TAKE")
428
  take = safe_extract_text(res, "TAKE")
 
429
  color = 0x2ecc71 if score > 0 else 0xe74c3c if score < 0 else 0xbdc3c7
430
  emb = discord.Embed(title="📰 Macro Sentiment Pulse", color=color)
431
  emb.add_field(name="Sentiment Score", value=f"**{score:+.1f} / 10**", inline=True)
432
  emb.add_field(name="Core Catalyst", value=summary, inline=False)
433
  emb.add_field(name="🏦 Institutional Analysis", value=f"```\n{take}\n```", inline=False)
 
434
  await m.delete()
435
  await ctx.send(embed=emb)
436
 
 
442
  """基于截图全维度识别回测/实盘参数,并执行风控打分"""
443
  if not ctx.message.attachments:
444
  return await ctx.send("❌ Audit Refused: 请上传结算单或净值曲线。")
 
445
  sid = random.randint(1000, 9999)
446
  m = await ctx.send(f"🛡️ **[SID:{sid}] Audit Engine Initializing...**")
 
 
447
  prompt = (
448
  "你是一名顶级量化风险官。请执行全维度审计。\n"
449
  "【提取要求】:\n"
 
453
  "输出格式:\n"
454
  "RET: [值]\nMDD: [值]\nPF: [值]\nSHARPE: [值]\nTRADES: [笔数]\nS_BASE: [分]\nH_ZH: [优势]"
455
  )
 
456
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
 
457
  if "ERROR" in res:
458
  await m.delete()
459
  return await ctx.send("🔴 [Audit Crash] Engine disconnected.")
 
 
460
  ret = safe_extract_float(res, "RET")
461
  mdd = safe_extract_float(res, "MDD")
462
  pf = safe_extract_float(res, "PF")
463
  sharpe = safe_extract_float(res, "SHARPE")
464
  trades = int(safe_extract_float(res, "TRADES"))
465
  s_base = safe_extract_float(res, "S_BASE")
 
 
 
466
  if mdd > 35.0:
467
  s_final = 0.0
468
  status = "⚠️ REJECTED (High MDD Risk)"
 
473
  s_final = min(max(s_final, 0.0), 100.0)
474
  status = "✅ AUDIT CLEARED"
475
  color = 0x2ecc71
 
 
476
  bk_engine.update_audit_stats(ctx.author.id, s_final, mdd, sharpe)
477
  h_zh = safe_extract_text(res, "H_ZH")
 
478
  emb = discord.Embed(title="🛡️ BK-GTA Gene Audit Report", color=color)
479
  emb.add_field(name="Verdict", value=f"**{status}**", inline=False)
 
480
  metrics = (
481
  f"💰 Return: {ret}% | MDD: {mdd}%\n"
482
  f"⚖️ PF: {pf} | Sharpe: {sharpe}\n"
 
485
  emb.add_field(name="Data Extraction", value=f"```\n{metrics}\n```", inline=False)
486
  emb.add_field(name="Analysis Score", value=f"**{s_final:.2f} / 100**", inline=True)
487
  emb.add_field(name="Highlights", value=h_zh, inline=False)
 
488
  emb.set_footer(text=f"SID: {sid} | Assets +50")
489
  await m.delete()
490
  await ctx.send(embed=emb)
 
498
  """由合伙人开启的宏观/行情博弈盘口"""
499
  bid = str(random.randint(100, 999))
500
  bk_engine.active_bets[bid] = {
501
+ "topic": topic, "up": 0, "down": 0,
 
502
  "u_up": {}, "u_down": {},
503
  "status": "OPEN",
504
  "start_time": str(datetime.datetime.now())
 
510
  """交易员消耗积分参与盘口对冲"""
511
  if bid not in bk_engine.active_bets:
512
  return await ctx.send("❌ Error: Invalid Bet ID.")
 
513
  b = bk_engine.active_bets[bid]
514
  if b["status"] != "OPEN":
515
  return await ctx.send("❌ Error: This market is closed.")
 
516
  u = bk_engine.get_user(ctx.author.id)
517
  if u["points"] < amt or amt <= 0:
518
  return await ctx.send("❌ Error: Insufficient asset points.")
 
519
  side = side.upper()
520
  if side not in ["UP", "DOWN"]:
521
  return await ctx.send("❌ Error: Choose UP or DOWN.")
 
 
522
  u["points"] -= amt
523
  uid = str(ctx.author.id)
 
524
  pool_key = "u_up" if side == "UP" else "u_down"
525
  b[side.lower()] += amt
526
  b[pool_key][uid] = b[pool_key].get(uid, 0) + amt
 
527
  bk_engine.save_db()
528
  await ctx.send(f"✅ Position Locked: {ctx.author.name} added {amt} to {side} pool.")
529
 
 
534
  if bid not in bk_engine.active_bets: return
535
  b = bk_engine.active_bets[bid]
536
  winner = winner.upper()
 
537
  win_pool = b["u_up"] if winner == "UP" else b["u_down"]
538
  total_win = b["up"] if winner == "UP" else b["down"]
539
  total_lose = b["down"] if winner == "UP" else b["up"]
 
540
  if total_win > 0:
541
  for uid, amt in win_pool.items():
 
542
  profit = (amt / total_win) * total_lose
543
  user = bk_engine.get_user(int(uid))
544
  user["points"] += round(amt + profit)
 
545
  b["status"] = "SETTLED"
546
  bk_engine.save_db()
547
  await ctx.send(f"🏁 Bet `{bid}` Settled! Winner: **{winner}**. Profits Distributed.")
548
 
549
  # ==================================================================
550
+ # 模块五:PVP 竞技场 (Arena PvP Engine) [增强版]
551
  # ==================================================================
552
  @new_bot.command()
553
+ async def challenge(ctx, member: discord.Member, days: int = 1):
554
+ """发起基于量化能力的 1对1 竞技(支持 1-30 天周期)"""
555
  if member.id == ctx.author.id:
556
  return await ctx.send("❌ Logical Error: 不能向自己发起决斗。")
557
+ if days < 1 or days > 30:
558
+ return await ctx.send("❌ 周期范围: 1-30 天。")
559
  did = f"DUEL-{random.randint(100, 999)}"
560
  bk_engine.pending_challenges[member.id] = {
561
  "challenger": ctx.author.id,
562
  "did": did,
563
+ "days": days,
564
  "expiry": datetime.datetime.now() + datetime.timedelta(hours=1)
565
  }
566
+ await ctx.send(
567
+ f"⚔️ **{ctx.author.name}** 发起决斗请求!目标:{member.mention}\n"
568
+ f"ID: `{did}` | 周期: **{days} 天** | `!accept` 接受。"
569
+ )
570
 
571
  @new_bot.command()
572
  async def accept(ctx):
573
  """接受竞技场挑战"""
574
  if ctx.author.id not in bk_engine.pending_challenges:
575
  return await ctx.send("❌ Error: No pending challenges.")
 
576
  req = bk_engine.pending_challenges.pop(ctx.author.id)
577
  did = req["did"]
578
+ days = req.get("days", 1)
579
  bk_engine.active_duels[did] = {
580
  "p1": req["challenger"], "p2": ctx.author.id,
581
  "s1": None, "s2": None,
582
+ "days": days,
583
  "start": str(datetime.datetime.now())
584
  }
585
+ await ctx.send(
586
+ f"🔥 **ARENA LOCKED!** Duel ID: `{did}` | 周期: **{days} 天**\n"
587
+ f"双方请通过 `!duel_submit {did}` 上传审计截图。"
588
+ )
589
 
590
  @new_bot.command()
591
  async def duel_submit(ctx, did: str):
592
+ """在对决中提交截图,执行最终胜负判定(含 AI 解说 + 自动晋升)"""
593
  if did not in bk_engine.active_duels:
594
  return await ctx.send("❌ Error: Duel not active.")
 
595
  if not ctx.message.attachments:
596
  return await ctx.send("❌ Refused: 请上传结算截图。")
 
597
  duel = bk_engine.active_duels[did]
598
+ if ctx.author.id not in [duel["p1"], duel["p2"]]:
599
+ return
600
  m = await ctx.send(f"🔍 **[Arena] Auditing Submission...**")
601
+ prompt = (
602
+ "Arena Duel Audit: 请识别此交易截图的综合能力评���。\n"
603
+ "FORMAT: SCORE: [0-100的数值]"
604
+ )
605
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
606
  score = safe_extract_float(res, "SCORE")
607
+ if ctx.author.id == duel["p1"]:
608
+ duel["s1"] = score
609
+ else:
610
+ duel["s2"] = score
611
 
612
+ # 双方都交了 执行最终结算
 
 
 
613
  if duel["s1"] is not None and duel["s2"] is not None:
614
+ w_score = max(duel["s1"], duel["s2"])
615
+ l_score = min(duel["s1"], duel["s2"])
616
+ wid = duel["p1"] if duel["s1"] > duel["s2"] else duel["p2"]
617
+ lid = duel["p2"] if wid == duel["p1"] else duel["p1"]
618
  new_w, new_l, strk = bk_engine.update_elo(wid, lid)
619
 
620
+ # ========== Arena Shoutcaster AI 解说 ==========
621
+ shout_prompt = (
622
+ f"你是一名热血电竞风格的交易竞技解说员(Arena Shoutcaster)。\n"
623
+ f"两位交易员刚完成了一场量化对决:\n"
624
+ f"🥇 胜者得分: {w_score:.1f} | 🥈 败者得分: {l_score:.1f}\n"
625
+ f"请用激昂的中文解说风格,输出 80 字以内的赛事评论。\n"
626
+ f"分析胜者核心优势、败者改进方向,像体育赛事解说一样精彩。\n"
627
+ f"FORMAT: COMMENTARY: [解说内容]"
628
+ )
629
+ shout_res = await call_gemini_multiplex(shout_prompt)
630
+ commentary = safe_extract_text(shout_res, "COMMENTARY")
631
+ if commentary == "N/A":
632
+ commentary = (
633
+ shout_res[:200]
634
+ if shout_res and "ERROR" not in shout_res
635
+ else "精彩对决!胜者展现了更出色的风控纪律与策略执行力。"
636
+ )
637
+
638
  emb = discord.Embed(title="🏆 Arena Final Verdict", color=0xf1c40f)
639
+ emb.add_field(
640
+ name="🥇 Winner",
641
+ value=f"<@{wid}>\nScore: {w_score:.1f}\nNew ELO: {new_w}",
642
+ inline=True
643
+ )
644
+ emb.add_field(
645
+ name="🥈 Defeated",
646
+ value=f"<@{lid}>\nScore: {l_score:.1f}\nNew ELO: {new_l}",
647
+ inline=True
648
+ )
649
+ emb.add_field(
650
+ name="🎙️ Arena Shoutcaster",
651
+ value=f"```\n{commentary}\n```",
652
+ inline=False
653
+ )
654
  emb.set_footer(text=f"Streak: {strk} | Duel ID: {did}")
 
655
  await ctx.send(embed=emb)
656
+
657
+ # ========== 3 连胜自动晋升 Arena Overlord ==========
658
+ if strk >= 3:
659
+ try:
660
+ guild = ctx.guild
661
+ role = discord.utils.get(guild.roles, name="Arena Overlord")
662
+ if not role:
663
+ role = await guild.create_role(
664
+ name="Arena Overlord",
665
+ color=discord.Color.gold(),
666
+ hoist=True,
667
+ reason="BK-GTA 3-Streak Auto Promotion"
668
+ )
669
+ winner_member = guild.get_member(wid)
670
+ if winner_member and role not in winner_member.roles:
671
+ await winner_member.add_roles(role)
672
+ await ctx.send(
673
+ f"🎖️ **PROMOTION!** <@{wid}> 达成 **{strk} 连胜**!\n"
674
+ f"自动晋升为 **Arena Overlord** 👑"
675
+ )
676
+ else:
677
+ await ctx.send(
678
+ f"🔥 <@{wid}> 已达 **{strk} 连胜**!Overlord 霸主地位稳固!"
679
+ )
680
+ except Exception as e:
681
+ logger.warning(f"Auto-promotion failed: {e}")
682
+ await ctx.send(
683
+ f"🎖️ <@{wid}> 达成 **{strk} 连胜**!"
684
+ f"(身份组授予需要 Bot 管理角色权限)"
685
+ )
686
+
687
  bk_engine.active_duels.pop(did)
688
  else:
689
  await ctx.send(f"✅ Submission Received (Score: {score:.1f}). Waiting for Opponent...")
 
690
  await m.delete()
691
 
692
  # ==================================================================
 
697
  """提取审计数据并渲染具备社交传播属性的海报图片"""
698
  if not ctx.message.attachments:
699
  return await ctx.send("❌ 请上传审计截图��")
 
700
  m = await ctx.send("🎨 **[GPU-Node] Rendering Institutional Poster...**")
 
701
  prompt = "Extract RET: [val] and SHARPE: [val]. FORMAT: RET: [v] SHARPE: [v]"
702
  res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
703
  ret, sharpe = safe_extract_float(res, "RET"), safe_extract_float(res, "SHARPE")
 
 
704
  try:
705
  W, H = 600, 800
 
706
  base = Image.new('RGB', (W, H), color=(18, 20, 30))
707
  draw = ImageDraw.Draw(base)
 
 
708
  for i in range(0, W, 40): draw.line([(i, 0), (i, H)], fill=(30, 35, 45), width=1)
709
  for j in range(0, H, 40): draw.line([(0, j), (W, j)], fill=(30, 35, 45), width=1)
 
 
710
  draw.rectangle([10, 10, 590, 790], outline=(46, 204, 113), width=2)
711
  draw.rectangle([20, 20, 580, 780], outline=(46, 204, 113), width=5)
 
 
712
  draw.text((40, 60), "BK-GTA QUANTITATIVE SYSTEM", fill=(255, 255, 255))
713
  draw.text((40, 150), f"TRADER ID: {ctx.author.name.upper()}", fill=(46, 204, 113))
714
  draw.text((40, 260), f"ALPHA RETURN: +{ret}%", fill=(255, 255, 255))
715
  draw.text((40, 360), f"SHARPE RATIO: {sharpe}", fill=(255, 255, 255))
716
  draw.text((40, 460), f"SESSION: {SESSION_ID}", fill=(120, 120, 130))
 
 
717
  draw.text((40, 720), "VERIFIED BY BK-GTA SENTINEL AI", fill=(46, 204, 113))
 
 
718
  with io.BytesIO() as image_binary:
719
  base.save(image_binary, 'PNG')
720
  image_binary.seek(0)
721
  await ctx.send(file=discord.File(fp=image_binary, filename=f"Viral_{ctx.author.id}.png"))
 
722
  except Exception as e:
723
  logger.error(f"Render Error: {e}")
724
  await ctx.send(f"❌ 渲染引擎报错: `{e}`")
725
  finally:
726
  await m.delete()
727
 
728
+ # ==================================================================
729
+ # 模块七:每日门诊 (Daily Clinic / Sync Log) [新增]
730
+ # ==================================================================
731
+ @new_bot.command()
732
+ async def sync_log(ctx):
733
+ """每日门诊:AI 深度心理建模,诊断报复性交易与情绪倾斜 (Tilt)"""
734
+ if not ctx.message.attachments:
735
+ return await ctx.send("❌ 请上传今日交易存根/截图进行心理诊断。")
736
+ m = await ctx.send("🏥 **[Clinic] 正在执行深度心理建模...**")
737
+ prompt = (
738
+ "你是一名顶级交易心理学专家与行为金融分析师。请分析此交易记录截图,执行以下诊断:\n"
739
+ "1. TILT_SCORE: 情绪倾斜指数 (0-10, 0=完全理性, 10=严重失控)\n"
740
+ "2. REVENGE_RISK: 报复性交易风险等级 (LOW/MEDIUM/HIGH/CRITICAL)\n"
741
+ "3. PATTERN: 识别的行为模式 (如: 恐惧离场, 过度交易, 追涨杀跌, 锚定偏差等)\n"
742
+ "4. DIAGNOSIS: 中文诊断报告 (100字内, 包含具体问题分析)\n"
743
+ "5. PRESCRIPTION: 中文处方建议 (50字内的具体行动指令)\n"
744
+ "输出格式必须严格如下:\n"
745
+ "TILT_SCORE: [值]\n"
746
+ "REVENGE_RISK: [等级]\n"
747
+ "PATTERN: [模式]\n"
748
+ "DIAGNOSIS: [诊断]\n"
749
+ "PRESCRIPTION: [处方]"
750
+ )
751
+ res = await call_gemini_multiplex(prompt, ctx.message.attachments[0])
752
+ if "ERROR" in res:
753
+ await m.delete()
754
+ return await ctx.send("🔴 [Clinic Offline] 诊断节点断连。")
755
+ tilt = safe_extract_float(res, "TILT_SCORE")
756
+ revenge = safe_extract_text(res, "REVENGE_RISK", "PATTERN")
757
+ pattern = safe_extract_text(res, "PATTERN", "DIAGNOSIS")
758
+ diagnosis = safe_extract_text(res, "DIAGNOSIS", "PRESCRIPTION")
759
+ prescription = safe_extract_text(res, "PRESCRIPTION")
760
+ if tilt <= 3:
761
+ color, status = 0x2ecc71, "🟢 心理状态健康"
762
+ elif tilt <= 6:
763
+ color, status = 0xf39c12, "🟡 轻度情绪偏移"
764
+ elif tilt <= 8:
765
+ color, status = 0xe67e22, "🟠 中度情绪倾斜"
766
+ else:
767
+ color, status = 0xe74c3c, "🔴 严重情绪失控"
768
+ emb = discord.Embed(title="🏥 BK-GTA Daily Clinic | 每日门诊", color=color)
769
+ emb.add_field(name="心理状态", value=f"**{status}**", inline=False)
770
+ emb.add_field(name="Tilt Index", value=f"**{tilt:.1f}** / 10", inline=True)
771
+ emb.add_field(name="报复性交易风险", value=f"**{revenge}**", inline=True)
772
+ emb.add_field(name="行为模式识别", value=f"`{pattern}`", inline=False)
773
+ emb.add_field(name="📋 诊断报告", value=f"```\n{diagnosis}\n```", inline=False)
774
+ emb.add_field(name="💊 处方", value=f"```\n{prescription}\n```", inline=False)
775
+ u = bk_engine.get_user(ctx.author.id)
776
+ u["points"] += 30
777
+ bk_engine.save_db()
778
+ emb.set_footer(text=f"Assets +30 | Session: {SESSION_ID}")
779
+ await m.delete()
780
+ await ctx.send(embed=emb)
781
+
782
+ # ==================================================================
783
+ # 模块八:阵营系统 (Faction System) [新增]
784
+ # ==================================================================
785
+ @new_bot.command()
786
+ async def factions(ctx):
787
+ """查看所有阵营信息与实时对抗比分"""
788
+ emb = discord.Embed(title="⚔️ Faction War | 阵营对抗", color=0xf1c40f)
789
+ for name, info in bk_engine.data["factions"].items():
790
+ member_count = sum(
791
+ 1 for u in bk_engine.data["users"].values()
792
+ if u.get("faction") == name
793
+ )
794
+ emb.add_field(
795
+ name=f"{info['emoji']} {name}",
796
+ value=(
797
+ f"Honor Points: **{info['score']}**\n"
798
+ f"Total Wins: **{info['total_wins']}**\n"
799
+ f"Members: **{member_count}**"
800
+ ),
801
+ inline=True
802
+ )
803
+ emb.add_field(
804
+ name="📜 加入方式",
805
+ value="`!join BULLS` 或 `!join BEARS`\n⚠️ 绑定后不可更改",
806
+ inline=False
807
+ )
808
+ emb.set_footer(text=f"Session: {SESSION_ID} | V62.9")
809
+ await ctx.send(embed=emb)
810
+
811
+ @new_bot.command()
812
+ async def join(ctx, faction_name: str):
813
+ """加入阵营,绑定后不可更改"""
814
+ faction_name = faction_name.upper()
815
+ if faction_name not in bk_engine.data["factions"]:
816
+ available = ", ".join(bk_engine.data["factions"].keys())
817
+ return await ctx.send(f"❌ 无效阵营。可选: `{available}`")
818
+ u = bk_engine.get_user(ctx.author.id)
819
+ if u["faction"] is not None:
820
+ emoji = bk_engine.data["factions"][u["faction"]]["emoji"]
821
+ return await ctx.send(f"❌ 你已绑定阵营 {emoji} **{u['faction']}**,不可更改。")
822
+ u["faction"] = faction_name
823
+ bk_engine.save_db()
824
+ f_info = bk_engine.data["factions"][faction_name]
825
+ await ctx.send(
826
+ f"✅ {f_info['emoji']} **{ctx.author.name}** 已加入 **{faction_name}** 阵营!为荣誉而战!"
827
+ )
828
+
829
+ # ==================================================================
830
+ # 模块九:知识库治理 (Knowledge Archive) [新增]
831
+ # ==================================================================
832
+ @new_bot.command()
833
+ @commands.has_permissions(administrator=True)
834
+ async def archive(ctx, msg_id: int):
835
+ """将优质案例入库至 #📚-case-study 频道永久保存"""
836
+ try:
837
+ source_msg = await ctx.channel.fetch_message(msg_id)
838
+ except discord.NotFound:
839
+ return await ctx.send("❌ 消息未找到,请确认 Message ID 正确。")
840
+ except Exception as e:
841
+ return await ctx.send(f"❌ 获取消息失败: `{e}`")
842
+
843
+ archive_channel = discord.utils.get(ctx.guild.text_channels, name="📚-case-study")
844
+ if not archive_channel:
845
+ archive_channel = discord.utils.get(ctx.guild.text_channels, name="case-study")
846
+ if not archive_channel:
847
+ return await ctx.send("❌ 未找到 `#📚-case-study` 或 `#case-study` 频道,请先创建。")
848
+
849
+ emb = discord.Embed(
850
+ title="📚 Archived Case Study",
851
+ description=source_msg.content[:4096] if source_msg.content else "(Embed/Attachment)",
852
+ color=0x9b59b6,
853
+ timestamp=source_msg.created_at
854
+ )
855
+ emb.add_field(name="Author", value=source_msg.author.mention, inline=True)
856
+ emb.add_field(name="Source", value=ctx.channel.mention, inline=True)
857
+ emb.add_field(name="Archived By", value=ctx.author.mention, inline=True)
858
+ if source_msg.embeds:
859
+ orig = source_msg.embeds[0]
860
+ if orig.description:
861
+ emb.add_field(name="Original Content", value=orig.description[:1024], inline=False)
862
+
863
+ files = []
864
+ for att in source_msg.attachments:
865
+ try:
866
+ file_data = await att.read()
867
+ files.append(discord.File(io.BytesIO(file_data), filename=att.filename))
868
+ except Exception:
869
+ pass
870
+
871
+ await archive_channel.send(embed=emb, files=files if files else None)
872
+ await ctx.send(f"✅ 案例已归档至 {archive_channel.mention}")
873
+
874
+ # ==================================================================
875
+ # 模块十:多语言镜像翻译 (Global Translate Engine) [新增]
876
+ # ==================================================================
877
+ @new_bot.command()
878
+ @commands.has_permissions(administrator=True)
879
+ async def global_translate(ctx, msg_id: int, lang: str = "EN"):
880
+ """生成指定语言(EN/JP/RU/KR/ES/FR/DE)的镜像报告"""
881
+ try:
882
+ source_msg = await ctx.channel.fetch_message(msg_id)
883
+ except discord.NotFound:
884
+ return await ctx.send("❌ 消息未找到。")
885
+ except Exception as e:
886
+ return await ctx.send(f"❌ 获取消息失败: `{e}`")
887
+
888
+ lang = lang.upper()
889
+ lang_map = {
890
+ "EN": "English", "JP": "Japanese", "RU": "Russian",
891
+ "KR": "Korean", "ES": "Spanish", "FR": "French",
892
+ "DE": "German", "AR": "Arabic", "PT": "Portuguese"
893
+ }
894
+ target_lang = lang_map.get(lang, lang)
895
+
896
+ content = source_msg.content or ""
897
+ if source_msg.embeds:
898
+ for emb in source_msg.embeds:
899
+ if emb.title: content += f"\n{emb.title}"
900
+ if emb.description: content += f"\n{emb.description}"
901
+ for field in emb.fields:
902
+ content += f"\n{field.name}: {field.value}"
903
+ if not content.strip():
904
+ return await ctx.send("❌ 消息内容为空,无法翻译。")
905
+
906
+ m = await ctx.send(f"🌐 **[Translator] Generating {target_lang} mirror...**")
907
+ prompt = (
908
+ f"你是专业金融翻译官。请将以下内容精准翻译为 {target_lang}。\n"
909
+ f"要求:保持金融术语专业性,保留所有数字和格式结构。\n"
910
+ f"原文:\n{content}"
911
+ )
912
+ res = await call_gemini_multiplex(prompt)
913
+ if "ERROR" in res:
914
+ await m.delete()
915
+ return await ctx.send("🔴 翻译引擎离线。")
916
+ emb = discord.Embed(
917
+ title=f"🌐 Mirror Report [{lang}] | {target_lang}",
918
+ description=res[:4096],
919
+ color=0x3498db
920
+ )
921
+ emb.set_footer(text=f"ZH → {target_lang} | Session: {SESSION_ID}")
922
+ await m.delete()
923
+ await ctx.send(embed=emb)
924
+
925
  # ==================================================================
926
  # 系统对账与自动化任务 (System Reliability)
927
  # ==================================================================
928
  @tasks.loop(hours=1)
929
  async def auto_backup():
 
930
  bk_engine.save_db()
931
  logger.info("💾 [Checkpoint] Data persistence executed.")
932
 
933
  @new_bot.event
934
  async def on_ready():
 
935
  if not auto_backup.is_running(): auto_backup.start()
 
 
936
  activity = discord.Activity(type=discord.ActivityType.watching, name="HFT Markets")
937
  await new_bot.change_presence(status=discord.Status.online, activity=activity)
 
938
  logger.info(f"--------------------------------------------------")
939
  logger.info(f"✅ [V62.9 Ultimate] BK-GTA Architecture Online")
940
+ logger.info(f"🚀 Session: {SESSION_ID} | Commands: {len(new_bot.commands)}")
941
  logger.info(f"--------------------------------------------------")
942
 
943
  @new_bot.event
 
950
 
951
  @new_bot.event
952
  async def on_command_error(ctx, error):
 
953
  if isinstance(error, commands.CommandNotFound): return
954
  if isinstance(error, commands.MissingPermissions):
955
  return await ctx.send("❌ 权限审计未通过:仅限合伙人执行。")
 
 
956
  exc = "".join(traceback.format_exception(type(error), error, error.__traceback__))
957
  logger.error(f"🔥 [System Crash] {exc}")
958
  await ctx.send(f"⚠️ [Logic Breach] 内部逻辑断裂: `{error}`")
 
963
  # ==============================================================================
964
  # 6. 物理点火启动链 (Physical Start Engine)
965
  # ==============================================================================
 
966
  bot = create_bot_with_commands()
967
 
968
  if __name__ == "__main__":
 
974
  logger.info(f" Session: {SESSION_ID}")
975
  logger.info("=" * 55)
976
 
 
977
  keep_alive()
978
 
 
979
  logger.info("⏳ [Boot] Waiting 10s for container network to initialize...")
980
  time.sleep(10)
981
 
 
982
  if not DISCORD_TOKEN:
983
  logger.critical("❌ CRITICAL: DISCORD_TOKEN is missing in environment.")
 
984
  while True:
985
  time.sleep(3600)
986
  else:
 
991
  wait = min(30 * attempt, 120)
992
  logger.info(f"⏳ [Boot] Retry in {wait}s (attempt {attempt}/{max_retries})...")
993
  time.sleep(wait)
 
994
  bot = create_bot_with_commands()
 
995
  logger.info(f"🚀 [Boot] Starting Discord connection (attempt {attempt}/{max_retries})...")
996
  bot.run(DISCORD_TOKEN)
997
+ break
 
998
  except discord.LoginFailure:
999
  logger.critical("❌ [FATAL] Invalid DISCORD_TOKEN! Check token in Secrets.")
1000
+ break
 
1001
  except discord.PrivilegedIntentsRequired:
1002
  logger.critical(
1003
  "❌ [FATAL] Privileged Intents not enabled!\n"
1004
  "→ https://discord.com/developers/applications → Bot tab\n"
1005
  "→ Enable 'SERVER MEMBERS INTENT' and 'MESSAGE CONTENT INTENT'"
1006
  )
1007
+ break
 
1008
  except (OSError, ConnectionError) as e:
1009
  logger.error(f"🌐 [Boot] Network error (attempt {attempt}): {e}")
 
 
1010
  except Exception as e:
1011
  logger.critical(f"🔥 ENGINE COLLAPSE (attempt {attempt}): {e}")
1012
  traceback.print_exc()
1013
 
 
1014
  logger.warning("🔴 [Boot] Bot stopped. Keeping process alive for Gradio...")
1015
  while True:
1016
  time.sleep(3600)
1017
 
1018
  # ==============================================================================
1019
+ # END OF ARCHITECT SOURCE CODE | BK-GTA V62.9 — HuggingFace Edition (COMPLETE)
1020
  # ==============================================================================