Your wechat account may be LIMITED to log in WEB wechat, error info: <error><ret>1203</ret><message...

本文回顾了微信网页版API的封锁历史,自2017年起,腾讯开始限制新注册微信号登录网页版微信,直至2019年全面屏蔽所有账号登录。此举迫使营销者转向手机端API或Hook桌面PC端API。

转载:https://zhuanlan.zhihu.com/p/76180564

微信网页版限制登录或禁止登录将影响一大批使用itchat等Web Api方案的微信机器人

网页版微信 API 被封了,像使用 itchat wxpy wxbot等基于 web API 的微信 robot 方案都失效了

 

一、第一次开始限制

在2017年9月份开始,腾讯已经 开始限制 新注册的微信号禁止登录网页版微信,老的微信号则不受影响

可以自行验证,用新注册的微信号登录微信网页版,会提示:

 

<error><ret>1203</ret><message>为了你的帐号安全,新注册的微信号不能登录网页微信。你可以使用 Windows 微信或 Mac 微信在电脑端登录。Windows 微信下载地址:  Mac 微信下载地址: </message></error> 老账号不受影响,但是肯定会渐渐推进,封掉 web API 这条路。 

二、第二次完全屏蔽与限制
而从2019年7月份开始,腾讯彻底关闭了网页版微信登录入口,在 或  登录网页版的微信都会提示,不管是新注册的微信号,还是老的微信号都是如此

虽然网站还可打开和扫描,但基本上无法登录了,所以估计应该是彻底关闭网页登录接口了。

 

<error><ret>1203</ret><message>为了你的帐号安全,此微信号不能登录网页微信。你可以使用Windows微信或Mac微信在电脑端登录。Windows微信下载地址:https://pc.weixin.qq.com  Mac微信下载地址:https://mac.weixin.qq.com</message></error>

 

之后做营销的会转向靠逆向或者抓包 手机端 API 或者Hook 桌面 PC端 微信 API

转载于:https://www.cnblogs.com/fby698/p/11515470.html

import json import logging import asyncio from typing import Optional, Dict, Any from fastapi import FastAPI, Request, Response, HTTPException from pydantic import BaseModel, Field import httpx from pydantic_settings import BaseSettings from openai import OpenAI import re import xml.etree.ElementTree as ET # ==================== 配置类 ==================== class Settings(BaseSettings): version: str = "1.0" app_name: str = "拖车调度客服系统" host: str = "0.0.0.0" port: int = 8081 wechat_appid: str = "wx_V4GKlfb0V4NLpW5HHKR5U" wechat_device_id: str = "wx_wR_U4zPj2M_OTS3BCyoE4" target_group: str = "52692331298@chatroom" vllm_api_url: str = "http://localhost:8000/v1" vllm_model: str = "Qwen" ai_timeout: int = 30 forward_api_url: str = "http://api.geweapi.com/gewe/v2/api/message/postText" forward_api_token: str = "019abe72-4122-48e3-9a7c-ba47a560da5d" forward_timeout: int = 10 class Config: env_file = ".env" settings = Settings() # ==================== 数据模型 ==================== class WeChatMessage(BaseModel): TypeName: str Appid: str Wxid: str Data: Dict[str, Any] class ApifoxModel(BaseModel): token: str = Field(..., alias="X-GEWE-TOKEN", serialization_alias="X-GEWE-TOKEN") app_id: str = Field(..., alias="appId") ats: Optional[str] = Field(None) content: str = Field(...) to_wxid: str = Field(..., alias="toWxid") class Config: populate_by_name = True json_encoders = {str: lambda v: v.replace("\u2005", " ") if v else v} # ==================== FastAPI应用 ==================== app = FastAPI( title=settings.app_name, description="智能拖车调度服务接口", version=settings.version ) # ==================== 日志配置 ==================== logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler(), logging.FileHandler('wechat_service.log')] ) logger = logging.getLogger("wechat.service") # ==================== 服务类 ==================== class WeChatService: @staticmethod def parse_message(raw_data: bytes) -> WeChatMessage: try: data = json.loads(raw_data.decode('utf-8')) return WeChatMessage(**data) except Exception as e: logger.error(f"消息解析失败: {str(e)}") raise HTTPException(400, detail="Invalid message format") @staticmethod def extract_content(msg: WeChatMessage) -> Dict[str, Any]: content = msg.Data["Content"]["string"] speaker_id, _, actual_content = content.partition(":") # 新增:解析引用消息 quoted_msg = None if msg.Data.get("MsgType") == 49: # 引用消息类型 quoted_msg = WeChatService.parse_quoted_message(msg.Data) return { "is_group": "@chatroom" in msg.Data["FromUserName"]["string"], "group_id": msg.Data["FromUserName"]["string"].strip(), "speaker_id": speaker_id.strip(), "speaker_nickname": msg.Data.get("PushContent", "").split(":")[0].strip(), "content": actual_content.strip(), "msg_type": msg.Data["MsgType"], "msg_id": msg.Data.get("MsgId", ""), "original_msg": msg.Data, "quoted_msg": quoted_msg # 新增:引用消息内容 } @staticmethod def parse_quoted_message(msg_data: Dict) -> Optional[Dict]: """解析引用消息内容""" try: content = msg_data.get("Content", {}).get("string", "") xml_match = re.search(r'<msg>.*?</msg>', content, re.DOTALL) if not xml_match: return None xml_content = xml_match.group(0) root = ET.fromstring(xml_content) refer_msg = root.find('.//refermsg') if refer_msg is None: return None return { "msg_id": refer_msg.find('svrid').text if refer_msg.find('svrid') is not None else "", "sender": refer_msg.find('fromusr').text if refer_msg.find('fromusr') is not None else "", "content": refer_msg.find('content').text if refer_msg.find('content') is not None else "", "display_name": refer_msg.find('displayname').text if refer_msg.find('displayname') is not None else "" } except Exception as e: logger.error(f"解析引用消息失败: {str(e)}") return None @staticmethod def parse_location_message(content: str) -> Optional[str]: try: xml_match = re.search(r'<msg>.*?</msg>', content, re.DOTALL) if not xml_match: return None xml_content = xml_match.group(0) root = ET.fromstring(xml_content) location = root.find('location') if location is None: return None label = location.get('label', '未知位置') x = location.get('x', '0.0') y = location.get('y', '0.0') return f"位置:{label},经纬度:{x},{y}" except Exception as e: logger.error(f"解析位置消息失败: {str(e)}") return None class AIService: @staticmethod async def generate_reply(prompt: str, quoted_content: Optional[str] = None) -> str: try: # 如果有引用消息,将其加入prompt if quoted_content: prompt = f"""你正在回复一条引用消息,请根据上下文进行回复。 被引用的原消息内容: "{quoted_content}" 当前需要回复的内容: "{prompt}" 请直接回复用户的问题,保持专业和礼貌:""" client = OpenAI(base_url=settings.vllm_api_url, api_key="EMPTY") response = client.completions.create( model=settings.vllm_model, prompt=prompt, max_tokens=100, # 增加token限制以便更好处理引用消息 temperature=0.7, top_p=0.9, stop=["\n\n", "。", "!", "?"], ) return response.choices[0].text.strip() except Exception as e: logger.error(f"AI生成失败: {str(e)}") return "收到您的请求,客服将尽快处理" class MessageSender: @staticmethod async def send(content: str, to_wxid: str, at_wxid: Optional[str] = None, original_msg: Optional[Dict] = None, quoted_msg: Optional[Dict] = None) -> bool: try: clean_content = content.split('\n')[0].strip() ats = at_wxid if at_wxid else None # 优先使用传入的引用消息,如果没有则使用original_msg中的引用 quoted_msg = quoted_msg or (original_msg.get("quoted_msg") if original_msg else None) if quoted_msg: # 构建引用回复消息 xml_template = """<?xml version='1.0'?> <msg> <appmsg appid="" sdkver="0"> <title>{title}</title> <des></des> <action>view</action> <type>57</type> <showtype>0</showtype> <content></content> <url></url> <lowurl></lowurl> <appattach> <totallen>0</totallen> <attachid></attachid> <fileext></fileext> </appattach> <extinfo></extinfo> <sourceusername></sourceusername> <sourcedisplayname></sourcedisplayname> <commenturl></commenturl> <thumburl></thumburl> <mediatagname></mediatagname> <messageaction><![CDATA[]]></messageaction> <messagemedia></messagemedia> <messageattr><![CDATA[]]></messageattr> <refermsg> <type>1</type> <svrid>{msg_id}</svrid> <fromusr>{from_user}</fromusr> <chatusr>{chat_user}</chatusr> <displayname>{display_name}</displayname> <content>{quoted_content}</content> <msgsource><![CDATA[]]></msgsource> </refermsg> <appname>{app_name}</appname> </appmsg> <fromusername>{bot_wxid}</fromusername> <scene>0</scene> <appinfo> <version>1</version> <appname></appname> </appinfo> <commenturl></commenturl> </msg>""" xml_content = xml_template.format( title=clean_content[:60], msg_id=quoted_msg.get("msg_id", ""), from_user=quoted_msg.get("sender", ""), chat_user=to_wxid, quoted_content=quoted_msg.get("content", ""), display_name=quoted_msg.get("display_name", "未知用户"), app_name=settings.app_name, bot_wxid=settings.wechat_device_id ) forward_data = { "appId": settings.wechat_appid, "toWxid": to_wxid, "xml": xml_content } async with httpx.AsyncClient(timeout=settings.forward_timeout) as client: response = await client.post( "http://api.geweapi.com/gewe/v2/api/message/forwardUrl", json=forward_data, headers={ "X-GEWE-TOKEN": settings.forward_api_token, "Content-Type": "application/json" } ) return response.json().get("ret", -1) == 0 elif original_msg: # 普通引用回复 msg_id = original_msg.get("MsgId", "") from_user = original_msg.get("FromUserName", {}).get("string", "") original_content = original_msg.get("Content", {}).get("string", "") if ':' in original_content: quoted_sender, quoted_content = original_content.split(':', 1) quoted_content = quoted_content.strip() else: quoted_sender = from_user quoted_content = original_content xml_template = """<?xml version='1.0'?> <msg> <appmsg appid="" sdkver="0"> <title>{title}</title> <des></des> <action>view</action> <type>57</type> <showtype>0</showtype> <content></content> <url></url> <lowurl></lowurl> <appattach> <totallen>0</totallen> <attachid></attachid> <fileext></fileext> </appattach> <extinfo></extinfo> <sourceusername></sourceusername> <sourcedisplayname></sourcedisplayname> <commenturl></commenturl> <thumburl></thumburl> <mediatagname></mediatagname> <messageaction><![CDATA[]]></messageaction> <messagemedia></messagemedia> <messageattr><![CDATA[]]></messageattr> <refermsg> <type>1</type> <svrid>{msg_id}</svrid> <fromusr>{from_user}</fromusr> <chatusr>{chat_user}</chatusr> <displayname>{display_name}</displayname> <content>{quoted_content}</content> <msgsource><![CDATA[]]></msgsource> </refermsg> <appname>{app_name}</appname> </appmsg> <fromusername>{bot_wxid}</fromusername> <scene>0</scene> <appinfo> <version>1</version> <appname></appname> </appinfo> <commenturl></commenturl> </msg>""" xml_content = xml_template.format( title=clean_content[:60], msg_id=msg_id, from_user=from_user, chat_user=to_wxid, quoted_content=quoted_content, display_name=original_msg.get("PushContent", "").split(":")[0].strip(), app_name=settings.app_name, bot_wxid=settings.wechat_device_id ) forward_data = { "appId": settings.wechat_appid, "toWxid": to_wxid, "xml": xml_content } async with httpx.AsyncClient(timeout=settings.forward_timeout) as client: response = await client.post( "http://api.geweapi.com/gewe/v2/api/message/forwardUrl", json=forward_data, headers={ "X-GEWE-TOKEN": settings.forward_api_token, "Content-Type": "application/json" } ) return response.json().get("ret", -1) == 0 else: # 普通消息发送 message = ApifoxModel( token=settings.forward_api_token, app_id=settings.wechat_appid, content=clean_content, to_wxid=to_wxid, ats=ats ) async with httpx.AsyncClient(timeout=settings.forward_timeout) as client: response = await client.post( settings.forward_api_url, json=message.model_dump(by_alias=True, exclude_none=True), headers={ "X-GEWE-TOKEN": settings.forward_api_token, "Content-Type": "application/json" } ) return response.json().get("ret", -1) == 0 except Exception as e: logger.error(f"发送失败: {str(e)}") return False # ==================== 路由处理 ==================== @app.post("/process") async def process_message(request: Request): try: raw_data = await request.body() logger.info(f"接收到的原始字节: {raw_data}") wechat_msg = WeChatService.parse_message(raw_data) msg_info = WeChatService.extract_content(wechat_msg) logger.info(f"接收到的消息内容: {msg_info}") # 支持处理文本消息(1)、位置消息(48)和引用消息(49) if not (msg_info["is_group"] and msg_info["group_id"] == settings.target_group and msg_info["msg_type"] in [1, 48, 49]): return Response(content="success") # 处理位置消息 if msg_info["msg_type"] == 48: location_label = WeChatService.parse_location_message(msg_info["content"]) if location_label: query_content = f"位置:{location_label}" logger.info(f"解析到的位置信息: {query_content}") else: return Response(content="success") else: query_content = msg_info["content"].strip() logger.info(f"提取到的文本内容: {query_content}") # 生成AI回复,如果有引用消息则带上引用内容 quoted_content = msg_info.get("quoted_msg", {}).get("content") if msg_info.get("quoted_msg") else None if query_content == "在吗": reply = "您好,请问有什么可以帮您?" elif "拖车" in query_content: reply = "好的,请您发一下电话和定位,我马上为您安排服务。" else: prompt = f"""你是一个专业的拖车调度客服AI助手,请按照以下原则与客户互动: 1. 当收到不明确信息时,礼貌询问具体要求 2. 对于非工作相关问题,礼貌回复需要查询后答复 3. 当用户明确需要服务时,立即询问定位和电话信息 4. 收到部分信息时,询问缺少的信息 5. 收到完整信息后,确认正在创建订单 当前问题:"{query_content}"。请直接回答用户问题。""" reply = await AIService.generate_reply(prompt, quoted_content) logger.info(f"生成的回复内容: {reply}") await MessageSender.send( content=reply, to_wxid=msg_info["group_id"], at_wxid=msg_info["speaker_id"], original_msg=msg_info["original_msg"], quoted_msg=msg_info.get("quoted_msg") ) return Response(content="success") except Exception as e: logger.error(f"处理异常: {str(e)}") return Response(content="success", status_code=500) # ==================== 启动服务 ==================== if __name__ == "__main__": import uvicorn uvicorn.run(app, host=settings.host, port=settings.port) 引用接收到的信息内容显示不存在 引用引用中的消息内容就显示存在 该如何解决给出完整代码
06-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值