This commit is contained in:
丹尼尔
2026-03-12 18:42:23 +08:00
parent 66362780a0
commit 8b62c445fc
10 changed files with 4801 additions and 73 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -41,10 +41,11 @@ SLIDER_VERIFY_BASE_URL = os.getenv("SLIDER_VERIFY_BASE_URL", "http://113.44.162.
SLIDER_VERIFY_KEY = os.getenv("SLIDER_VERIFY_KEY", "408449830")
# 发送文本消息swagger 中为 POST /message/SendTextMessagebody 为 SendMessageModelMsgItem 数组)
SEND_MSG_PATH = (os.getenv("SEND_MSG_PATH") or "/message/SendTextMessage").strip()
# 发送图片消息:部分上游为独立接口,或与文本同 path 仅 MsgType 不同(如 3=图片)
SEND_IMAGE_PATH = (os.getenv("SEND_IMAGE_PATH") or "").strip() or SEND_MSG_PATH
# 图片消息 MsgType部分上游为 0常见为 3
IMAGE_MSG_TYPE = int(os.getenv("IMAGE_MSG_TYPE", "3"))
# 发送图片消息:7006 为 /message/SendImageMessagebody MsgItem 含 ImageContent、MsgType=0
SEND_IMAGE_UPSTREAM_BASE_URL = (os.getenv("SEND_IMAGE_UPSTREAM_BASE_URL") or "").strip() or CHECK_STATUS_BASE_URL
SEND_IMAGE_PATH = (os.getenv("SEND_IMAGE_PATH") or "").strip() or "/message/SendImageMessage"
# 图片消息 MsgType7006 SendImageMessage 为 0
IMAGE_MSG_TYPE = int(os.getenv("IMAGE_MSG_TYPE", "0"))
# 按 key 缓存取码结果与 Data62供后续步骤使用
qrcode_store: dict = {}
@@ -91,6 +92,17 @@ def _allowed_ai_reply(key: str, from_user: str) -> bool:
return allowed
def _is_super_admin(key: str, from_user: str) -> bool:
"""仅超级管理员可调用代发消息等 function call白名单仅可得到普通回复。"""
if not from_user or not from_user.strip():
return False
cfg = store.get_ai_reply_config(key)
if not cfg:
return False
super_admins = set(cfg.get("super_admin_wxids") or [])
return from_user.strip() in super_admins
async def _ai_takeover_reply(key: str, from_user: str, content: str) -> None:
"""收到他人消息时由 AI 接管:根据指令生成回复或调用内置动作(如代发消息)。"""
if not from_user or not content or not content.strip():
@@ -151,6 +163,13 @@ async def _ai_takeover_reply(key: str, from_user: str, content: str) -> None:
action_type = str(action.get("type") or "").strip()
if action_type == "send_message":
if not _is_super_admin(key, from_user):
await _send_message_upstream(
key, from_user,
"代发消息等操作仅限超级管理员使用,您可正常聊天获得回复。"
)
logger.info("AI send_message rejected: from_user not super_admin (key=***%s)", key[-4:] if len(key) >= 4 else "****")
return
target = str(action.get("target_wxid") or "").strip()
msg = str(action.get("content") or "").strip()
if target and msg:
@@ -1246,7 +1265,20 @@ async def api_create_push_task(body: PushTaskCreate):
@app.get("/api/messages")
async def api_list_messages(key: str = Query(..., description="账号 key"), limit: int = Query(100, le=500)):
return {"items": store.list_sync_messages(key, limit=limit)}
"""拉取同步消息列表并依联系人GetContactDetailsList的昵称将 wxid 换算为展示名FromDisplayName/ToDisplayName不使用客户档案备注。"""
items = store.list_sync_messages(key, limit=limit)
try:
contact_index = await _build_contact_index(key)
except Exception:
contact_index = {}
for m in items:
from_wxid = (m.get("FromUserName") or m.get("from") or "").strip()
to_wxid = (m.get("ToUserName") or m.get("to") or "").strip()
from_info = contact_index.get(from_wxid) if isinstance(contact_index.get(from_wxid), dict) else None
to_info = contact_index.get(to_wxid) if isinstance(contact_index.get(to_wxid), dict) else None
m["FromDisplayName"] = (from_info.get("nick_name") or from_wxid).strip() if from_info else from_wxid
m["ToDisplayName"] = (to_info.get("nick_name") or to_wxid).strip() if to_info else to_wxid
return {"items": items}
@app.get("/api/callback-status")
@@ -1359,14 +1391,14 @@ async def _send_batch_upstream(key: str, items: List[dict]) -> dict:
async def _send_image_upstream(key: str, to_user_name: str, image_content: str,
text_content: Optional[str] = "",
at_wxid_list: Optional[List[str]] = None) -> dict:
"""发送图片消息MsgItem 含 ImageContent、MsgType=3或 0依上游可选 TextContent、AtWxIDList。"""
url = f"{WECHAT_UPSTREAM_BASE_URL.rstrip('/')}{SEND_IMAGE_PATH}"
"""发送图片消息:走 7006 /message/SendImageMessageMsgItem 含 ImageContent、MsgType=0、TextContent、AtWxIDList。"""
url = f"{SEND_IMAGE_UPSTREAM_BASE_URL.rstrip('/')}{SEND_IMAGE_PATH}"
item = {
"ToUserName": to_user_name,
"MsgType": IMAGE_MSG_TYPE,
"AtWxIDList": list(at_wxid_list) if at_wxid_list else [],
"ImageContent": image_content or "",
"MsgType": IMAGE_MSG_TYPE,
"TextContent": text_content or "",
"AtWxIDList": at_wxid_list or [],
"ToUserName": to_user_name,
}
payload = {"MsgItem": [item]}
async with httpx.AsyncClient(trust_env=False, timeout=15.0) as client: