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

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: