├── .gitattributes ├── clients ├── rust │ ├── README.MD │ └── wcferry │ │ ├── lib │ │ └── README.MD │ │ ├── proto │ │ ├── README.MD │ │ └── roomdata.proto │ │ ├── src │ │ ├── main.rs │ │ └── proto │ │ │ └── roomdata.rs │ │ ├── build.rs │ │ └── Cargo.toml ├── java │ ├── wcferry │ │ ├── settings.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── libs │ │ │ └── nng-java-1.4.0-SNAPSHOT.jar │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── win32-x86-64 │ │ │ │ │ └── nng.dll │ │ │ │ └── logback.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── iamteer │ │ │ │ ├── SDK.java │ │ │ │ └── Main.java │ │ ├── build.gradle │ │ ├── README.MD │ │ └── gradlew.bat │ └── wechat-ferry-mvn │ │ ├── dll │ │ ├── .gitkeep │ │ ├── sdk.dll │ │ ├── spy.dll │ │ ├── spy_debug.dll │ │ └── readme.txt │ │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── proto │ │ │ │ └── .gitkeep │ │ │ ├── win32-x86-64 │ │ │ │ ├── .gitkeep │ │ │ │ └── nng.dll │ │ │ ├── libs │ │ │ │ └── nng-java-1.4.0-SNAPSHOT.jar │ │ │ ├── application.yml │ │ │ └── logback-spring.xml │ │ │ └── java │ │ │ └── com │ │ │ └── wechat │ │ │ └── ferry │ │ │ ├── task │ │ │ └── .gitkeep │ │ │ ├── aggregation │ │ │ ├── .gitkeep │ │ │ └── facade │ │ │ │ └── ChatRoomDo.java │ │ │ ├── constant │ │ │ └── .gitkeep │ │ │ ├── entity │ │ │ ├── dto │ │ │ │ ├── .gitkeep │ │ │ │ └── WxPpMsgDTO.java │ │ │ ├── po │ │ │ │ ├── .gitkeep │ │ │ │ └── wcf │ │ │ │ │ ├── ChatRoom.java │ │ │ │ │ ├── RevokeMsgStorage.java │ │ │ │ │ ├── ContactHeadImgUrl.java │ │ │ │ │ ├── ChatRoomInfo.java │ │ │ │ │ └── Contact.java │ │ │ ├── vo │ │ │ │ ├── request │ │ │ │ │ ├── .gitkeep │ │ │ │ │ ├── WxPpWcfRevokeMsgReq.java │ │ │ │ │ ├── WxPpWcfGroupMemberReq.java │ │ │ │ │ ├── WxPpWcfDatabaseTableReq.java │ │ │ │ │ ├── WxPpWcfDatabaseSqlReq.java │ │ │ │ │ ├── WxPpWcfDeleteGroupMemberReq.java │ │ │ │ │ ├── WxPpWcfInviteGroupMemberReq.java │ │ │ │ │ ├── WxPpWcfReceiveTransferReq.java │ │ │ │ │ ├── WxPpWcfAddFriendGroupMemberReq.java │ │ │ │ │ ├── WxPpWcfPatOnePatMsgReq.java │ │ │ │ │ ├── WxPpWcfSendFileMsgReq.java │ │ │ │ │ ├── WxPpWcfSendEmojiMsgReq.java │ │ │ │ │ ├── WxPpWcfSendImageMsgReq.java │ │ │ │ │ ├── WxPpWcfPassFriendApplyReq.java │ │ │ │ │ ├── WxPpWcfSendXmlMsgReq.java │ │ │ │ │ ├── WxPpWcfSendTextMsgReq.java │ │ │ │ │ └── WxPpWcfSendRichTextMsgReq.java │ │ │ │ └── response │ │ │ │ │ ├── .gitkeep │ │ │ │ │ ├── WxPpWcfDatabaseRowResp.java │ │ │ │ │ ├── WxPpWcfMsgTypeResp.java │ │ │ │ │ ├── WxPpWcfDatabaseTableResp.java │ │ │ │ │ ├── WxPpWcfSendFileMsgResp.java │ │ │ │ │ ├── WxPpWcfSendTextMsgResp.java │ │ │ │ │ ├── WxPpWcfSendXmlMsgResp.java │ │ │ │ │ ├── WxPpWcfSendEmojiMsgResp.java │ │ │ │ │ ├── WxPpWcfSendRichTextMsgResp.java │ │ │ │ │ ├── WxPpWcfSendPatOnePatMsgResp.java │ │ │ │ │ ├── WxPpWcfDatabaseFieldResp.java │ │ │ │ │ ├── WxPpWcfLoginInfoResp.java │ │ │ │ │ ├── WxPpWcfSendImageMsgResp.java │ │ │ │ │ ├── WxPpWcfGroupMemberResp.java │ │ │ │ │ └── WxPpWcfContactsResp.java │ │ │ ├── IResponse.java │ │ │ └── TResponse.java │ │ │ ├── service │ │ │ ├── impl │ │ │ │ ├── .gitkeep │ │ │ │ └── WeChatMsgServiceImpl.java │ │ │ ├── WeChatMsgService.java │ │ │ └── SDK.java │ │ │ ├── strategy │ │ │ ├── .gitkeep │ │ │ └── msg │ │ │ │ └── receive │ │ │ │ ├── ReceiveMsgStrategy.java │ │ │ │ ├── impl │ │ │ │ └── SignInMsgStrategyImpl.java │ │ │ │ └── ReceiveMsgFactory.java │ │ │ ├── WeChatFerryApplication.java │ │ │ ├── enums │ │ │ ├── WhetherEnum.java │ │ │ ├── WxPpMsgTypeEnum.java │ │ │ ├── MsgEventTypeEnum.java │ │ │ ├── DatabaseNameEnum.java │ │ │ ├── ReceiveMsgChannelEnum.java │ │ │ ├── TableNameEnum.java │ │ │ ├── SexEnum.java │ │ │ ├── ResponseCodeEnum.java │ │ │ ├── MsgCallbackTypeEnum.java │ │ │ ├── WxContactsTypeEnum.java │ │ │ ├── WxContactsMixedEnum.java │ │ │ ├── WxContactsOfficialEnum.java │ │ │ └── WcfMsgTypeEnum.java │ │ │ ├── config │ │ │ ├── ProtobufConfig.java │ │ │ ├── WebConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ └── WeChatFerryProperties.java │ │ │ ├── controller │ │ │ └── WeChatMsgController.java │ │ │ └── exception │ │ │ ├── BizException.java │ │ │ └── GlobalExceptionHandler.java │ │ └── .gitignore ├── python │ ├── MANIFEST.in │ ├── wcferry │ │ ├── __init__.py │ │ └── wxmsg.py │ ├── setup.py │ ├── README.MD │ └── test.py ├── gohttp │ ├── public │ │ ├── assets │ │ │ ├── style.css │ │ │ ├── icon.png │ │ │ └── logo.png │ │ ├── index.html │ │ └── swagger │ │ │ └── index.html │ ├── main.go │ ├── start-dev.bat │ ├── config.yml │ ├── httpd │ │ └── server.go │ ├── README.md │ └── go.mod ├── http │ └── README.MD ├── pyauto │ ├── wcfauto │ │ ├── auto_res │ │ │ └── __init__.py │ │ ├── __init__.py │ │ ├── event │ │ │ ├── __init__.py │ │ │ └── event.py │ │ └── msg_list.py │ ├── setup.py │ ├── demo.py │ └── README.MD ├── go │ ├── README.md │ ├── go.mod │ └── LICENSE └── go_wcf_http │ ├── README.MD │ ├── go.mod │ └── app │ └── robot.go ├── WeChatFerry ├── spy │ ├── spy.def │ ├── spy.aps │ ├── resource.h │ ├── framework.h │ ├── spy.h │ ├── Spy.vcxproj.user │ ├── user_info.h │ ├── rpc_server.h │ ├── receive_msg.h │ ├── chatroom_mgmt.h │ ├── contact_mgmt.h │ ├── exec_sql.h │ ├── dllmain.cpp │ ├── funcs.h │ ├── send_msg.h │ ├── spy_types.h │ ├── spy.cpp │ ├── user_info.cpp │ ├── spy.rc │ └── chatroom_mgmt.cpp ├── sdk │ ├── sdk.def │ ├── sdk.h │ ├── framework.h │ ├── SDK.vcxproj.user │ ├── dllmain.cpp │ ├── injector.h │ ├── SDK.vcxproj.filters │ └── sdk.cpp ├── smc │ ├── Codec.lib │ └── codec.h ├── .editorconfig ├── rpc │ ├── tool │ │ ├── proto │ │ │ ├── Makefile │ │ │ ├── _utils.py │ │ │ └── nanopb_pb2.py │ │ ├── protoc.bat │ │ ├── protoc-gen-nanopb │ │ ├── protoc-gen-nanopb.bat │ │ ├── nanopb_generator.py2 │ │ ├── protoc-gen-nanopb-py2 │ │ └── protoc │ ├── proto │ │ └── wcf.options │ ├── pb_util.h │ ├── pb_types.h │ └── nanopb │ │ └── pb_common.h ├── vcpkg.json ├── com │ ├── log.h │ ├── util.h │ └── log.cpp └── WeChatFerry.sln ├── assets ├── QR.jpeg └── TEQuant.jpg ├── docs ├── source │ ├── _static │ │ └── QRCodes.jpg │ ├── openapi.rst │ ├── readme_link.rst │ ├── index.rst │ └── conf.py ├── requirements.txt ├── Makefile └── make.bat ├── .gitmodules ├── .readthedocs.yml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── Build-WeChatFerry.yml ├── .gitignore └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | clients/** linguist-vendored 2 | -------------------------------------------------------------------------------- /clients/rust/README.MD: -------------------------------------------------------------------------------- 1 | 测试方法在 wechat.rs 中 2 | -------------------------------------------------------------------------------- /clients/rust/wcferry/lib/README.MD: -------------------------------------------------------------------------------- 1 | 将最新的 dll、exe 放入此文件夹下 2 | -------------------------------------------------------------------------------- /clients/rust/wcferry/proto/README.MD: -------------------------------------------------------------------------------- 1 | 将最新的 wcf.proto 放入此文件夹下 2 | -------------------------------------------------------------------------------- /WeChatFerry/spy/spy.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | InitSpy 3 | CleanupSpy 4 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/sdk.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | WxInitSDK 3 | WxDestroySDK 4 | -------------------------------------------------------------------------------- /clients/java/wcferry/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'wcferry' 2 | 3 | -------------------------------------------------------------------------------- /clients/python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include wcferry/*.dll 2 | include wcferry/*.exe 3 | -------------------------------------------------------------------------------- /assets/QR.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/assets/QR.jpeg -------------------------------------------------------------------------------- /assets/TEQuant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/assets/TEQuant.jpg -------------------------------------------------------------------------------- /WeChatFerry/spy/spy.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/WeChatFerry/spy/spy.aps -------------------------------------------------------------------------------- /clients/gohttp/public/assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | background: #f2f4f6; 4 | } 5 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/sdk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int WxInitSDK(bool debug, int port); 4 | int WxDestroySDK(); 5 | -------------------------------------------------------------------------------- /WeChatFerry/smc/Codec.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/WeChatFerry/smc/Codec.lib -------------------------------------------------------------------------------- /WeChatFerry/spy/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/WeChatFerry/spy/resource.h -------------------------------------------------------------------------------- /clients/rust/wcferry/src/main.rs: -------------------------------------------------------------------------------- 1 | mod wechat; 2 | 3 | fn main() { 4 | println!("Hello, wcferry!"); 5 | } 6 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/dll/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /docs/source/_static/QRCodes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/docs/source/_static/QRCodes.jpg -------------------------------------------------------------------------------- /clients/gohttp/public/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/gohttp/public/assets/icon.png -------------------------------------------------------------------------------- /clients/gohttp/public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/gohttp/public/assets/logo.png -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/dll/sdk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wechat-ferry-mvn/dll/sdk.dll -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/dll/spy.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wechat-ferry-mvn/dll/spy.dll -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/proto/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/win32-x86-64/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/dll/spy_debug.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wechat-ferry-mvn/dll/spy_debug.dll -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/task/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/python/wcferry/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from wcferry.client import Wcf, __version__ 4 | from wcferry.wxmsg import WxMsg 5 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/aggregation/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/constant/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/dto/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/service/impl/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/strategy/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /clients/java/wcferry/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wcferry/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /clients/java/wcferry/libs/nng-java-1.4.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wcferry/libs/nng-java-1.4.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /WeChatFerry/sdk/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 4 | // Windows 头文件 5 | #include 6 | -------------------------------------------------------------------------------- /WeChatFerry/spy/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 4 | // Windows 头文件 5 | #include 6 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep -------------------------------------------------------------------------------- /WeChatFerry/spy/spy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework.h" 4 | 5 | #define SUPPORT_VERSION L"3.9.11.25" 6 | 7 | void InitSpy(int port); 8 | void CleanupSpy(); 9 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-autoapi 2 | markdown-it-py[linkify,plugins] 3 | myst_parser 4 | sphinx 5 | sphinx_copybutton 6 | sphinx_rtd_theme 7 | sphinxcontrib-napoleon 8 | -------------------------------------------------------------------------------- /clients/java/wcferry/src/main/resources/win32-x86-64/nng.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wcferry/src/main/resources/win32-x86-64/nng.dll -------------------------------------------------------------------------------- /clients/http/README.MD: -------------------------------------------------------------------------------- 1 | # ~~WeChatFerry HTTP 客户端~~ 2 | 3 | ❗ **wcfhttp 不再维护,有需要可以使用 [WcfRust](https://github.com/lich0821/wcf-client-rust) 或者 [GoHttp](clients/gohttp/README.md)。** 4 | -------------------------------------------------------------------------------- /docs/source/openapi.rst: -------------------------------------------------------------------------------- 1 | OpenAPI 2 | ========= 3 | 4 | .. toctree:: 5 | :titlesonly: 6 | 7 | wcfhttp 8 | ------- 9 | 安装启动 wcfhttp 后,通过 http://localhost:9999/docs 查看。 10 | -------------------------------------------------------------------------------- /WeChatFerry/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | charset = utf-8-bom 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 4 -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/win32-x86-64/nng.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wechat-ferry-mvn/src/main/resources/win32-x86-64/nng.dll -------------------------------------------------------------------------------- /WeChatFerry/sdk/SDK.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /WeChatFerry/spy/Spy.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/proto/Makefile: -------------------------------------------------------------------------------- 1 | PROTOC?=../protoc 2 | 3 | all: nanopb_pb2.py 4 | 5 | %_pb2.py: %.proto 6 | $(PROTOC) --python_out=. $< 7 | 8 | .PHONY: clean 9 | clean: 10 | rm nanopb_pb2.py 11 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/libs/nng-java-1.4.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0ise/WeChatFerry/master/clients/java/wechat-ferry-mvn/src/main/resources/libs/nng-java-1.4.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/dll/readme.txt: -------------------------------------------------------------------------------- 1 | 说明: 2 | 由于项目规范限制,本目录保留,但最新的DLL还需要各位自行去下载解压 3 | 请把最新的 https://github.com/lich0821/WeChatFerry/releases/latest 下载的文件解压后放到该目录下 4 | sdk.dll 5 | spy.dll 6 | spy_debug.dll 7 | -------------------------------------------------------------------------------- /clients/pyauto/wcfauto/auto_res/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from wcfauto.auto_res.bot import Register 4 | from wcfauto.auto_res.core import load_function 5 | 6 | 7 | Register = load_function(Register) 8 | 9 | -------------------------------------------------------------------------------- /docs/source/readme_link.rst: -------------------------------------------------------------------------------- 1 | 项目介绍 2 | ========= 3 | 4 | .. include:: ../../clients/http/README.MD 5 | :parser: myst_parser.sphinx_ 6 | 7 | .. include:: ../../clients/python/README.MD 8 | :parser: myst_parser.sphinx_ 9 | -------------------------------------------------------------------------------- /WeChatFerry/spy/user_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "pb_types.h" 6 | 7 | using namespace std; 8 | 9 | string GetHomePath(); 10 | string GetSelfWxid(); 11 | 12 | UserInfo_t GetUserInfo(); 13 | -------------------------------------------------------------------------------- /clients/pyauto/wcfauto/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from wcfauto.auto_res import Register 4 | from wcfauto.wcf import WcfV2 as Wcf 5 | from wcfauto.wcf import WxMsgV2 as WxMsg 6 | 7 | __version__ = "39.0.3.0" 8 | 9 | -------------------------------------------------------------------------------- /clients/pyauto/wcfauto/event/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from wcfauto.event.event import Event 4 | from wcfauto.event.core import load_function 5 | 6 | Event = load_function(Event) 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /WeChatFerry/spy/rpc_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SPY_EXPORTS 4 | #define SPY_API __declspec(dllexport) 5 | #else 6 | #define SPY_API __declspec(dllimport) 7 | #endif 8 | 9 | int RpcStartServer(int port); 10 | int RpcStopServer(); 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "clients/node"] 2 | path = clients/node 3 | url = https://github.com/stkevintan/node-wcferry 4 | branch = main 5 | [submodule "clients/wcferry-node"] 6 | path = clients/wcferry-node 7 | url = https://github.com/dr-forget/wcferry-node.git 8 | -------------------------------------------------------------------------------- /WeChatFerry/spy/receive_msg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pb_types.h" 4 | 5 | void EnableLog(); 6 | void DisableLog(); 7 | void ListenPyq(); 8 | void UnListenPyq(); 9 | void ListenMessage(); 10 | void UnListenMessage(); 11 | MsgTypes_t GetMsgTypes(); 12 | -------------------------------------------------------------------------------- /WeChatFerry/spy/chatroom_mgmt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int AddChatroomMember(std::string roomid, std::string wxids); 6 | int DelChatroomMember(std::string roomid, std::string wxids); 7 | int InviteChatroomMember(std::string roomid, std::string wxids); 8 | -------------------------------------------------------------------------------- /clients/java/wcferry/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | formats: 4 | - epub 5 | - htmlzip 6 | 7 | build: 8 | os: ubuntu-22.04 9 | tools: 10 | python: "3.10" 11 | 12 | sphinx: 13 | configuration: docs/source/conf.py 14 | 15 | python: 16 | install: 17 | - requirements: docs/requirements.txt 18 | -------------------------------------------------------------------------------- /clients/gohttp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | 6 | "wechat-rest/httpd" 7 | 8 | "github.com/opentdp/wrest-chat/args" 9 | ) 10 | 11 | //go:embed public 12 | var efs embed.FS 13 | 14 | func main() { 15 | 16 | args.Efs = &efs 17 | 18 | httpd.Server() 19 | 20 | } 21 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/IResponse.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity; 2 | 3 | /** 4 | * 返回类接口 5 | */ 6 | public interface IResponse { 7 | 8 | /** 9 | * 状态码 10 | */ 11 | String getCode(); 12 | 13 | /** 14 | * 返回信息 15 | */ 16 | String getMsg(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /WeChatFerry/spy/contact_mgmt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "string" 4 | #include 5 | 6 | #include "pb_types.h" 7 | 8 | vector GetContacts(); 9 | int AcceptNewFriend(std::string v3, std::string v4, int scene); 10 | int AddFriendByWxid(std::string wxid, std::string msg); 11 | RpcContact_t GetContactByWxid(std::string wxid); 12 | -------------------------------------------------------------------------------- /clients/rust/wcferry/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .build_client(true) 4 | .build_server(false) 5 | .out_dir("src/proto") 6 | .compile(&["proto/wcf.proto", "proto/roomdata.proto"], &["."]) 7 | .expect("failed to compile protos"); 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/protoc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: This file acts as a drop-in replacement of binary protoc.exe. 3 | :: It will use either Python-based protoc from grpcio-tools package, 4 | :: or if it is not available, protoc.exe from path if found. 5 | 6 | setLocal enableDelayedExpansion 7 | set mydir=%~dp0 8 | python "%mydir%\protoc" %* 9 | exit /b %ERRORLEVEL% 10 | -------------------------------------------------------------------------------- /WeChatFerry/spy/exec_sql.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "pb_types.h" 6 | 7 | DbNames_t GetDbNames(); 8 | DbTables_t GetDbTables(const string db); 9 | DbRows_t ExecDbQuery(const string db, const string sql); 10 | int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx); 11 | vector GetAudioData(uint64_t msgid); 12 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/proto/wcf.options: -------------------------------------------------------------------------------- 1 | # Generate all fields as pointers. 2 | * mangle_names:M_STRIP_PACKAGE 3 | * fallback_type:FT_POINTER 4 | MsgTypes* fallback_type:FT_CALLBACK 5 | RpcContact* fallback_type:FT_CALLBACK 6 | DbNames* fallback_type:FT_CALLBACK 7 | DbTable* fallback_type:FT_CALLBACK 8 | DbField* fallback_type:FT_CALLBACK 9 | DbRow* fallback_type:FT_CALLBACK 10 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/aggregation/facade/ChatRoomDo.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.aggregation.facade; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | /** 7 | * 聚合模型类-联系人 8 | * 9 | * @author chandler 10 | * @date 2023-06-08 22:39:53 11 | */ 12 | @Slf4j 13 | @Data 14 | public class ChatRoomDo { 15 | } 16 | -------------------------------------------------------------------------------- /clients/go/README.md: -------------------------------------------------------------------------------- 1 | # WeChatFerry-go 2 | 3 | [WeChatFerry](https://github.com/lich0821/WeChatFerry) golang client 4 | 5 | ## Usage 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "github.com/danbai225/WeChatFerry-go/wcf" 12 | ) 13 | 14 | func main() { 15 | c, err := wcf.NewWCF("") 16 | if err != nil { 17 | panic(err) 18 | } 19 | println(c.IsLogin()) 20 | } 21 | 22 | ``` 23 | -------------------------------------------------------------------------------- /WeChatFerry/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wcf", 3 | "version-string": "1.0.0", 4 | "dependencies": [ 5 | { 6 | "name": "protobuf", 7 | "features": [ "zlib" ] 8 | }, 9 | "spdlog", 10 | "nng", 11 | "magic-enum", 12 | "minhook" 13 | ], 14 | "builtin-baseline": "80d54ff62d528339c626a6fbc3489a7f25956ade" 15 | } 16 | -------------------------------------------------------------------------------- /clients/gohttp/start-dev.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :: 3 | 4 | SET CGO_ENABLED=0 5 | SET GO111MODULE=on 6 | 7 | SET GOOS=windows 8 | SET GOARCH=amd64 9 | 10 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 11 | 12 | CD /d %~dp0 13 | 14 | go mod tidy 15 | 16 | if exist .local.yml ( 17 | echo use .local.yaml as config 18 | go run main.go .local.yml 19 | ) else ( 20 | go run main.go 21 | ) 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 请求添加新功能 3 | about: 提出一个关于本项目新功能 / 新特性的建议 4 | title: "[\U0001F4A1SUG] 一句话描述你希望新增的功能或特性" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **你希望添加的功能是否与某个问题相关?** 11 | 关于这个问题的简洁清晰的描述,例如,当 [...] 时,我总是很沮丧。 12 | 13 | **描述你希望的解决方案** 14 | 关于解决方案的简洁清晰的描述。 15 | 16 | **描述你考虑的替代方案** 17 | 关于你考虑的,能实现这个功能的其他替代方案的简洁清晰的描述。 18 | 19 | **其他** 20 | 你可以添加其他任何的资料、链接或者屏幕截图,以帮助我们理解这个新功能。 21 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 | #include "framework.h" 3 | 4 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 5 | { 6 | switch (ul_reason_for_call) { 7 | case DLL_PROCESS_ATTACH: 8 | case DLL_THREAD_ATTACH: 9 | case DLL_THREAD_DETACH: 10 | case DLL_PROCESS_DETACH: 11 | break; 12 | } 13 | return TRUE; 14 | } 15 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/protoc-gen-nanopb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This file is used to invoke nanopb_generator.py as a plugin 3 | # to protoc on Linux and other *nix-style systems. 4 | # Use it like this: 5 | # protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb --nanopb_out=dir foo.proto 6 | 7 | from nanopb_generator import * 8 | 9 | if __name__ == '__main__': 10 | # Assume we are running as a plugin under protoc. 11 | main_plugin() 12 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/injector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework.h" 4 | 5 | HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase); 6 | bool EjectDll(HANDLE process, HMODULE dllBase); 7 | bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, DWORD *ret); 8 | bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz, 9 | DWORD *ret); 10 | -------------------------------------------------------------------------------- /clients/rust/wcferry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wcferry" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tonic = "0.8.3" 10 | prost = "0.11.5" 11 | nng = "1.0.1" 12 | serde_json = "1.0" 13 | serde = { version = "1.0", features = ["derive"] } 14 | log = "0.4.17" 15 | hex = "0.4" 16 | 17 | [build-dependencies] 18 | tonic-build = "0.8.4" 19 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/service/WeChatMsgService.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.service; 2 | 3 | /** 4 | * 业务接口-消息处理 5 | * 6 | * @author chandler 7 | * @date 2024-10-01 14:30 8 | */ 9 | public interface WeChatMsgService { 10 | 11 | /** 12 | * 接收消息 13 | * 14 | * @param jsonString json数据 15 | * 16 | * @author chandler 17 | * @date 2024-10-01 14:33 18 | */ 19 | void receiveMsg(String jsonString); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /WeChatFerry/spy/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 | #include "framework.h" 3 | #include 4 | #include 5 | 6 | #include "spy.h" 7 | 8 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 9 | { 10 | switch (ul_reason_for_call) { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | -------------------------------------------------------------------------------- /clients/rust/wcferry/proto/roomdata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package roomdata; 3 | 4 | message RoomData { 5 | 6 | message RoomMember { 7 | string wxid = 1; 8 | string name = 2; 9 | int32 state = 3; 10 | } 11 | 12 | repeated RoomMember members = 1; 13 | 14 | int32 field_2 = 2; 15 | int32 field_3 = 3; 16 | int32 field_4 = 4; 17 | int32 room_capacity = 5; 18 | int32 field_6 = 6; 19 | int64 field_7 = 7 [jstype = JS_STRING]; 20 | int64 field_8 = 8 [jstype = JS_STRING]; 21 | } 22 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/protoc-gen-nanopb.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: This file is used to invoke nanopb_generator.py as a plugin 3 | :: to protoc on Windows. 4 | :: Use it like this: 5 | :: protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb.bat --nanopb_out=dir foo.proto 6 | :: 7 | :: Note that if you use the binary package of nanopb, the protoc 8 | :: path is already set up properly and there is no need to give 9 | :: --plugin= on the command line. 10 | 11 | set mydir=%~dp0 12 | python "%mydir%\nanopb_generator.py" --protoc-plugin %* 13 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/nanopb_generator.py2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # This file is a wrapper around nanopb_generator.py in case you want to run 3 | # it with Python 2 instead of default Python 3. This only exists for backwards 4 | # compatibility, do not use for new projects. 5 | 6 | from nanopb_generator import * 7 | 8 | if __name__ == '__main__': 9 | # Check if we are running as a plugin under protoc 10 | if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv: 11 | main_plugin() 12 | else: 13 | main_cli() 14 | -------------------------------------------------------------------------------- /clients/java/wcferry/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /clients/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/danbai225/WeChatFerry-go 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/danbai225/go-logs v0.2.2 7 | go.nanomsg.org/mangos/v3 v3.4.2 8 | google.golang.org/protobuf v1.31.0 9 | ) 10 | 11 | require ( 12 | github.com/Microsoft/go-winio v0.5.2 // indirect 13 | github.com/goccy/go-json v0.10.2 // indirect 14 | github.com/gorilla/websocket v1.5.0 // indirect 15 | github.com/kpango/fastime v1.1.9 // indirect 16 | github.com/kpango/glg v1.6.15 // indirect 17 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. PackageName documentation master file, created by 2 | sphinx-quickstart on Tue Jan 31 12:31:42 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | WeChatFerry 7 | ======================================= 8 | 9 | .. image:: _static/QRCodes.jpg 10 | 11 | .. toctree:: 12 | :maxdepth: 4 13 | 14 | readme_link 15 | openapi 16 | autoapi/index 17 | 18 | 19 | 索引及附录 20 | ================== 21 | 22 | * :ref:`genindex` 23 | * :ref:`modindex` 24 | * :ref:`search` 25 | -------------------------------------------------------------------------------- /clients/go_wcf_http/README.MD: -------------------------------------------------------------------------------- 1 | # wechatFerry 的 go版本http端 2 | 3 | 接口文档:https://apifox.com/apidoc/shared-6e6950ec-1a6d-4545-90d6-d27d31af2b7c 4 | 5 | http服务器的端口是 8000 需要修改的自行编译懒得写配置文件 6 | localhost:8000是本地接口文档 7 | 8 | 由于用到了cgo 编译需要安装Mingw 9 | Mingw下载地址:https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-win32/sjlj/x86_64-8.1.0-release-win32-sjlj-rt_v6-rev0.7z/download 10 | 11 | 打包命令 12 | 13 | #x86 win 编译 14 | set GOOS=windows 15 | set GOARCH=amd64 16 | go build -ldflags="-s -w" -o go_wcf_http3.9.10.27.exe .\main.go 17 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/service/SDK.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.service; 2 | 3 | import com.sun.jna.Library; 4 | 5 | /** 6 | * SDK.dll的接口类 7 | * 8 | * @author xinggq 9 | * @date 2024-07-10 15:21 10 | */ 11 | public interface SDK extends Library { 12 | 13 | /** 14 | * 初始化SDK 15 | * 16 | * @param debug 开发模式 17 | * @param port 端口 18 | * @return 状态值 19 | */ 20 | int WxInitSDK(boolean debug, int port); 21 | 22 | /** 23 | * 退出SDK 24 | * 25 | * @return 状态值 26 | */ 27 | int WxDestroySDK(); 28 | 29 | } -------------------------------------------------------------------------------- /WeChatFerry/spy/funcs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.h" 4 | #include 5 | 6 | int IsLogin(void); 7 | std::string GetAudio(uint64_t id, std::string dir); 8 | std::string GetPCMAudio(uint64_t id, std::string dir, int32_t sr); 9 | std::string DecryptImage(std::string src, std::string dst); 10 | int RefreshPyq(uint64_t id); 11 | int DownloadAttach(uint64_t id, std::string thumb, std::string extra); 12 | int RevokeMsg(uint64_t id); 13 | OcrResult_t GetOcrResult(std::string path); 14 | string GetLoginUrl(); 15 | int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid); 16 | -------------------------------------------------------------------------------- /clients/gohttp/config.yml: -------------------------------------------------------------------------------- 1 | # 运行日志 2 | Log: 3 | Dir: logs # 存储目录,非必要不修改 4 | Level: info # 记录级别,debug|info|warn|error 5 | Target: stdout # 输出方式,both|file|null|stdout|stderr 6 | 7 | # Wcf 服务 8 | Wcf: 9 | Address: 127.0.0.1:7601 # 若使用外部地址,请删除 SdkLibrary 选项 10 | MsgPrint: false # 是否将消息输出到控制台,可用于调试 11 | SdkLibrary: sdk.dll # wcf 二进制文件路径,留空则不托管(Linux 必须留空) 12 | 13 | # Web 服务 14 | Web: 15 | Address: 127.0.0.1:7600 # 监听地址,外网访问修改为 0.0.0.0:7600 16 | PushUrl: "" # 消息推送地址,一行一个,留空则不启用 17 | Storage: storage # 附件存储路径,非必要不修改 18 | Swagger: true # 是否启用内置接口文档和调试工具 19 | Token: "" # 使用 Token 验证请求,留空则不验证 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 报告 3 | about: 提交一份 bug 报告,帮助 WeChatFerry 变得更好 4 | title: "[\U0001F41BBUG] 用一句话描述您的问题。" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **描述这个 bug** 11 | 对 bug 作一个清晰简明的描述,包括: 12 | - 什么问题? 13 | - 是否安装后第一次启动就出现? 14 | - 重启微信后能否解决?(使用任务管理器,杀掉微信进程) 15 | - 直接运行 [最新发布](https://github.com/lich0821/WeChatFerry/releases/latest) 里的 `cpp.exe` 是否正常? 16 | 17 | **使用环境(请补全下列信息):** 18 | - 操作系统:【如 Windows 7, Windows 10, Windows Server 2008 等】 19 | - 操作系统版本:【32 位 或 64 位】 20 | - Python 版本:【如 3.7.9 32 位,3.8.15 64 位 等】 21 | 22 | **屏幕截图** 23 | 添加屏幕截图以帮助解释您的问题。(可选) 24 | 25 | **崩溃信息** 26 | 微信崩溃原因描述文件内容。(可选) 27 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/protoc-gen-nanopb-py2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is used to invoke nanopb_generator.py2 as a plugin 4 | # to protoc on Linux and other *nix-style systems. 5 | # 6 | # The difference from protoc-gen-nanopb is that this executes with Python 2. 7 | # 8 | # Use it like this: 9 | # protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb-py2 --nanopb_out=dir foo.proto 10 | # 11 | # Note that if you use the binary package of nanopb, the protoc 12 | # path is already set up properly and there is no need to give 13 | # --plugin= on the command line. 14 | 15 | MYPATH=$(dirname "$0") 16 | exec "$MYPATH/nanopb_generator.py2" --protoc-plugin 17 | -------------------------------------------------------------------------------- /clients/gohttp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Wrest Chat 7 | 8 | 9 | 16 | 17 | 18 | 19 |

Wrest Chat

20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/WeChatFerryApplication.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | /** 8 | * 启动类 9 | * 10 | * @author chandler 11 | * @date 2024-09-21 12:19 12 | */ 13 | @EnableScheduling 14 | @SpringBootApplication 15 | public class WeChatFerryApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(WeChatFerryApplication.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgStrategy.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.strategy.msg.receive; 2 | 3 | import com.wechat.ferry.entity.dto.WxPpMsgDTO; 4 | 5 | /** 6 | * 策略接口-消息处理-接收消息处理 7 | * 8 | * @author chandler 9 | * @date 2024-12-25 14:07 10 | */ 11 | public interface ReceiveMsgStrategy { 12 | 13 | /** 14 | * 获取策略的类型 15 | * 16 | * @return 返回代表策略类型的字符串 17 | */ 18 | String getStrategyType(); 19 | 20 | /** 21 | * 具体的处理 22 | * 23 | * @return 如果是多字段,可以转为JSON字符串返回,已适配不同的返回数据 24 | */ 25 | String doHandle(WxPpMsgDTO dto); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | !.editorconfig 4 | !.github/ 5 | !.readthedocs.yml 6 | !.gitattributes 7 | 8 | # VS2019 build files 9 | Debug/ 10 | Release/ 11 | x64/ 12 | Out/ 13 | 14 | # Generated files 15 | *.pb.h 16 | *.pb.c 17 | *.pb.cc 18 | *.dll 19 | *.exe 20 | build/ 21 | logs/ 22 | java/wcferry/bin/ 23 | vcpkg_installed/ 24 | 25 | *_pb2_grpc.py 26 | __pycache__ 27 | */python/dist/ 28 | */python/*.egg-info 29 | 30 | */java/target/ 31 | WcfGrpc.java 32 | WcfOuterClass.java 33 | 34 | */rust/target/ 35 | */rust/wcferry/Cargo.lock 36 | */rust/wcferry/.wcf.lock 37 | */rust/wcferry/target/CACHEDIR.TAG 38 | 39 | */http/dist/ 40 | */http/*.egg-info/ 41 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfRevokeMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-撤回消息 11 | * 12 | * @author chandler 13 | * @date 2024-12-25 12:00 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfRevokeMsgReq", description = "个微WCF撤回消息请求入参") 17 | public class WxPpWcfRevokeMsgReq { 18 | 19 | /** 20 | * 消息编号 21 | */ 22 | @ApiModelProperty(value = "场景") 23 | private String msgId; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /WeChatFerry/smc/codec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | double filetime; 9 | int32_t totPackets; 10 | } DecTime_t; 11 | 12 | int Mp3Encode(std::vector &pcm, std::string &mp3path, int32_t sr); 13 | int Mp3Encode(std::vector &pcm, std::vector &mp3, int32_t sr); 14 | DecTime_t SilkDecode(std::vector &silk, std::vector &pcm, int32_t sr); 15 | 16 | int Silk2Mp3(std::string inpath, std::string outpath, int sr); 17 | int Silk2Mp3(std::vector &silk, std::string mp3path, int sr); 18 | int Silk2Mp3(std::vector &silk, std::vector &mp3, int sr); 19 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/pb_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 7 | bool decode_string(pb_istream_t *stream, const pb_field_t *field, void **arg); 8 | bool encode_types(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 9 | bool encode_contacts(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 10 | bool encode_dbnames(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 11 | bool encode_tables(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 12 | bool encode_rows(pb_ostream_t *stream, const pb_field_t *field, void *const *arg); 13 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfDatabaseRowResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import java.util.List; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求出参-个微WCF数据库记录 11 | * 12 | * @author chandler 13 | * @date 2024/10/02 17:14 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfDatabaseRowResp", description = "个微WCF数据库记录查询请求出参") 17 | public class WxPpWcfDatabaseRowResp { 18 | 19 | /** 20 | * 字段列表 21 | */ 22 | @ApiModelProperty(value = "字段列表") 23 | private List fieldList; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /clients/java/wcferry/src/main/java/com/iamteer/SDK.java: -------------------------------------------------------------------------------- 1 | package com.iamteer; 2 | 3 | import com.sun.jna.Library; 4 | import com.sun.jna.Native; 5 | 6 | /** 7 | * SDK.dll的接口类 8 | * @Description 9 | * @Author xinggq 10 | * @Date 2024/7/10 11 | */ 12 | public interface SDK extends Library { 13 | // 读取项目根目录下dll文件夹,而且只能写绝对路径,放在resource下会有问题,暂时先不解决 14 | // 打包后,dll文件应该放在jar外,这样dll更新不需要生成jar包,重启下就ok了 15 | SDK INSTANCE = Native.load(System.getProperty("user.dir")+"\\dll\\sdk.dll", SDK.class); 16 | 17 | /** 18 | * 初始化SDK 19 | * @param debug 20 | * @param port 21 | * @return 22 | */ 23 | int WxInitSDK(boolean debug, int port); 24 | 25 | /** 26 | * 退出SDK 27 | * @return 28 | */ 29 | int WxDestroySDK(); 30 | } -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfGroupMemberReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-个微WCF查询群成员 11 | * 12 | * @author chandler 13 | * @date 2024-10-02 20:55 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfGroupMemberReq", description = "个微WCF查询群成员请求入参") 17 | public class WxPpWcfGroupMemberReq { 18 | 19 | /** 20 | * 群编号 21 | */ 22 | @NotBlank(message = "群编号不能为空") 23 | @ApiModelProperty(value = "群编号") 24 | private String groupNo; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /WeChatFerry/com/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef ENABLE_DEBUG_LOG 6 | #include 7 | 8 | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG 9 | void log_buffer(uint8_t *buffer, size_t len); 10 | #define LOG_BUFFER(buf, len) log_buffer((buf), (len)) 11 | #else 12 | #define LOG_BUFFER(...) (void)0 13 | #endif 14 | 15 | #include "spdlog/sinks/rotating_file_sink.h" 16 | #include "spdlog/sinks/stdout_color_sinks.h" 17 | #include "spdlog/spdlog.h" 18 | 19 | #define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__); 20 | #define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__); 21 | #define LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__); 22 | #define LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__); 23 | 24 | void InitLogger(std::string path); 25 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfMsgTypeResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF消息类型 9 | * 10 | * @author chandler 11 | * @date 2024/10/01 21:26 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfMsgTypeResp", description = "个微WCF消息类型查询请求出参") 15 | public class WxPpWcfMsgTypeResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wcferry/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'org.example' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'org.slf4j:slf4j-api:2.0.7' 14 | implementation 'ch.qos.logback:logback-core:1.3.6' 15 | implementation 'ch.qos.logback:logback-classic:1.3.6' 16 | implementation 'com.google.protobuf:protobuf-java:3.22.2' 17 | implementation 'net.java.dev.jna:jna:5.6.0' 18 | implementation fileTree(dir: 'libs', include: ['*.jar']) 19 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 20 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | } 26 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDatabaseTableReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-查询-个微WCF数据库表查询 11 | * 12 | * @author chandler 13 | * @date 2024-10-02 17:55 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfDatabaseTableReq", description = "个微WCF数据库表查询请求入参") 17 | public class WxPpWcfDatabaseTableReq { 18 | 19 | /** 20 | * 数据库名称 21 | */ 22 | @NotBlank(message = "数据库名称不能为空") 23 | @ApiModelProperty(value = "数据库名称") 24 | private String databaseName; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfDatabaseTableResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF数据库表 9 | * 10 | * @author chandler 11 | * @date 2024/12/24 13:40 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfDatabaseRowResp", description = "个微WCF数据库表查询请求出参") 15 | public class WxPpWcfDatabaseTableResp { 16 | 17 | /** 18 | * 表名 19 | */ 20 | @ApiModelProperty(value = "表名") 21 | private String tableName; 22 | 23 | /** 24 | * SQL 25 | */ 26 | @ApiModelProperty(value = "SQL") 27 | private String sql; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendFileMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送文件消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/04 23:07 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendFileMsgResp", description = "个微WCF发送文件消息请求出参") 15 | public class WxPpWcfSendFileMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendTextMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送文本消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/03 10:17 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendTextMsgResp", description = "个微WCF发送文本消息请求出参") 15 | public class WxPpWcfSendTextMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendXmlMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送XML消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/04 23:11 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendXmlMsgResp", description = "个微WCF发送XML消息请求出参") 15 | public class WxPpWcfSendXmlMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /WeChatFerry/spy/send_msg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | typedef struct { 8 | string name; 9 | string account; 10 | string title; 11 | string digest; 12 | string url; 13 | string thumburl; 14 | string receiver; 15 | } RichText_t; 16 | 17 | void SendTextMessage(string wxid, string msg, string atWxids); 18 | void SendImageMessage(string wxid, string path); 19 | void SendFileMessage(string wxid, string path); 20 | void SendXmlMessage(string receiver, string xml, string path, uint64_t type); 21 | void SendEmotionMessage(string wxid, string path); 22 | int SendRichTextMessage(RichText_t &rt); 23 | int SendPatMessage(string roomid, string wxid); 24 | int ForwardMessage(uint64_t msgid, string receiver); 25 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendEmojiMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送GIF消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/04 23:13 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendEmojiMsgResp", description = "个微WCF发送GIF消息请求出参") 15 | public class WxPpWcfSendEmojiMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendRichTextMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送富文本消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/06 15:46 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendRichTextMsgResp", description = "个微WCF发送富文本消息请求出参") 15 | public class WxPpWcfSendRichTextMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendPatOnePatMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送拍一拍消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/06 15:52 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendPatOnePatMsgResp", description = "个微WCF发送拍一拍消息请求出参") 15 | public class WxPpWcfSendPatOnePatMsgResp { 16 | 17 | /** 18 | * 类型编号 19 | */ 20 | @ApiModelProperty(value = "类型编号") 21 | private Integer id; 22 | 23 | /** 24 | * 类型名称 25 | */ 26 | @ApiModelProperty(value = "类型名称") 27 | private String name; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WhetherEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 枚举-是否 8 | * 9 | * @author chandler 10 | * @date 2023/3/14 10:21 11 | */ 12 | @Getter 13 | @AllArgsConstructor 14 | public enum WhetherEnum { 15 | 16 | /** 17 | * 1-Y-是 18 | */ 19 | YES("1", "是", "Y", true), 20 | 21 | /** 22 | * 2-N-否 23 | */ 24 | NO("2", "否", "N", false), 25 | 26 | /** 27 | * 未匹配上 28 | */ 29 | UN_MATCH("", null, null, null), 30 | 31 | // 结束 32 | ; 33 | 34 | private final String code; 35 | private final String name; 36 | private final String key; 37 | private final Boolean bool; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/wcf/ChatRoom.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.po.wcf; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 实体类-群信息表 9 | * 10 | * @author chandler 11 | * @date 2024-12-27 10:01 12 | */ 13 | @Data 14 | @ApiModel(value = "ChatRoom", description = "群信息表") 15 | public class ChatRoom { 16 | 17 | /** 18 | * 群名称 19 | */ 20 | @ApiModelProperty(value = "群名称") 21 | private String chatRoomName; 22 | 23 | /** 24 | * 用户名称列表 25 | */ 26 | @ApiModelProperty(value = "用户名称列表") 27 | private String userNameList; 28 | 29 | /** 30 | * 显示名称列表 31 | */ 32 | @ApiModelProperty(value = "显示名称列表") 33 | private String displayNameList; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /WeChatFerry/spy/spy_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework.h" 4 | #include 5 | 6 | typedef uint64_t QWORD; 7 | 8 | struct WxString { 9 | const wchar_t *wptr; 10 | DWORD size; 11 | DWORD capacity; 12 | const char *ptr; 13 | DWORD clen; 14 | WxString() 15 | { 16 | wptr = NULL; 17 | size = 0; 18 | capacity = 0; 19 | ptr = NULL; 20 | clen = 0; 21 | } 22 | 23 | WxString(std::wstring &ws) 24 | { 25 | wptr = ws.c_str(); 26 | size = (DWORD)ws.size(); 27 | capacity = (DWORD)ws.capacity(); 28 | ptr = NULL; 29 | clen = 0; 30 | } 31 | }; 32 | 33 | typedef struct RawVector { 34 | #ifdef _DEBUG 35 | QWORD head; 36 | #endif 37 | QWORD start; 38 | QWORD finish; 39 | QWORD end; 40 | } RawVector_t; 41 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/wcf/RevokeMsgStorage.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.po.wcf; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 实体类-撤回消息存储表 9 | * 10 | * @author chandler 11 | * @date 2024-12-27 10:08 12 | */ 13 | @Data 14 | @ApiModel(value = "RevokeMsgStorage对象", description = "撤回消息存储表") 15 | public class RevokeMsgStorage { 16 | 17 | /** 18 | * 创建时间 19 | */ 20 | @ApiModelProperty(value = "创建时间") 21 | private Integer createTime; 22 | 23 | /** 24 | * 消息服务ID 25 | */ 26 | @ApiModelProperty(value = "消息服务ID") 27 | private Integer msgSvrId; 28 | 29 | /** 30 | * 撤回服务ID 31 | */ 32 | @ApiModelProperty(value = "撤回服务ID") 33 | private Integer revokeSvrId; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/wcf/ContactHeadImgUrl.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.po.wcf; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 实体类-联系人头像信息表 9 | * 10 | * @author chandler 11 | * @date 2024-12-27 09:59 12 | */ 13 | @Data 14 | @ApiModel(value = "ContactHeadImgUrl对象", description = "联系人头像信息表") 15 | public class ContactHeadImgUrl { 16 | 17 | /** 18 | * 用户名 19 | */ 20 | @ApiModelProperty(value = "用户名") 21 | private String userName; 22 | 23 | /** 24 | * 小头像URL 25 | */ 26 | @ApiModelProperty(value = "小头像URL") 27 | private String smallHeadIngUrl; 28 | 29 | /** 30 | * 大头像URL 31 | */ 32 | @ApiModelProperty(value = "大头像URL") 33 | private String bigHeadIngUrl; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfDatabaseFieldResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF数据库字段 9 | * 10 | * @author chandler 11 | * @date 2024/10/02 17:15 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfDatabaseFieldResp", description = "个微WCF数据库字段查询请求出参") 15 | public class WxPpWcfDatabaseFieldResp { 16 | 17 | /** 18 | * 字段类型 19 | */ 20 | @ApiModelProperty(value = "字段类型") 21 | private String type; 22 | 23 | /** 24 | * 字段 25 | */ 26 | @ApiModelProperty(value = "字段") 27 | private String column; 28 | 29 | /** 30 | * 字段值 31 | */ 32 | @ApiModelProperty(value = "字段值") 33 | private Object value; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/gohttp/httpd/server.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/opentdp/go-helper/httpd" 5 | 6 | "github.com/opentdp/wrest-chat/args" 7 | "github.com/opentdp/wrest-chat/httpd/middle" 8 | "github.com/opentdp/wrest-chat/httpd/wcfrest" 9 | ) 10 | 11 | // @title Wrest Chat Api 12 | // @version v0.10.0 13 | // @description 基于 WeChatFerry RPC 实现的微信接口,使用 Go 语言编写,无第三方运行时依赖,易于对接任意编程语言。 14 | // @contact.name WeChatRest 15 | // @contact.url https://github.com/opentdp/wrest-chat 16 | // @license.name Apache 2.0 17 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 18 | // @BasePath / 19 | 20 | func Server() { 21 | 22 | httpd.Engine(args.Debug) 23 | 24 | // Wcfrest 路由 25 | wcfrest.Route() 26 | 27 | // Swagger 守卫 28 | httpd.Use(middle.SwaggerGuard) 29 | 30 | // 前端文件路由 31 | httpd.StaticEmbed("/", "public", args.Efs) 32 | 33 | // 启动 HTTP 服务 34 | httpd.Server(args.Web.Address) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDatabaseSqlReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-查询-个微WCF数据库SQL查询 11 | * 12 | * @author chandler 13 | * @date 2024-10-02 17:10 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfDatabaseSqlReq", description = "个微WCF数据库SQL查询请求入参") 17 | public class WxPpWcfDatabaseSqlReq { 18 | 19 | /** 20 | * 数据库名称 21 | */ 22 | @NotBlank(message = "数据库名称不能为空") 23 | @ApiModelProperty(value = "数据库名称") 24 | private String databaseName; 25 | 26 | /** 27 | * SQL语句 28 | */ 29 | @NotBlank(message = "SQL语句不能为空") 30 | @ApiModelProperty(value = "SQL语句") 31 | private String sqlText; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDeleteGroupMemberReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | 11 | /** 12 | * 请求入参-个微WCF删除群成员 13 | * 14 | * @author chandler 15 | * @date 2024-12-25 10:01 16 | */ 17 | @Data 18 | @ApiModel(value = "wxPpWcfDeleteGroupMemberReq", description = "个微WCF删除群成员请求入参") 19 | public class WxPpWcfDeleteGroupMemberReq { 20 | 21 | /** 22 | * 群编号 23 | */ 24 | @NotBlank(message = "群编号不能为空") 25 | @ApiModelProperty(value = "群编号") 26 | private String groupNo; 27 | 28 | /** 29 | * 待删除的群成员列表 30 | */ 31 | @ApiModelProperty(value = "待删除的群成员列表") 32 | private List groupMembers; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfInviteGroupMemberReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | 11 | /** 12 | * 请求入参-个微WCF邀请群成员 13 | * 14 | * @author chandler 15 | * @date 2024-12-25 09:51 16 | */ 17 | @Data 18 | @ApiModel(value = "wxPpWcfInviteGroupMemberReq", description = "个微WCF邀请群成员请求入参") 19 | public class WxPpWcfInviteGroupMemberReq { 20 | 21 | /** 22 | * 群编号 23 | */ 24 | @NotBlank(message = "群编号不能为空") 25 | @ApiModelProperty(value = "群编号") 26 | private String groupNo; 27 | 28 | /** 29 | * 待邀请的群成员列表 30 | */ 31 | @ApiModelProperty(value = "待邀请的群成员列表") 32 | private List groupMembers; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfReceiveTransferReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求入参-个微WCF接收转账 9 | * 10 | * @author chandler 11 | * @date 2024-12-25 13:46 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfReceiveTransferReq", description = "个微WCF接收转账请求入参") 15 | public class WxPpWcfReceiveTransferReq { 16 | 17 | /** 18 | * 转账人 19 | */ 20 | @ApiModelProperty(value = "转账人") 21 | private String weChatUid; 22 | 23 | /** 24 | * 转账编号 transferId 25 | */ 26 | @ApiModelProperty(value = "转账编号") 27 | private String transferId; 28 | 29 | /** 30 | * 交易编号 Transaction id 31 | */ 32 | @ApiModelProperty(value = "交易编号") 33 | private String transactionId; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfAddFriendGroupMemberReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 请求入参-添加群成员为好友 13 | * 14 | * @author chandler 15 | * @date 2024-12-25 09:53 16 | */ 17 | @Data 18 | @ApiModel(value = "wxPpWcfAddFriendGroupMemberReq", description = "个微WCF添加群成员为好友请求入参") 19 | public class WxPpWcfAddFriendGroupMemberReq { 20 | 21 | /** 22 | * 群编号 23 | */ 24 | @NotBlank(message = "群编号不能为空") 25 | @ApiModelProperty(value = "群编号") 26 | private String groupNo; 27 | 28 | /** 29 | * 待添加的群成员列表 30 | */ 31 | @ApiModelProperty(value = "待添加的群成员列表") 32 | private List groupMembers; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfPatOnePatMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-个微WCF发送拍一拍消息 11 | * 12 | * @author chandler 13 | * @date 2024-10-06 15:50 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfPatOnePatMsgReq", description = "个微WCF发送拍一拍消息请求入参") 17 | public class WxPpWcfPatOnePatMsgReq { 18 | 19 | /** 20 | * 消息接收人 21 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 22 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 23 | */ 24 | @NotBlank(message = "消息接收人不能为空") 25 | @ApiModelProperty(value = "消息接收人") 26 | private String recipient; 27 | 28 | /** 29 | * 要拍的人的wxid 30 | */ 31 | @NotBlank(message = "要拍的人的wxid不能为空") 32 | @ApiModelProperty(value = "要拍的人的wxid") 33 | private String patUser; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendFileMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-个微WCF发送文件消息 11 | * 12 | * @author chandler 13 | * @date 2024-10-04 23:08 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfSendFileMsgReq", description = "个微WCF发送文件消息请求入参") 17 | public class WxPpWcfSendFileMsgReq { 18 | 19 | /** 20 | * 资源路径-本地文件路径 21 | */ 22 | @NotBlank(message = "资源路径不能为空") 23 | @ApiModelProperty(value = "资源路径-本地文件路径") 24 | private String resourcePath; 25 | 26 | /** 27 | * 消息接收人 28 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 29 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 30 | */ 31 | @NotBlank(message = "消息接收人不能为空") 32 | @ApiModelProperty(value = "消息接收人") 33 | private String recipient; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WxPpMsgTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 枚举-本服务定义消息类型 8 | * 9 | * @author chandler 10 | * @date 2024/10/01 15:55 11 | */ 12 | @Getter 13 | @AllArgsConstructor 14 | public enum WxPpMsgTypeEnum { 15 | 16 | /** 17 | * 0-未知 18 | */ 19 | UNKNOWN("0", "未知", "文本"), 20 | 21 | /** 22 | * 1-文本 23 | */ 24 | TEXT("1", "文本", "文本"), 25 | 26 | /** 27 | * 2-表情符号 28 | */ 29 | EMOJI("2", "表情符号", "1"), 30 | 31 | /** 32 | * 3-表情包(表情图片) 33 | */ 34 | EMOJI_IMAGE("3", "表情包(表情图片)", "47"), 35 | 36 | /** 37 | * 4-引用消息 38 | */ 39 | QUOTE("4", "引用消息", "49"), 40 | 41 | /** 42 | * 未匹配上 43 | */ 44 | UN_MATCH("", null, "文本"), 45 | 46 | // 结束 47 | ; 48 | 49 | private final String code; 50 | private final String name; 51 | private final String dllCode; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendEmojiMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-个微WCF发送表情消息 11 | * 12 | * @author chandler 13 | * @date 2024-10-04 23:14 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfSendEmojiMsgReq", description = "个微WCF发送表情消息请求入参") 17 | public class WxPpWcfSendEmojiMsgReq { 18 | 19 | /** 20 | * 资源路径-本地表情路径 21 | * 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\) 22 | */ 23 | @NotBlank(message = "资源路径不能为空") 24 | @ApiModelProperty(value = "资源路径-本地表情路径") 25 | private String resourcePath; 26 | 27 | /** 28 | * 消息接收人 29 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 30 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 31 | */ 32 | @NotBlank(message = "消息接收人不能为空") 33 | @ApiModelProperty(value = "消息接收人") 34 | private String recipient; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/config/ProtobufConfig.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.config; 2 | 3 | import java.util.Collections; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | /** 11 | * 配置类-protobuf 12 | * 13 | * @author chandler 14 | * @date 2024-09-26 21:35 15 | */ 16 | @Configuration 17 | public class ProtobufConfig { 18 | 19 | /** 20 | * protobuf 序列化 21 | */ 22 | @Bean 23 | ProtobufHttpMessageConverter protobufHttpMessageConverter() { 24 | return new ProtobufHttpMessageConverter(); 25 | } 26 | 27 | /** 28 | * protobuf 反序列化 29 | */ 30 | @Bean 31 | RestTemplate restTemplate(ProtobufHttpMessageConverter protobufHttpMessageConverter) { 32 | return new RestTemplate(Collections.singletonList(protobufHttpMessageConverter)); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfLoginInfoResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-登录WCF个微信息 9 | * 10 | * @author chandler 11 | * @date 2024/10/05 22:53 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfLoginInfoResp", description = "登录WCF个微信息查询请求出参") 15 | public class WxPpWcfLoginInfoResp { 16 | 17 | /** 18 | * 微信内部识别号UID 19 | * 原始微信账号ID,以"wxid_"开头,初始默认的微信ID=微信号。 20 | */ 21 | @ApiModelProperty(value = "微信内部识别号UID") 22 | private String weChatUid; 23 | 24 | /** 25 | * 微信昵称 26 | */ 27 | @ApiModelProperty(value = "微信昵称") 28 | private String weChatNickname; 29 | 30 | /** 31 | * 手机号 32 | */ 33 | @ApiModelProperty(value = "手机号") 34 | private String phone; 35 | 36 | /** 37 | * 文件/图片等父路径 38 | */ 39 | @ApiModelProperty(value = "文件/图片等父路径") 40 | private String homePath; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfSendImageMsgResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF发送图像消息 9 | * 10 | * @author chandler 11 | * @date 2024/10/03 10:17 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfSendImageMsgResp", description = "个微WCF发送图像消息请求出参") 15 | public class WxPpWcfSendImageMsgResp { 16 | 17 | /** 18 | * 状态码 19 | */ 20 | @ApiModelProperty(value = "状态码") 21 | private String code; 22 | 23 | /** 24 | * 返回信息 25 | */ 26 | @ApiModelProperty(value = "返回信息") 27 | private String msg; 28 | 29 | /** 30 | * 图片地址 31 | */ 32 | @ApiModelProperty(value = "图片地址") 33 | private String path; 34 | 35 | /** 36 | * 消息接收人 37 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 38 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 39 | */ 40 | @ApiModelProperty(value = "消息接收人") 41 | private String recipient; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/wcf/ChatRoomInfo.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.po.wcf; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 实体类-群详细信息表 9 | * 10 | * @author chandler 11 | * @date 2024-12-27 10:03 12 | */ 13 | @Data 14 | @ApiModel(value = "ChatRoomInfo", description = "群详细信息表") 15 | public class ChatRoomInfo { 16 | 17 | /** 18 | * 群名称 19 | */ 20 | @ApiModelProperty(value = "群名称") 21 | private String chatRoomName; 22 | 23 | /** 24 | * 群公告 25 | */ 26 | @ApiModelProperty(value = "群公告") 27 | private String announcement; 28 | 29 | /** 30 | * 公告编辑者 31 | */ 32 | @ApiModelProperty(value = "公告编辑者") 33 | private String announcementEditor; 34 | 35 | /** 36 | * 公告发布时间 37 | */ 38 | @ApiModelProperty(value = "公告发布时间") 39 | private String announcementPublishTime; 40 | 41 | /** 42 | * 群状态 43 | */ 44 | @ApiModelProperty(value = "群状态") 45 | private Integer chatRoomStatus; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/strategy/msg/receive/impl/SignInMsgStrategyImpl.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.strategy.msg.receive.impl; 2 | 3 | import com.wechat.ferry.entity.dto.WxPpMsgDTO; 4 | import com.wechat.ferry.enums.ReceiveMsgChannelEnum; 5 | import com.wechat.ferry.strategy.msg.receive.ReceiveMsgStrategy; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 策略实现类-接收消息-签到处理 11 | * 12 | * @author chandler 13 | * @date 2024-12-25 14:19 14 | */ 15 | @Slf4j 16 | @Component 17 | public class SignInMsgStrategyImpl implements ReceiveMsgStrategy { 18 | 19 | @Override 20 | public String getStrategyType() { 21 | log.debug("[接收消息]-[签到处理]-匹配到:{}-{}-策略", ReceiveMsgChannelEnum.SIGN_IN.getCode(), ReceiveMsgChannelEnum.SIGN_IN.getName()); 22 | return ReceiveMsgChannelEnum.SIGN_IN.getCode(); 23 | } 24 | 25 | @Override 26 | public String doHandle(WxPpMsgDTO dto) { 27 | // TODO 这里写具体的操作 28 | // 当前是使用的所有策略类全部执行 所以这里需要控制哪种类型才处理 29 | log.info("签到处理"); 30 | return ""; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Changhua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/MsgEventTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-消息事件类型 12 | * 13 | * @author chandler 14 | * @date 2024/12/27 18:09 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum MsgEventTypeEnum { 19 | 20 | /** 21 | * 1-注入成功 22 | */ 23 | INJECT("1", "注入成功"), 24 | 25 | /** 26 | * 未匹配上 27 | */ 28 | UN_MATCH("", null), 29 | 30 | // 结束 31 | ; 32 | 33 | private final String code; 34 | private final String name; 35 | 36 | /** 37 | * map集合 key:code val:枚举 38 | */ 39 | public static final Map codeMap = Arrays.stream(values()).collect(Collectors.toMap(MsgEventTypeEnum::getCode, v -> v)); 40 | 41 | /** 42 | * 根据code获取枚举 43 | */ 44 | public static MsgEventTypeEnum getCodeMap(String code) { 45 | return codeMap.getOrDefault(code, UN_MATCH); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /clients/go/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Changhua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/DatabaseNameEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-数据库名称 12 | * 13 | * @author chandler 14 | * @date 2024/12/24 15:10 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum DatabaseNameEnum { 19 | 20 | /** 21 | * MicroMsg- 22 | */ 23 | MICRO_MSG("MicroMsg.db", ""), 24 | 25 | /** 26 | * 未匹配上 27 | */ 28 | UN_MATCH("", null), 29 | 30 | // 结束 31 | ; 32 | 33 | private final String code; 34 | private final String name; 35 | 36 | /** 37 | * map集合 key:code val:枚举 38 | */ 39 | public static final Map codeMap = Arrays.stream(values()).collect(Collectors.toMap(DatabaseNameEnum::getCode, v -> v)); 40 | 41 | /** 42 | * 根据code获取枚举 43 | */ 44 | public static DatabaseNameEnum getCodeMap(String code) { 45 | return codeMap.getOrDefault(code, UN_MATCH); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /clients/gohttp/public/swagger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Wrest Chat Document 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 |
20 | 21 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/ReceiveMsgChannelEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-接收消息处理渠道 12 | * 13 | * @author chandler 14 | * @date 2024/12/25 14:15 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum ReceiveMsgChannelEnum { 19 | 20 | /** 21 | * 1-签到 22 | */ 23 | SIGN_IN("1", "签到"), 24 | 25 | /** 26 | * 未匹配上 27 | */ 28 | UN_MATCH("", null), 29 | 30 | // 结束 31 | ; 32 | 33 | private final String code; 34 | private final String name; 35 | 36 | /** 37 | * map集合 key:code val:枚举 38 | */ 39 | public static final Map codeMap = 40 | Arrays.stream(values()).collect(Collectors.toMap(ReceiveMsgChannelEnum::getCode, v -> v)); 41 | 42 | /** 43 | * 根据code获取枚举 44 | */ 45 | public static ReceiveMsgChannelEnum getCodeMap(String code) { 46 | return codeMap.getOrDefault(code, UN_MATCH); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/TableNameEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-表名称 12 | * 13 | * @author chandler 14 | * @date 2024/12/24 15:24 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum TableNameEnum { 19 | 20 | /** 21 | * CONTACT-联系人 22 | */ 23 | CONTACT("Contact", "联系人", ""), 24 | 25 | /** 26 | * 未匹配上 27 | */ 28 | UN_MATCH("", null, ""), 29 | 30 | // 结束 31 | ; 32 | 33 | private final String code; 34 | private final String name; 35 | private final String db; 36 | 37 | /** 38 | * map集合 key:code val:枚举 39 | */ 40 | public static final Map codeMap = Arrays.stream(values()).collect(Collectors.toMap(TableNameEnum::getCode, v -> v)); 41 | 42 | /** 43 | * 根据code获取枚举 44 | */ 45 | public static TableNameEnum getCodeMap(String code) { 46 | return codeMap.getOrDefault(code, UN_MATCH); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendImageMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-个微WCF发送图片消息 11 | * 12 | * @author chandler 13 | * @date 2024-10-04 15:55 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfSendImageMsgReq", description = "个微WCF发送图片消息请求入参") 17 | public class WxPpWcfSendImageMsgReq { 18 | 19 | /** 20 | * 资源路径-本地图片地址 21 | * 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\) 22 | * 如:`C:/Projs/WeChatRobot/TEQuant.jpeg` 23 | * 或 `https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/TEQuant.jpg` 24 | */ 25 | @NotBlank(message = "资源路径不能为空") 26 | @ApiModelProperty(value = "资源路径-本地图片地址") 27 | private String resourcePath; 28 | 29 | /** 30 | * 消息接收人 31 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 32 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 33 | */ 34 | @NotBlank(message = "消息接收人不能为空") 35 | @ApiModelProperty(value = "消息接收人") 36 | private String recipient; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfGroupMemberResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF查询群成员 9 | * 10 | * @author chandler 11 | * @date 2024/10/01 21:26 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfGroupMemberResp", description = "个微WCF群成员查询请求出参") 15 | public class WxPpWcfGroupMemberResp { 16 | 17 | /** 18 | * 微信内部识别号UID 19 | * 原始微信账号ID,以"wxid_"开头,初始默认的微信ID=微信号。 20 | */ 21 | @ApiModelProperty(value = "微信内部识别号UID") 22 | private String weChatUid; 23 | 24 | /** 25 | * 群内昵称 26 | */ 27 | @ApiModelProperty(value = "群内昵称") 28 | private String groupNickName; 29 | 30 | /** 31 | * 状态 32 | */ 33 | @ApiModelProperty(value = "状态") 34 | private String state; 35 | 36 | /** 37 | * 是否自己 38 | */ 39 | @ApiModelProperty(value = "是否自己") 40 | private Boolean whetherSelf; 41 | 42 | /** 43 | * 是否企微 44 | */ 45 | @ApiModelProperty(value = "是否企微") 46 | private Boolean whetherWork; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /clients/rust/wcferry/src/proto/roomdata.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::derive_partial_eq_without_eq)] 2 | #[derive(Clone, PartialEq, ::prost::Message)] 3 | pub struct RoomData { 4 | #[prost(message, repeated, tag = "1")] 5 | pub members: ::prost::alloc::vec::Vec, 6 | #[prost(int32, tag = "2")] 7 | pub field_2: i32, 8 | #[prost(int32, tag = "3")] 9 | pub field_3: i32, 10 | #[prost(int32, tag = "4")] 11 | pub field_4: i32, 12 | #[prost(int32, tag = "5")] 13 | pub room_capacity: i32, 14 | #[prost(int32, tag = "6")] 15 | pub field_6: i32, 16 | #[prost(int64, tag = "7")] 17 | pub field_7: i64, 18 | #[prost(int64, tag = "8")] 19 | pub field_8: i64, 20 | } 21 | /// Nested message and enum types in `RoomData`. 22 | pub mod room_data { 23 | #[allow(clippy::derive_partial_eq_without_eq)] 24 | #[derive(Clone, PartialEq, ::prost::Message)] 25 | pub struct RoomMember { 26 | #[prost(string, tag = "1")] 27 | pub wxid: ::prost::alloc::string::String, 28 | #[prost(string, tag = "2")] 29 | pub name: ::prost::alloc::string::String, 30 | #[prost(int32, tag = "3")] 31 | pub state: i32, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/SexEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-性别 12 | * 13 | * @author chandler 14 | * @date 2024/10/01 15:42 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum SexEnum { 19 | 20 | /** 21 | * 0-未知 22 | */ 23 | UNKNOWN("0", "未知"), 24 | 25 | /** 26 | * 1-男 27 | */ 28 | BOY("1", "男"), 29 | 30 | /** 31 | * 2-女 32 | */ 33 | GIRL("2", "女"), 34 | 35 | /** 36 | * 未匹配上 37 | */ 38 | UN_MATCH("", null), 39 | 40 | // 结束 41 | ; 42 | 43 | private final String code; 44 | private final String name; 45 | 46 | /** 47 | * map集合 key:code val:枚举 48 | */ 49 | public static final Map codeMap = Arrays.stream(values()).collect(Collectors.toMap(SexEnum::getCode, v -> v)); 50 | 51 | /** 52 | * 根据code获取枚举 53 | */ 54 | public static SexEnum getCodeMap(String code) { 55 | return codeMap.getOrDefault(code, UN_MATCH); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 5 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | /** 9 | * 配置类-配置项目首页 10 | * 11 | * @author chandler 12 | * @date 2024-10-04 10:30 13 | */ 14 | @Configuration 15 | public class WebConfig implements WebMvcConfigurer { 16 | 17 | @Override 18 | public void addViewControllers(ViewControllerRegistry registry) { 19 | // 将根路径 "/" 的请求重定向到 "/index.html" 20 | registry.addViewController("/").setViewName("forward:/index.html"); 21 | WebMvcConfigurer.super.addViewControllers(registry); 22 | } 23 | 24 | @Override 25 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 26 | // 添加资源处理器,用于映射静态资源路径 27 | registry.addResourceHandler("/**") 28 | // 添加资路径 29 | .addResourceLocations("classpath:/templates/"); 30 | WebMvcConfigurer.super.addResourceHandlers(registry); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfPassFriendApplyReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 请求入参-通过好友申请 11 | * 12 | * @author chandler 13 | * @date 2024-12-25 09:30 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfPassFriendApplyReq", description = "个微WCF通过好友申请请求入参") 17 | public class WxPpWcfPassFriendApplyReq { 18 | 19 | /** 20 | * 申请人 21 | * v3 xml.attrib["encryptusername"] 22 | * 加密用户名 (好友申请消息里 v3 开头的字符串) 23 | */ 24 | @NotBlank(message = "申请人不能为空") 25 | @ApiModelProperty(value = "申请人") 26 | private String applicant; 27 | 28 | /** 29 | * 审核人 30 | * v4 xml.attrib["ticket"] 31 | * Ticket (好友申请消息里 v4 开头的字符串) 32 | * 一般指自己,别人申请添加,自己审核是否通过 33 | */ 34 | @NotBlank(message = "审核人不能为空") 35 | @ApiModelProperty(value = "审核人") 36 | private String reviewer; 37 | 38 | /** 39 | * 场景 40 | * 申请方式 (好友申请消息里的 scene); 为了兼容旧接口,默认为扫码添加 (30) 41 | */ 42 | @ApiModelProperty(value = "场景") 43 | private String scene; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /WeChatFerry/spy/spy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "log.h" 4 | #include "rpc_server.h" 5 | #include "spy.h" 6 | #include "util.h" 7 | 8 | UINT64 g_WeChatWinDllAddr = 0; 9 | 10 | static bool IsWxVersionMatched(const wchar_t *version) 11 | { 12 | if (wcscmp(version, SUPPORT_VERSION) != 0) { 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | void InitSpy(LPVOID args) 19 | { 20 | 21 | wchar_t version[16] = { 0 }; 22 | PortPath_t *pp = (PortPath_t *)args; 23 | 24 | InitLogger(pp->path); 25 | g_WeChatWinDllAddr = (UINT64)GetModuleHandle(L"WeChatWin.dll"); // 获取wechatWin模块地址 26 | if (g_WeChatWinDllAddr == 0) { 27 | LOG_ERROR("获取 wechatWin.dll 模块地址失败"); 28 | return; // TODO: 退出进程,避免后面操作失败 29 | } 30 | 31 | if (!GetWeChatVersion(version)) { // 获取微信版本 32 | LOG_ERROR("获取微信版本失败"); 33 | return; 34 | } 35 | LOG_INFO("WeChat version: {}", Wstring2String(version).c_str()); 36 | if (!IsWxVersionMatched(version)) { 37 | LOG_ERROR("不支持当前版本"); 38 | MessageBox(NULL, L"不支持当前版本", L"错误", 0); 39 | return; 40 | } 41 | 42 | RpcStartServer(pp->port); 43 | } 44 | 45 | void CleanupSpy() 46 | { 47 | LOG_DEBUG("CleanupSpy"); 48 | RpcStopServer(); 49 | } 50 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # 配置文件 2 | 3 | # 服务端配置 4 | server: 5 | # 端口设置 6 | port: 9201 7 | 8 | spring: 9 | # 配置应用信息 10 | application: 11 | # 应用名 12 | name: wechat-ferry 13 | # swagger适配 14 | mvc: 15 | pathmatch: 16 | matching-strategy: ant_path_matcher 17 | 18 | # 日志配置 19 | logging: 20 | config: classpath:logback-spring.xml 21 | 22 | # 本服务参数 23 | wechat: 24 | ferry: 25 | # DLL文件位置 26 | dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll 27 | # socket端口 28 | socket-port: 10086 29 | # 联系人类型-官方杂号,禁止与其他分类重复(格式:代码|名称) 30 | contacts-type-mixed: 31 | - filehelper|文件传输助手 32 | # 联系人类型-公众号,禁止与其他分类重复(格式:代码|名称) 33 | contacts-type-official: 34 | - weixinguanhaozhushou|微信公众平台 35 | # 需要开启消息处理的群 36 | open-msg-groups: 37 | - 53257911728@chatroom 38 | # 接收消息回调开关 39 | receive-msg-callback-switch: false 40 | # 接收消息回调地址 41 | receive-msg-callback-urls: 42 | - http://localhost:9001/msg 43 | # 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调 44 | send-msg-callback-flag: '1' 45 | # 发送消息回调地址 46 | send-msg-callback-urls: 47 | - http://localhost:9001/msg 48 | # 调用第三方服务客户端成功状态码 49 | third-party-ok-codes: 50 | # key:状态码字段 val:状态码值 51 | code: '200' 52 | 53 | -------------------------------------------------------------------------------- /clients/pyauto/setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import wcfauto 5 | from setuptools import find_packages, setup 6 | 7 | with open("README.md", "r", encoding="utf-8") as fh: 8 | long_description = fh.read() 9 | 10 | 11 | setup( 12 | name="wcfauto", 13 | version=wcfauto.__version__, 14 | author="bujinzhang", 15 | author_email="", 16 | description="一个玩微信的工具", 17 | long_description=long_description, 18 | long_description_content_type="text/markdown", 19 | license="MIT", 20 | url="https://github.com/lich0821/WeChatFerry", 21 | python_requires=">=3.8", 22 | packages=find_packages(), 23 | include_package_data=True, 24 | install_requires=[ 25 | "setuptools", 26 | "wcferry", 27 | ], 28 | classifiers=[ 29 | "Environment :: Win32 (MS Windows)", 30 | "Intended Audience :: Developers", 31 | "Intended Audience :: Customer Service", 32 | "Topic :: Communications :: Chat", 33 | "Operating System :: Microsoft :: Windows", 34 | "Programming Language :: Python", 35 | ], 36 | project_urls={ 37 | "Documentation": "https://wechatferry.readthedocs.io/zh/latest/index.html", 38 | "GitHub": "https://github.com/lich0821/WeChatFerry/", 39 | }, 40 | ) 41 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/pb_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | typedef map MsgTypes_t; 10 | 11 | typedef struct { 12 | int32_t gender; 13 | string wxid; 14 | string code; 15 | string remark; 16 | string name; 17 | string country; 18 | string province; 19 | string city; 20 | } RpcContact_t; 21 | 22 | typedef vector DbNames_t; 23 | 24 | typedef struct { 25 | string name; 26 | string sql; 27 | } DbTable_t; 28 | typedef vector DbTables_t; 29 | 30 | typedef struct { 31 | int32_t type; 32 | string column; 33 | vector content; 34 | } DbField_t; 35 | typedef vector DbRow_t; 36 | typedef vector DbRows_t; 37 | 38 | typedef struct { 39 | bool is_self; 40 | bool is_group; 41 | uint32_t type; 42 | uint32_t ts; 43 | uint64_t id; 44 | string sender; 45 | string roomid; 46 | string content; 47 | string sign; 48 | string thumb; 49 | string extra; 50 | string xml; 51 | } WxMsg_t; 52 | 53 | typedef struct { 54 | string wxid; 55 | string name; 56 | string mobile; 57 | string home; 58 | } UserInfo_t; 59 | 60 | typedef struct { 61 | int32_t status; 62 | string result; 63 | } OcrResult_t; 64 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/ResponseCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import com.wechat.ferry.entity.IResponse; 4 | 5 | /** 6 | * 枚举-返回类状态码 7 | */ 8 | public enum ResponseCodeEnum implements IResponse { 9 | 10 | /** 11 | * 成功-200 12 | */ 13 | SUCCESS("200", "请求成功"), 14 | 15 | /** 16 | * 参数错误-400 17 | */ 18 | PARAM_ERROR("400", "参数错误"), 19 | 20 | /** 21 | * 401-身份验证失败 22 | */ 23 | NO_AUTH("401", "身份验证失败"), 24 | 25 | /** 26 | * 403-您无权访问此资源 27 | */ 28 | UNAUTHORIZED("403", "您无权访问此资源"), 29 | 30 | /** 31 | * 404-未找到该资源 32 | */ 33 | NOT_FOUND("404", "未找到该资源"), 34 | 35 | /** 36 | * 失败-500 37 | */ 38 | FAILED("500", "请求失败"), 39 | 40 | ; 41 | 42 | private final String code; 43 | private final String msg; 44 | 45 | ResponseCodeEnum(String code, String msg) { 46 | this.code = code; 47 | this.msg = msg; 48 | } 49 | 50 | @Override 51 | public String getCode() { 52 | return code; 53 | } 54 | 55 | @Override 56 | public String getMsg() { 57 | return msg; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return this.name() + "{" + code + '|' + msg + "}"; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/MsgCallbackTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-消息回调开关 12 | * 1-关闭 2-全部回调 3-发送成功才回调 13 | * 14 | * @author chandler 15 | * @date 2024/10/01 15:42 16 | */ 17 | @Getter 18 | @AllArgsConstructor 19 | public enum MsgCallbackTypeEnum { 20 | 21 | /** 22 | * 1-关闭 23 | */ 24 | CLOSE("1", "关闭"), 25 | 26 | /** 27 | * 2-全部回调 28 | */ 29 | ALL("2", "全部回调"), 30 | 31 | /** 32 | * 3-发送成功才回调 33 | */ 34 | SUCCESS("3", "发送成功才回调"), 35 | 36 | /** 37 | * 未匹配上 38 | */ 39 | UN_MATCH("", null), 40 | 41 | // 结束 42 | ; 43 | 44 | private final String code; 45 | private final String name; 46 | 47 | /** 48 | * map集合 key:code val:枚举 49 | */ 50 | public static final Map codeMap = 51 | Arrays.stream(values()).collect(Collectors.toMap(MsgCallbackTypeEnum::getCode, v -> v)); 52 | 53 | /** 54 | * 根据code获取枚举 55 | */ 56 | public static MsgCallbackTypeEnum getCodeMap(String code) { 57 | return codeMap.getOrDefault(code, UN_MATCH); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendXmlMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import javax.validation.constraints.NotNull; 5 | 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | 10 | /** 11 | * 请求入参-个微WCF发送XML消息 12 | * 13 | * @author chandler 14 | * @date 2024-10-04 23:11 15 | */ 16 | @Data 17 | @ApiModel(value = "wxPpWcfSendXmlMsgReq", description = "个微WCF发送XML消息请求入参") 18 | public class WxPpWcfSendXmlMsgReq { 19 | 20 | /** 21 | * 消息接收人 22 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 23 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 24 | */ 25 | @NotBlank(message = "消息接收人不能为空") 26 | @ApiModelProperty(value = "消息接收人") 27 | private String recipient; 28 | 29 | /** 30 | * XML报文内容 31 | */ 32 | @NotBlank(message = "XML报文内容不能为空") 33 | @ApiModelProperty(value = "XML报文内容") 34 | private String xmlContent; 35 | 36 | /** 37 | * 资源路径-封面图片路径 38 | */ 39 | @ApiModelProperty(value = "资源路径-封面图片路径") 40 | private String resourcePath; 41 | 42 | /** 43 | * XML类型,如:21 为小程序 44 | */ 45 | @NotNull(message = "XML类型不能为空") 46 | @ApiModelProperty(value = "XML类型") 47 | private String xmlType; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /clients/python/setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | from __future__ import print_function 6 | from setuptools import setup, find_packages 7 | 8 | import wcferry 9 | 10 | with open("README.md", "r", encoding="utf-8") as fh: 11 | long_description = fh.read() 12 | 13 | 14 | setup( 15 | name="wcferry", 16 | version=wcferry.__version__, 17 | author="Changhua", 18 | author_email="lichanghua0821@gmail.com", 19 | description="一个玩微信的工具", 20 | long_description=long_description, 21 | long_description_content_type="text/markdown", 22 | license="MIT", 23 | url="https://github.com/lich0821/WeChatFerry", 24 | python_requires=">=3.8", 25 | packages=find_packages(), 26 | include_package_data=True, 27 | install_requires=[ 28 | "setuptools", 29 | "grpcio-tools", 30 | "pynng", 31 | "requests", 32 | ], 33 | classifiers=[ 34 | "Environment :: Win32 (MS Windows)", 35 | "Intended Audience :: Developers", 36 | "Intended Audience :: Customer Service", 37 | "Topic :: Communications :: Chat", 38 | "Operating System :: Microsoft :: Windows", 39 | "Programming Language :: Python", 40 | ], 41 | project_urls={ 42 | "Documentation": "https://wechatferry.readthedocs.io/zh/latest/index.html", 43 | "GitHub": "https://github.com/lich0821/WeChatFerry/", 44 | }, 45 | ) 46 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendTextMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | 11 | /** 12 | * 请求入参-个微WCF发送文本消息 13 | * 14 | * @author chandler 15 | * @date 2024-10-02 20:33 16 | */ 17 | @Data 18 | @ApiModel(value = "wxPpWcfSendTextMsgReq", description = "个微WCF发送文本消息请求入参") 19 | public class WxPpWcfSendTextMsgReq { 20 | 21 | /** 22 | * 消息文本 23 | * 消息内容(如果是 @ 消息则需要有跟 @ 的人数量相同的 @) 24 | * 换行使用 `\\\\n` (单杠) 25 | */ 26 | @NotBlank(message = "消息文本不能为空") 27 | @ApiModelProperty(value = "消息文本") 28 | private String msgText; 29 | 30 | /** 31 | * 消息接收人 32 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 33 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 34 | */ 35 | @NotBlank(message = "消息接收人不能为空") 36 | @ApiModelProperty(value = "消息接收人") 37 | private String recipient; 38 | 39 | /** 40 | * 要艾特的用户 41 | * 群聊时要 @ 的人(私聊时为空字符串),多个用逗号分隔。 42 | * 艾特所有人用 notify@all(必须是群主或者管理员才有权限) 43 | */ 44 | @ApiModelProperty(value = "要艾特的用户") 45 | private List atUsers; 46 | 47 | /** 48 | * 是否艾特全体 49 | * 默认为false 50 | */ 51 | @ApiModelProperty(value = "是否艾特全体") 52 | private Boolean isAtAll = false; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/controller/WeChatMsgController.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import com.wechat.ferry.entity.TResponse; 10 | import com.wechat.ferry.enums.ResponseCodeEnum; 11 | import com.wechat.ferry.service.WeChatMsgService; 12 | 13 | import io.swagger.annotations.Api; 14 | import io.swagger.annotations.ApiOperation; 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * 控制层-微信消息处理 19 | * 20 | * @author chandler 21 | * @date 2024-10-01 14:25 22 | */ 23 | @Slf4j 24 | @RestController 25 | @RequestMapping("/wechat/msg") 26 | @Api(tags = "微信消息处理-接口") 27 | public class WeChatMsgController { 28 | 29 | private WeChatMsgService weChatMsgService; 30 | 31 | @Autowired 32 | public void setWeChatMsgService(WeChatMsgService weChatMsgService) { 33 | this.weChatMsgService = weChatMsgService; 34 | } 35 | 36 | @ApiOperation(value = "接收微信消息", notes = "receiveMsg") 37 | @PostMapping(value = "/receive") 38 | public TResponse receiveMsg(@RequestBody String jsonString) { 39 | weChatMsgService.receiveMsg(jsonString); 40 | return TResponse.ok(ResponseCodeEnum.SUCCESS); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.oas.annotations.EnableOpenApi; 10 | import springfox.documentation.service.ApiInfo; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | 14 | /** 15 | * 配置类-swagger 16 | * http://localhost:9201/swagger-ui/index.html 17 | * 18 | * @author chandler 19 | * @date 2024-09-24 22:13 20 | */ 21 | @EnableOpenApi 22 | @Configuration 23 | public class SwaggerConfig { 24 | 25 | @Bean 26 | public Docket api() { 27 | return new Docket(DocumentationType.SWAGGER_2).select() 28 | // 替换为您的Controller所在的包路径 29 | .apis(RequestHandlerSelectors.basePackage("com.wechat.ferry.controller")) 30 | // 地址 31 | .paths(PathSelectors.any()).build().apiInfo(apiInfo()); 32 | } 33 | 34 | private ApiInfo apiInfo() { 35 | return new ApiInfoBuilder() 36 | // 文档标题 37 | .title("WeChatFerry接口文档") 38 | // 文档路径 39 | .description("微信机器人底层框架接口文档") 40 | // 文档版本 41 | .version("1.0.0").build(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/po/wcf/Contact.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.po.wcf; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 实体类-联系人表 9 | * 10 | * @author chandler 11 | * @date 2024-12-27 09:47 12 | */ 13 | @Data 14 | @ApiModel(value = "Contact对象", description = "联系人表") 15 | public class Contact { 16 | 17 | /** 18 | * 用户名 19 | */ 20 | @ApiModelProperty(value = "用户名") 21 | private String userName; 22 | 23 | /** 24 | * 别名 25 | */ 26 | @ApiModelProperty(value = "别名") 27 | private String alias; 28 | 29 | /** 30 | * 昵称 31 | */ 32 | @ApiModelProperty(value = "昵称") 33 | private String nickname; 34 | 35 | /** 36 | * 删除标志 37 | */ 38 | @ApiModelProperty(value = "删除标志") 39 | private Integer delFlag; 40 | 41 | /** 42 | * 验证标志 43 | */ 44 | @ApiModelProperty(value = "验证标志") 45 | private Integer verifyFlag; 46 | 47 | /** 48 | * 备注 49 | */ 50 | @ApiModelProperty(value = "备注") 51 | private String remark; 52 | 53 | /** 54 | * 标签ID列表 55 | */ 56 | @ApiModelProperty(value = "标签ID列表") 57 | private String labelIdList; 58 | 59 | /** 60 | * 域名列表 61 | */ 62 | @ApiModelProperty(value = "域名列表") 63 | private String domainList; 64 | 65 | /** 66 | * 群类型 67 | */ 68 | @ApiModelProperty(value = "群类型") 69 | private Integer chatRoomType; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WxContactsTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 枚举-微信联系人类型 12 | * 13 | * @author chandler 14 | * @date 2024/10/02 18:35 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | public enum WxContactsTypeEnum { 19 | 20 | /** 21 | * 1-个微 22 | */ 23 | PERSON("1", "个微", ""), 24 | 25 | /** 26 | * 2-企微 27 | */ 28 | WORK("2", "企微", "@openim"), 29 | 30 | /** 31 | * 3-群组 32 | */ 33 | GROUP("3", "群组", "@chatroom"), 34 | 35 | /** 36 | * 4-官方杂号 37 | */ 38 | OFFICIAL_MIXED_NO("4", "官方杂号", null), 39 | 40 | /** 41 | * 5-公众号 42 | */ 43 | OFFICIAL_ACCOUNT("5", "公众号", "gh_"), 44 | 45 | /** 46 | * 未匹配上 47 | */ 48 | UN_MATCH("", null, null), 49 | 50 | // 结束 51 | ; 52 | 53 | private final String code; 54 | private final String name; 55 | private final String affix; 56 | 57 | /** 58 | * map集合 key:code val:枚举 59 | */ 60 | public static final Map codeMap = 61 | Arrays.stream(values()).collect(Collectors.toMap(WxContactsTypeEnum::getCode, v -> v)); 62 | 63 | /** 64 | * 根据code获取枚举 65 | */ 66 | public static WxContactsTypeEnum getCodeMap(String code) { 67 | return codeMap.getOrDefault(code, UN_MATCH); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /clients/gohttp/README.md: -------------------------------------------------------------------------------- 1 | # Wrest Chat 2 | 3 | 智能聊天助手,是一个通用的聊天辅助程序,通过 **Nanomsg 协议** 与聊天软件互通,内置 WEB 管理界面,可接入GPT、Gemini、星火、文心、混元、通义千问等大语言模型。目前已适配 *PC微信*,更多聊天软件适配中,敬请期待! 4 | 5 | > 为保证客户端纯粹性,此包仅提供 HTTP 和 Websocket 接口能力,完整功能可参考 [wrest-chat](https://github.com/opentdp/wrest-chat) 项目说明 6 | 7 | ## 功能特性 8 | 9 | 这里仅列举了一些主要的特性,其他信息请参阅[项目文档](https://docs.opentdp.org/#/wrest/)(by [KincaidYang](https://github.com/KincaidYang)) 10 | 11 | - 使用 Go 语言编写,无运行时依赖 12 | - 提供 HTTP 接口,便于对接各类编程语言 13 | - 提供 Websocket 接口,接收推送的新消息 14 | - 支持 HTTP/WS 接口授权,参见 [配置文件解析](https://docs.opentdp.org/#/wrest/配置文件解析) 15 | - 支持作为 SDK 使用,参见 [SDK模块说明](https://docs.opentdp.org/#/wrest/开发指南/SDK模块) 16 | - 内置 AI 机器人,参见 [BOT模块说明](https://docs.opentdp.org/#/wrest/开发指南/BOT模块) 17 | - 内置 Web 管理界面,可以管理机器人各项配置 18 | - 内置 Api 调试工具,所有接口都可以在线调试 19 | - 尽可能将消息中的 Xml 转为 Object,便于前端解析 20 | - 支持计划任务、外部指令、指令插件等扩展功能,详见 [wrest-plugin](https://github.com/opentdp/wrest-plugin) 21 | 22 | ## 代码提交 23 | 24 | 提交代码时请使用 `feat: something` 作为说明,支持的标识如下 25 | 26 | - `feat` 新功能(feature) 27 | - `fix` 错误修复 28 | - `docs` 文档更改(documentation) 29 | - `style` 格式(不影响代码含义的更改,空格、格式、缺少分号等) 30 | - `refactor` 重构(即不是新功能,也不是修补bug的代码变动) 31 | - `perf` 优化(提高性能的代码更改) 32 | - `test` 测试(添加缺失的测试或更正现有测试) 33 | - `chore` 构建过程或辅助工具的变动 34 | - `revert` 还原以前的提交 35 | 36 | ## 免责声明 37 | 38 | [WrestChat](https://github.com/opentdp/wrest-chat) 和 [WeChatFerry](https://github.com/lich0821/WeChatFerry) 是供学习交流的开源项目,代码及其制品仅供参考,不保证质量,不构成任何商业承诺或担保,不得用于商业或非法用途,使用者自行承担后果。 39 | 40 | ## 其他 41 | 42 | License [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.txt) 43 | 44 | Copyright (c) 2022 - 2024 OpenTDP 45 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfSendRichTextMsgReq.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.request; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | import javax.validation.constraints.NotBlank; 8 | 9 | /** 10 | * 请求入参-个微WCF发送富文本消息 11 | * 12 | * @author chandler 13 | * @date 2024-10-06 15:40 14 | */ 15 | @Data 16 | @ApiModel(value = "wxPpWcfSendRichTextMsgReq", description = "个微WCF发送富文本消息请求入参") 17 | public class WxPpWcfSendRichTextMsgReq { 18 | 19 | /** 20 | * 消息接收人 21 | * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) 22 | * 群聊为 roomid(xxxxxxxxxx@chatroom) 23 | */ 24 | @NotBlank(message = "消息接收人不能为空") 25 | @ApiModelProperty(value = "消息接收人") 26 | private String recipient; 27 | 28 | /** 29 | * 左下显示的名字 30 | */ 31 | @ApiModelProperty(value = "左下显示的名字") 32 | private String name; 33 | 34 | /** 35 | * 填公众号id 可以显示对应的头像(gh_ 开头的) 36 | */ 37 | @ApiModelProperty(value = "资源路径-封面图片路径") 38 | private String account; 39 | 40 | /** 41 | * 标题,最多两行 42 | */ 43 | @ApiModelProperty(value = "标题,最多两行") 44 | private String title; 45 | 46 | /** 47 | * 摘要,三行 48 | */ 49 | @ApiModelProperty(value = "摘要,三行") 50 | private String digest; 51 | 52 | /** 53 | * 点击后跳转的链接 54 | */ 55 | @ApiModelProperty(value = "点击后跳转的链接") 56 | private String jumpUrl; 57 | 58 | /** 59 | * 缩略图的链接 60 | */ 61 | @ApiModelProperty(value = "缩略图的链接") 62 | private String thumbnailUrl; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /WeChatFerry/com/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "spy_types.h" 6 | 7 | #define WECHAREXE L"WeChat.exe" 8 | #define WECHATWINDLL L"WeChatWin.dll" 9 | #define WCFSDKDLL L"sdk.dll" 10 | #define WCFSPYDLL L"spy.dll" 11 | #define WCFSPYDLL_DEBUG L"spy_debug.dll" 12 | 13 | #define GET_UINT64(addr) ((UINT64) * (UINT64 *)(addr)) 14 | #define GET_DWORD(addr) ((DWORD) * (UINT64 *)(addr)) 15 | #define GET_QWORD(addr) ((UINT64) * (UINT64 *)(addr)) 16 | #define GET_STRING(addr) ((CHAR *)(*(UINT64 *)(addr))) 17 | #define GET_WSTRING(addr) ((WCHAR *)(*(UINT64 *)(addr))) 18 | #define GET_STRING_FROM_P(addr) ((CHAR *)(addr)) 19 | #define GET_WSTRING_FROM_P(addr) ((WCHAR *)(addr)) 20 | 21 | typedef struct PortPath { 22 | int port; 23 | char path[MAX_PATH]; 24 | } PortPath_t; 25 | 26 | DWORD GetWeChatPid(); 27 | BOOL IsProcessX64(DWORD pid); 28 | int OpenWeChat(DWORD *pid); 29 | int GetWeChatVersion(wchar_t *version); 30 | size_t GetWstringByAddress(UINT64 address, wchar_t *buffer, UINT64 buffer_size); 31 | UINT32 GetMemoryIntByAddress(HANDLE hProcess, UINT64 address); 32 | std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, UINT64 address); 33 | std::wstring String2Wstring(std::string s); 34 | std::string Wstring2String(std::wstring ws); 35 | std::string GB2312ToUtf8(const char *gb2312); 36 | std::string GetStringByAddress(UINT64 address); 37 | std::string GetStringByStrAddr(UINT64 addr); 38 | std::string GetStringByWstrAddr(UINT64 addr); 39 | void DbgMsg(const char *zcFormat, ...); 40 | WxString *NewWxStringFromStr(const std::string &str); 41 | WxString *NewWxStringFromWstr(const std::wstring &ws); 42 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/config/WeChatFerryProperties.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.config; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.stereotype.Component; 8 | 9 | import lombok.Data; 10 | 11 | /** 12 | * 配置文件-WeChatFerry的配置文件 13 | * 14 | * @author chandler 15 | * @date 2024-09-21 21:35 16 | */ 17 | @Data 18 | @Component 19 | @ConfigurationProperties(prefix = "wechat.ferry") 20 | public class WeChatFerryProperties { 21 | 22 | /** 23 | * dll文件位置 24 | */ 25 | private String dllPath; 26 | 27 | /** 28 | * socket端口 29 | */ 30 | private Integer socketPort; 31 | 32 | /** 33 | * 联系人类型-官方杂号,禁止与其他分类重复(格式:代码|名称) 34 | * 使用时记得需要提取代码或者名称匹配 35 | */ 36 | private List contactsTypeMixed; 37 | 38 | /** 39 | * 联系人类型-公众号,禁止与其他分类重复(格式:代码|名称) 40 | * 使用时记得需要提取代码或者名称匹配 41 | */ 42 | private List contactsTypeOfficial; 43 | 44 | /** 45 | * 需要开启消息处理的群 46 | */ 47 | private List openMsgGroups; 48 | 49 | /** 50 | * 接收消息回调开关 51 | */ 52 | private Boolean receiveMsgCallbackSwitch = false; 53 | 54 | /** 55 | * 接收消息回调地址 56 | */ 57 | private List receiveMsgCallbackUrls; 58 | 59 | /** 60 | * 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调 61 | */ 62 | private String sendMsgCallbackFlag = "1"; 63 | 64 | /** 65 | * 发送消息回调地址 66 | */ 67 | private List sendMsgCallbackUrls; 68 | 69 | /** 70 | * 调用第三方服务客户端成功状态码 71 | */ 72 | private Map thirdPartyOkCodes; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /WeChatFerry/spy/user_info.cpp: -------------------------------------------------------------------------------- 1 | #include "user_info.h" 2 | #include "log.h" 3 | #include "util.h" 4 | 5 | extern UINT64 g_WeChatWinDllAddr; 6 | 7 | #define OS_USER_HOME 0x5932770 8 | #define OS_USER_WXID 0x595C270 9 | #define OS_USER_NAME 0x595C3D8 10 | #define OS_USER_MOBILE 0x595C318 11 | 12 | static char home[MAX_PATH] = { 0 }; 13 | 14 | string GetHomePath() 15 | { 16 | if (home[0] == 0) { 17 | string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; 18 | strncpy_s(home, path.c_str(), path.size()); 19 | } 20 | 21 | return string(home); 22 | } 23 | 24 | string GetSelfWxid() 25 | { 26 | UINT64 wxidType = 0; 27 | try { 28 | wxidType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); 29 | if (wxidType == 0xF) { 30 | return GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); 31 | } else { 32 | return GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); 33 | } 34 | } catch (...) { 35 | LOG_ERROR("wxid type: {:#x}", wxidType); 36 | LOG_BUFFER((uint8_t *)(g_WeChatWinDllAddr + OS_USER_WXID), 20); 37 | return "empty_wxid"; 38 | } 39 | } 40 | 41 | UserInfo_t GetUserInfo() 42 | { 43 | UserInfo_t ui; 44 | 45 | ui.wxid = GetSelfWxid(); 46 | 47 | UINT64 nameType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); 48 | if (nameType == 0xF) { 49 | ui.name = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME); 50 | } else { // 0x1F 51 | ui.name = GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME); 52 | } 53 | 54 | ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_MOBILE); 55 | ui.home = GetHomePath(); 56 | 57 | return ui; 58 | } 59 | -------------------------------------------------------------------------------- /clients/pyauto/wcfauto/event/event.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from abc import abstractmethod 3 | import logging 4 | from typing import Callable, Any 5 | from wcfauto.wcf import WcfV2 as Wcf 6 | from wcfauto.wcf import WxMsgV2 as WxMsg 7 | import asyncio 8 | 9 | 10 | class Event(object): 11 | _cbFunc = {} 12 | _loop_flag = False 13 | _thread_flag = False 14 | _inCache = False 15 | _message_callback_func_list = [] 16 | _loop = asyncio.get_event_loop() 17 | _filter_cache = {} 18 | _kind_dict = {'async': {}, 'universal': {}} 19 | 20 | def __init__(self): 21 | super(Event, self).__init__() 22 | self._message = None 23 | self._logger: logging = logging.getLogger() 24 | 25 | @abstractmethod 26 | def _add_callback(self, 27 | func: Callable[[Any], Any], 28 | bot: Wcf, 29 | kind: str, 30 | register_name: str, 31 | allow_other_rec: bool, 32 | judge_msg: Callable[[WxMsg], bool]): 33 | """ 34 | 消息处理函数加载器 35 | :param func: 装饰器装饰的函数 36 | :param bot: Wcf类 37 | :param kind: 装饰器所处类别, 分为异步和同步 38 | :param register_name: 装饰器所处类别下的函数类名(主要区分不同装饰器的作用, 以及为其中的 allow_other_rec参数做准备) 39 | :param allow_other_rec: 是否允许消息分发到其他不同类名装饰器(该参数只对同一个类 kind中的不同函数类装饰器有效 40 | 同步和异步装饰器属于互不干扰类型, 每个大类 kind中的参数只对该大类中的函数类装饰器有效) 41 | :param judge_msg: 判断是否为该装饰器所处理的消息的函数 42 | """ 43 | raise NotImplementedError 44 | 45 | @abstractmethod 46 | def _run_func(self): 47 | """ 48 | 消息分发器, 将消息发送给可接受消息的消息处理函数 49 | """ 50 | raise NotImplementedError 51 | 52 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/protoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This file acts as a drop-in replacement of binary protoc.exe. 3 | # It will use either Python-based protoc from grpcio-tools package, 4 | # or if it is not available, protoc.exe from path if found. 5 | 6 | import sys 7 | import os 8 | import os.path 9 | 10 | # Depending on how this script is run, we may or may not have PEP366 package name 11 | # available for relative imports. 12 | if not __package__: 13 | from proto._utils import invoke_protoc 14 | else: 15 | from .proto._utils import invoke_protoc 16 | 17 | if __name__ == '__main__': 18 | # Get path of the directory where this script is stored. 19 | if getattr(sys, 'frozen', False): 20 | mypath = os.path.dirname(sys.executable) # For pyInstaller 21 | else: 22 | mypath = os.path.dirname(__file__) 23 | 24 | # Avoid recursive calls to self 25 | env_paths = os.environ["PATH"].split(os.pathsep) 26 | if mypath in env_paths: 27 | env_paths.remove(mypath) 28 | os.environ["PATH"] = os.pathsep.join(env_paths) 29 | 30 | # Add argument for finding the nanopb generator when using --nanopb_out= 31 | # argument to protoc. 32 | if os.path.isfile(os.path.join(mypath, "protoc-gen-nanopb.exe")): 33 | protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb.exe") 34 | elif os.name == 'nt': 35 | protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb.bat") 36 | else: 37 | protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb") 38 | 39 | args = sys.argv[1:] 40 | 41 | if os.path.isfile(protoc_gen_nanopb): 42 | args = ['--plugin=protoc-gen-nanopb=%s' % protoc_gen_nanopb] + args 43 | 44 | status = invoke_protoc(['protoc'] + args) 45 | sys.exit(status) 46 | -------------------------------------------------------------------------------- /clients/go_wcf_http/go.mod: -------------------------------------------------------------------------------- 1 | module go_wechatFerry 2 | 3 | go 1.21.5 4 | 5 | require ( 6 | github.com/danbai225/go-logs v0.3.2 7 | github.com/gin-gonic/gin v1.10.0 8 | github.com/go-resty/resty/v2 v2.13.1 9 | github.com/google/uuid v1.6.0 10 | github.com/gorilla/websocket v1.5.0 11 | go.nanomsg.org/mangos/v3 v3.4.2 12 | google.golang.org/protobuf v1.34.2 13 | ) 14 | 15 | require ( 16 | github.com/Microsoft/go-winio v0.5.2 // indirect 17 | github.com/bytedance/sonic v1.11.6 // indirect 18 | github.com/bytedance/sonic/loader v0.1.1 // indirect 19 | github.com/cloudwego/base64x v0.1.4 // indirect 20 | github.com/cloudwego/iasm v0.2.0 // indirect 21 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 22 | github.com/gin-contrib/sse v0.1.0 // indirect 23 | github.com/go-playground/locales v0.14.1 // indirect 24 | github.com/go-playground/universal-translator v0.18.1 // indirect 25 | github.com/go-playground/validator/v10 v10.20.0 // indirect 26 | github.com/goccy/go-json v0.10.2 // indirect 27 | github.com/json-iterator/go v1.1.12 // indirect 28 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 29 | github.com/kpango/fastime v1.1.9 // indirect 30 | github.com/kpango/glg v1.6.15 // indirect 31 | github.com/leodido/go-urn v1.4.0 // indirect 32 | github.com/mattn/go-isatty v0.0.20 // indirect 33 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 34 | github.com/modern-go/reflect2 v1.0.2 // indirect 35 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 36 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 37 | github.com/ugorji/go/codec v1.2.12 // indirect 38 | golang.org/x/arch v0.8.0 // indirect 39 | golang.org/x/crypto v0.23.0 // indirect 40 | golang.org/x/net v0.25.0 // indirect 41 | golang.org/x/sys v0.20.0 // indirect 42 | golang.org/x/text v0.15.0 // indirect 43 | gopkg.in/yaml.v3 v3.0.1 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/nanopb/pb_common.h: -------------------------------------------------------------------------------- 1 | /* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. 2 | * These functions are rarely needed by applications directly. 3 | */ 4 | 5 | #ifndef PB_COMMON_H_INCLUDED 6 | #define PB_COMMON_H_INCLUDED 7 | 8 | #include "pb.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /* Initialize the field iterator structure to beginning. 15 | * Returns false if the message type is empty. */ 16 | bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); 17 | 18 | /* Get a field iterator for extension field. */ 19 | bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); 20 | 21 | /* Same as pb_field_iter_begin(), but for const message pointer. 22 | * Note that the pointers in pb_field_iter_t will be non-const but shouldn't 23 | * be written to when using these functions. */ 24 | bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); 25 | bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); 26 | 27 | /* Advance the iterator to the next field. 28 | * Returns false when the iterator wraps back to the first field. */ 29 | bool pb_field_iter_next(pb_field_iter_t *iter); 30 | 31 | /* Advance the iterator until it points at a field with the given tag. 32 | * Returns false if no such field exists. */ 33 | bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); 34 | 35 | /* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. 36 | * There can be only one extension range field per message. */ 37 | bool pb_field_iter_find_extension(pb_field_iter_t *iter); 38 | 39 | #ifdef PB_VALIDATE_UTF8 40 | /* Validate UTF-8 text string */ 41 | bool pb_validate_utf8(const char *s); 42 | #endif 43 | 44 | #ifdef __cplusplus 45 | } /* extern "C" */ 46 | #endif 47 | 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /WeChatFerry/WeChatFerry.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32802.440 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spy", "spy\spy.vcxproj", "{4DE80B82-5F6A-4C4C-9D16-1574308110FA}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdk", "sdk\sdk.vcxproj", "{ABFCB647-137F-478B-A73E-F0B1E3ADC215}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA} = {4DE80B82-5F6A-4C4C-9D16-1574308110FA} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|x64 = Debug|x64 16 | Dev|x64 = Dev|x64 17 | Release|x64 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Debug|x64.ActiveCfg = Debug|x64 21 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Debug|x64.Build.0 = Debug|x64 22 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Dev|x64.ActiveCfg = Dev|x64 23 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Dev|x64.Build.0 = Dev|x64 24 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x64.ActiveCfg = Release|x64 25 | {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x64.Build.0 = Release|x64 26 | {ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Debug|x64.ActiveCfg = Debug|x64 27 | {ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Dev|x64.ActiveCfg = Dev|x64 28 | {ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x64.ActiveCfg = Release|x64 29 | {ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x64.Build.0 = Release|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {76A678AA-570C-4CB7-B58F-3B2C170ACAC0} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /WeChatFerry/com/log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "log.h" 4 | #include "util.h" 5 | 6 | #define LOGGER_NAME "WCF" 7 | #define LOGGER_FILE_NAME "/logs/wcf.txt" 8 | #define LOGGER_MAX_SIZE 1024 * 1024 * 10 // 10M 9 | #define LOGGER_MAX_FILES 10 // 10 files 10 | 11 | void InitLogger(std::string path) 12 | { 13 | static std::shared_ptr logger = nullptr; 14 | if (logger != nullptr) { 15 | return; 16 | } 17 | // check and create logs folder 18 | std::filesystem::path logDir = std::filesystem::path(path) / "logs"; 19 | if (!std::filesystem::exists(logDir)) { 20 | std::filesystem::create_directory(logDir); 21 | } 22 | auto filename = std::filesystem::path(path + LOGGER_FILE_NAME).make_preferred().string(); 23 | try { 24 | logger = spdlog::rotating_logger_mt(LOGGER_NAME, filename, LOGGER_MAX_SIZE, LOGGER_MAX_FILES); 25 | } catch (const spdlog::spdlog_ex &ex) { 26 | MessageBox(NULL, String2Wstring(ex.what()).c_str(), L"Init LOGGER ERROR", 0); 27 | } 28 | 29 | spdlog::set_default_logger(logger); 30 | logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%s::%#::%!] %v"); 31 | #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG 32 | spdlog::set_level(spdlog::level::debug); 33 | logger->flush_on(spdlog::level::debug); 34 | #else 35 | logger->flush_on(spdlog::level::info); 36 | #endif 37 | LOG_DEBUG("InitLogger with debug level"); 38 | } 39 | 40 | #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG 41 | 42 | #define BUF_SIZE (1024 * 1024) 43 | static char buf[BUF_SIZE] = { 0 }; 44 | 45 | void log_buffer(uint8_t *buffer, size_t len) 46 | { 47 | size_t j = sprintf_s(buf, BUF_SIZE, "BUF@%p[%zd]: ", buffer, len); 48 | for (size_t i = 0; i < len; i++) { 49 | j += sprintf_s(buf + j, BUF_SIZE, "%02X ", buffer[i]); 50 | if (j > BUF_SIZE - 3) { 51 | break; 52 | } 53 | } 54 | LOG_DEBUG(buf); 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /clients/pyauto/wcfauto/msg_list.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # @Author: 小杨大帅哥 3 | import queue 4 | import time 5 | from threading import Thread 6 | 7 | class messageList(list): 8 | def __init__(self, *args, **kwargs): 9 | super(messageList, self).__init__(*args, **kwargs) 10 | self.__isRunning = True 11 | self.__th = None 12 | self.__time_step = 3*60 13 | self.__msg_queen = queue.Queue() 14 | self.start() 15 | 16 | def append(self, item) -> None: 17 | self.__isRunning = True 18 | if item['data'].get('msgid', None) is None: 19 | return 20 | super(messageList, self).append({str(item['data']['msgid']): item}) 21 | self.__msg_queen.put({'data': item, 'submit_time': time.time()}) 22 | 23 | def stop(self): 24 | self.__isRunning = False 25 | 26 | def find_msg(self, msgid): 27 | msgid = str(msgid) 28 | for msg_ele in self: 29 | if str(msgid) == str(list(msg_ele.keys())[0]): 30 | return msg_ele[msgid] 31 | return None 32 | 33 | def start(self): 34 | def _start(): 35 | while True: 36 | if self.__isRunning: 37 | try: 38 | new_data = self.__msg_queen.get() 39 | now = time.time() 40 | if now - new_data['submit_time'] >= self.__time_step: 41 | self.remove({str(new_data['data']['data']['msgid']): new_data['data']}) 42 | continue 43 | time.sleep(self.__time_step - (now - new_data['submit_time'])) 44 | self.remove({str(new_data['data']['data']['msgid']): new_data['data']}) 45 | except (queue.Empty, KeyboardInterrupt): 46 | pass 47 | self.__th = Thread(target=_start, name='run', daemon=True) 48 | self.__th.start() 49 | 50 | 51 | msg_list = messageList() 52 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | project = "WeChatFerry" 10 | copyright = "2023, Changhua" 11 | author = "Changhua" 12 | release = "3.9.2.23" 13 | 14 | # -- General configuration --------------------------------------------------- 15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 16 | 17 | templates_path = ["_templates"] 18 | exclude_patterns = [] 19 | 20 | language = "zh_CN" 21 | 22 | # -- Options for HTML output ------------------------------------------------- 23 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 24 | 25 | html_theme = "sphinx_rtd_theme" 26 | html_static_path = ["_static"] 27 | source_suffix = { 28 | ".rst": "restructuredtext", 29 | ".md": "markdown", 30 | } 31 | master_doc = "index" 32 | 33 | # -- General configuration 34 | extensions = [ 35 | "myst_parser", 36 | "autoapi.extension", 37 | "sphinx.ext.intersphinx", 38 | "sphinx.ext.napoleon", 39 | "sphinx_copybutton", 40 | ] 41 | 42 | intersphinx_mapping = { 43 | "python": ("https://docs.python.org/3/", None), 44 | "sphinx": ("https://www.sphinx-doc.org/en/master/", None), 45 | } 46 | intersphinx_disabled_domains = ["std"] 47 | 48 | 49 | myst_heading_anchors = 3 50 | myst_ref_domains = ["std", "py"] 51 | myst_enable_extensions = ["linkify", "colon_fence"] 52 | 53 | # Document Python Code 54 | autoapi_type = "python" 55 | autoapi_dirs = ["../../clients/python/wcferry"] 56 | autoapi_member_order = "groupwise" 57 | autoapi_options = ["members", "undoc-members", "show-inheritance", "show-module-summary", "imported-members"] 58 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/SDK.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 35 | 36 | 源文件 37 | 38 | 39 | 源文件 40 | 41 | 42 | 源文件 43 | 44 | 45 | 源文件 46 | 47 | 48 | 源文件 49 | 50 | 51 | 52 | 53 | 源文件 54 | 55 | 56 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/exception/BizException.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.exception; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.wechat.ferry.entity.IResponse; 7 | import com.wechat.ferry.enums.ResponseCodeEnum; 8 | 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | 12 | /** 13 | * 业务异常类 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class BizException extends RuntimeException { 18 | 19 | /** 20 | * 返回接口 21 | */ 22 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 23 | private final IResponse response; 24 | 25 | /** 26 | * 返回参数 27 | */ 28 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 29 | private transient Object[] arg; 30 | 31 | /** 32 | * 业务异常构造器 33 | * 34 | * @param msg 异常信息 35 | * @date 2021/11/24 23:58 36 | */ 37 | public BizException(String msg) { 38 | super(msg); 39 | this.response = ResponseCodeEnum.FAILED; 40 | this.arg = null; 41 | } 42 | 43 | /** 44 | * 业务异常构造器 45 | * 46 | * @param msg 异常信息 47 | * @param args 异常参数 48 | * @date 2021/11/24 23:58 49 | */ 50 | public BizException(String msg, Object... args) { 51 | super(msg); 52 | this.response = ResponseCodeEnum.FAILED; 53 | this.arg = args; 54 | } 55 | 56 | /** 57 | * 业务异常构造器 58 | * 59 | * @param t 异常响应码 60 | * @param args 异常参数 61 | * @date 2021/11/24 23:59 62 | */ 63 | public BizException(T t, Object... args) { 64 | super(Arrays.toString(args)); 65 | this.response = t; 66 | this.arg = args; 67 | } 68 | 69 | public BizException(BizException e) { 70 | this.response = e.getResponse(); 71 | this.arg = e.getArg(); 72 | } 73 | 74 | public BizException(ResponseCodeEnum t, String msg) { 75 | super(msg); 76 | this.response = t; 77 | this.arg = null; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WxContactsMixedEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | 8 | import org.springframework.util.ObjectUtils; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Getter; 12 | 13 | /** 14 | * 枚举-微信联系人-官方杂号 15 | * 16 | * @author chandler 17 | * @date 2024/12/24 16:01 18 | */ 19 | @Getter 20 | @AllArgsConstructor 21 | public enum WxContactsMixedEnum { 22 | 23 | /** 24 | * fmessage-朋友推荐消息 25 | */ 26 | F_MESSAGE("fmessage", "朋友推荐消息"), 27 | 28 | /** 29 | * medianote-语音记事本 30 | */ 31 | MEDIA_NOTE("medianote", "语音记事本"), 32 | 33 | /** 34 | * floatbottle-漂流瓶 35 | */ 36 | FLOAT_BOTTLE("floatbottle", "漂流瓶"), 37 | 38 | /** 39 | * filehelper-文件传输助手 40 | */ 41 | FILE_HELPER("filehelper", "文件传输助手"), 42 | 43 | /** 44 | * newsapp-新闻 45 | */ 46 | NEWS_APP("newsapp", "新闻"), 47 | 48 | /** 49 | * newsapp-微信团队 50 | */ 51 | WEI_XIN("weixin", "微信团队"), 52 | 53 | /** 54 | * 未匹配上 55 | */ 56 | UN_MATCH("", null), 57 | 58 | // 结束 59 | ; 60 | 61 | private final String code; 62 | private final String name; 63 | 64 | /** 65 | * map集合 key:code val:枚举 66 | */ 67 | public static final Map codeMap = 68 | Arrays.stream(values()).collect(Collectors.toMap(WxContactsMixedEnum::getCode, v -> v)); 69 | 70 | /** 71 | * 根据code获取枚举 72 | */ 73 | public static WxContactsMixedEnum getCodeMap(String code) { 74 | return codeMap.getOrDefault(code, UN_MATCH); 75 | } 76 | 77 | /** 78 | * map集合 key:code val:名称 79 | */ 80 | public static Map toCodeNameMap() { 81 | Map map = new HashMap<>(); 82 | for (WxContactsMixedEnum val : WxContactsMixedEnum.values()) { 83 | if (!ObjectUtils.isEmpty(val.getCode())) { 84 | map.put(val.getCode(), val.getName()); 85 | } 86 | } 87 | return map; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /clients/python/wcferry/wxmsg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | from datetime import datetime 5 | 6 | from wcferry import wcf_pb2 7 | 8 | 9 | class WxMsg(): 10 | """微信消息 11 | 12 | Attributes: 13 | type (int): 消息类型,可通过 `get_msg_types` 获取 14 | id (str): 消息 id 15 | xml (str): 消息 xml 部分 16 | sender (str): 消息发送人 17 | roomid (str): (仅群消息有)群 id 18 | content (str): 消息内容 19 | thumb (str): 视频或图片消息的缩略图路径 20 | extra (str): 视频或图片消息的路径 21 | """ 22 | 23 | def __init__(self, msg: wcf_pb2.WxMsg) -> None: 24 | self._is_self = msg.is_self 25 | self._is_group = msg.is_group 26 | self.type = msg.type 27 | self.id = msg.id 28 | self.ts = msg.ts 29 | self.sign = msg.sign 30 | self.xml = msg.xml 31 | self.sender = msg.sender 32 | self.roomid = msg.roomid 33 | self.content = msg.content 34 | self.thumb = msg.thumb 35 | self.extra = msg.extra 36 | 37 | def __str__(self) -> str: 38 | s = f"{'自己发的:' if self._is_self else ''}" 39 | s += f"{self.sender}[{self.roomid}]|{self.id}|{datetime.fromtimestamp(self.ts)}|{self.type}|{self.sign}" 40 | s += f"\n{self.xml.replace(chr(10), '').replace(chr(9),'')}\n" 41 | s += self.content 42 | s += f"\n{self.thumb}" if self.thumb else "" 43 | s += f"\n{self.extra}" if self.extra else "" 44 | return s 45 | 46 | def from_self(self) -> bool: 47 | """是否自己发的消息""" 48 | return self._is_self == 1 49 | 50 | def from_group(self) -> bool: 51 | """是否群聊消息""" 52 | return self._is_group 53 | 54 | def is_at(self, wxid) -> bool: 55 | """是否被 @:群消息,在 @ 名单里,并且不是 @ 所有人""" 56 | if not self.from_group(): 57 | return False # 只有群消息才能 @ 58 | 59 | if not re.findall(f"[\s|\S]*({wxid})[\s|\S]*", self.xml): 60 | return False # 不在 @ 清单里 61 | 62 | if re.findall(r"@(?:所有人|all|All)", self.content): 63 | return False # 排除 @ 所有人 64 | 65 | return True 66 | 67 | def is_text(self) -> bool: 68 | """是否文本消息""" 69 | return self.type == 1 70 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WxContactsOfficialEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | 8 | import org.springframework.util.ObjectUtils; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Getter; 12 | 13 | /** 14 | * 枚举-微信联系人-公众号 15 | * 16 | * @author chandler 17 | * @date 2024/12/24 16:36 18 | */ 19 | @Getter 20 | @AllArgsConstructor 21 | public enum WxContactsOfficialEnum { 22 | 23 | /** 24 | * wxid_2876568766325-应用宝-yingyongbao 25 | */ 26 | YING_YONG_BAO("wxid_2876568766325", "应用宝"), 27 | 28 | /** 29 | * wxid_2965349653612-i黑马-iheima 30 | */ 31 | I_HEI_MA("wxid_2965349653612", "i黑马"), 32 | 33 | /** 34 | * wxid_4302923029011-丁香医生-DingXiangYiSheng 35 | */ 36 | DING_XIANG_YI_SHENG("wxid_4302923029011", "丁香医生"), 37 | 38 | /** 39 | * mphelper-公众平台安全助手 40 | */ 41 | MP_HELPER("mphelper", "公众平台安全助手"), 42 | 43 | /** 44 | * weixinguanhaozhushou-微信公众平台-weixingongzhong 45 | */ 46 | WEI_XIN_GONG_ZHONG("weixinguanhaozhushou", "微信公众平台"), 47 | 48 | /** 49 | * 未匹配上 50 | */ 51 | UN_MATCH("", null), 52 | 53 | // 结束 54 | ; 55 | 56 | private final String code; 57 | private final String name; 58 | 59 | /** 60 | * map集合 key:code val:枚举 61 | */ 62 | public static final Map codeMap = 63 | Arrays.stream(values()).collect(Collectors.toMap(WxContactsOfficialEnum::getCode, v -> v)); 64 | 65 | /** 66 | * 根据code获取枚举 67 | */ 68 | public static WxContactsOfficialEnum getCodeMap(String code) { 69 | return codeMap.getOrDefault(code, UN_MATCH); 70 | } 71 | 72 | /** 73 | * map集合 key:code val:名称 74 | */ 75 | public static Map toCodeNameMap() { 76 | Map map = new HashMap<>(); 77 | for (WxContactsOfficialEnum val : WxContactsOfficialEnum.values()) { 78 | if (!ObjectUtils.isEmpty(val.getCode())) { 79 | map.put(val.getCode(), val.getName()); 80 | } 81 | } 82 | return map; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /clients/java/wcferry/README.MD: -------------------------------------------------------------------------------- 1 | # WeChatFerry Java 客户端 2 | 3 | ⚠️ **只支持 Windows** ⚠️ 4 | 5 | ## 快速开始 6 | 7 | * 下载 [最新发布的文件](https://github.com/lich0821/WeChatFerry/releases/latest),解压到 `WeChatFerry\clients\java\wcferry\dll`目录下。 8 | 9 | * 使用惯用 IDE,打开工程,编译,运行。 10 | 11 | ## 从头开始 12 | 13 | 【添加 `wcferry` 依赖】是必须要执行的,工程里不带这几个文件;如果开发新功能,一般需要修改 `wcf.proto`,从而需要重新生成 PB 文件;其他的,一般不需要关注。 14 | 15 | ### 添加 `nng` 依赖 16 | 17 | * 新建一个 `libs` 目录 18 | * 参考 [nng-java](https://github.com/voutilad/nng-java),编译 jar 包(`nng-java-1.4.0-SNAPSHOT.jar`) 19 | * 添加依赖 `nng-java-1.4.0-SNAPSHOT.jar` 20 | 21 | ```groovy 22 | implementation 'net.java.dev.jna:jna:5.6.0' 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | ``` 25 | 26 | #### 编译 / 安装 `mbedtls` 27 | 28 | * 下载 [Mbed TLS](https://github.com/Mbed-TLS/mbedtls) 代码并解压 29 | * 新建 `build` 目录,生成 VS 工程 30 | 31 | ```sh 32 | cd build 33 | cmake .. 34 | ``` 35 | 36 | * 以管理员身份运行 VS2019,打开 `mbed TLS.sln`,选择 `Release` `x64`,编译即可 37 | * [参考](https://github.com/Mbed-TLS/mbedtls) 38 | 39 | #### 编译 / 安装 `nng` 40 | 41 | * 下载 [nng](https://github.com/nanomsg/nng) 代码并解压 42 | * 新建 `build` 目录,生成 VS 工程 43 | 44 | ```sh 45 | cmake -DBUILD_SHARED_LIBS=ON -DNNG_ENABLE_TLS=ON -DMBEDTLS_ROOT_DIR="C:\Program Files (x86)\mbed TLS" .. 46 | ``` 47 | 48 | * 打开 `nng.sln`,选择 `Release` `x64`,编译即可 49 | * [参考](https://github.com/nanomsg/nng/issues/953) 50 | 51 | ### 添加 PB 依赖 52 | 53 | ```groovy 54 | implementation 'com.google.protobuf:protobuf-java:3.22.2' // 版本需要和 protoc 一致 55 | ``` 56 | 57 | #### 重新生成 PB 文件 58 | 59 | 参考 [Protocol Buffer Basics: Java](https://protobuf.dev/getting-started/javatutorial/#problem-domain)。 60 | * 下载 `protoc` 工具(注意,需要与 `com.google.protobuf:protobuf-java` 一致): 61 | 这里下载 [protoc-22.2](https://github.com/protocolbuffers/protobuf/releases/download/v22.2/protoc-22.2-win64.zip),解压,并将所在路径加入环境变量(以便随处可用)。 62 | 63 | * 编译 PB 文件 64 | 65 | ```sh 66 | # 调整项目路径 67 | cd C:/Projs/WeChatFerry/java/wcferry/src/main/java 68 | protoc.exe --java_out=. --proto_path=C:\Projs\WeChatFerry\WeChatFerry\rpc\proto wcf.proto 69 | ``` 70 | 71 | ### 添加 `dll` 依赖 72 | 73 | 将 `spy.dll` 、 `spy_debug.dll`、 `spy.dll` 添加到 `WeChatFerry\clients\java\wcferry\dll`。 74 | 75 | ### 其他问题 76 | 77 | ### 乱码 78 | 79 | 参考 [这篇文章](https://www.quanxiaoha.com/idea/idea-chinese-garbled-code.html)。 80 | -------------------------------------------------------------------------------- /clients/python/README.MD: -------------------------------------------------------------------------------- 1 | # WeChatFerry Python 客户端 2 | [![PyPi](https://img.shields.io/pypi/v/wcferry.svg)](https://pypi.python.org/pypi/wcferry) [![Downloads](https://static.pepy.tech/badge/wcferry)](https://pypi.python.org/pypi/wcferry) [![Documentation Status](https://readthedocs.org/projects/wechatferry/badge/?version=latest)](https://wechatferry.readthedocs.io/zh/latest/?badge=latest) 3 | 4 | |[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/YvgFFhF6D-79kXDzRqtg6w)| 5 | |:-:|:-:|:-:| 6 | 7 | 🤖示例机器人框架:[WeChatRobot](https://github.com/lich0821/WeChatRobot)。 8 | 9 | |![碲矿](https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/TEQuant.jpg)|![赞赏](https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg)| 10 | |:-:|:-:| 11 | |后台回复 `WCF` 加群交流|如果你觉得有用| 12 | 13 | ## 快速开始 14 | ```sh 15 | pip install --upgrade wcferry 16 | ``` 17 | 18 | ### Demo: 19 | 参考 [WeChatRobot](https://github.com/lich0821/WeChatRobot) 和上面的文档。 20 | 21 | ## 一起开发 22 | ### 配置环境 23 | ```sh 24 | # 创建虚拟环境 25 | python -m venv .env 26 | # 激活虚拟环境 27 | source .env/Scripts/activate 28 | # 升级 pip 29 | pip install --upgrade pip 30 | # 安装依赖包 31 | pip install grpcio-tools pynng 32 | ``` 33 | 34 | ### 重新生成 PB 文件 35 | ```sh 36 | # CMD 37 | cd clients\python\wcferry 38 | python -m grpc_tools.protoc --python_out=. --proto_path=..\..\..\WeChatFerry\rpc\proto\ wcf.proto 39 | 40 | # GitBash 41 | cd clients/python/wcferry 42 | python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc/proto/ wcf.proto 43 | ``` 44 | 45 | ## 版本更新 46 | 47 | ### v39.3.3.1 48 | * 修复 `send_xml` 49 | 50 |
点击查看更多 51 | 52 | 版本号:`w.x.y.z`。 53 | 54 | 其中: 55 | * `w` 是微信的大版本号,如 `37` (3.7.a.a), `38` (3.8.a.a), `39` (3.9.a.a) 56 | * `x` 是适配的微信的小版本号,从 0 开始 57 | * `y` 是 `WeChatFerry` 的版本,从 0 开始 58 | * `z` 是各客户端的版本,从 0 开始 59 | 60 | 功能: 61 | 62 | * 查询登录状态 63 | * 获取登录账号信息 64 | * 获取消息类型 65 | * 获取联系人 66 | * 获取可查询数据库 67 | * 获取数据库所有表 68 | * 获取语音消息 69 | * 发送文本消息(可 @) 70 | * 发送图片消息 71 | * 发送文件消息 72 | * 发送卡片消息 73 | * 发送 XML 消息 74 | * 发送 GIF 消息 75 | * 拍一拍群友 76 | * 转发消息 77 | * 开启接收消息 78 | * 关闭接收消息 79 | * 查询数据库 80 | * 获取朋友圈消息 81 | * 下载图片 82 | * 解密图片 83 | * 添加群成员 84 | * 删除群成员 85 | * 邀请群成员 86 | 87 |
88 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/proto/_utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | import os.path 4 | 5 | def has_grpcio_protoc(): 6 | # type: () -> bool 7 | """ checks if grpcio-tools protoc is installed""" 8 | 9 | try: 10 | import grpc_tools.protoc 11 | except ImportError: 12 | return False 13 | return True 14 | 15 | 16 | def invoke_protoc(argv): 17 | # type: (list) -> typing.Any 18 | """ 19 | Invoke protoc. 20 | 21 | This routine will use grpcio-provided protoc if it exists, 22 | using system-installed protoc as a fallback. 23 | 24 | Args: 25 | argv: protoc CLI invocation, first item must be 'protoc' 26 | """ 27 | 28 | # Add current directory to include path if nothing else is specified 29 | if not [x for x in argv if x.startswith('-I')]: 30 | argv.append("-I.") 31 | 32 | # Add default protoc include paths 33 | nanopb_include = os.path.dirname(os.path.abspath(__file__)) 34 | argv.append('-I' + nanopb_include) 35 | 36 | if has_grpcio_protoc(): 37 | import grpc_tools.protoc as protoc 38 | import pkg_resources 39 | proto_include = pkg_resources.resource_filename('grpc_tools', '_proto') 40 | argv.append('-I' + proto_include) 41 | 42 | return protoc.main(argv) 43 | else: 44 | return subprocess.call(argv) 45 | 46 | def print_versions(): 47 | try: 48 | if has_grpcio_protoc(): 49 | import grpc_tools.protoc 50 | sys.stderr.write("Using grpcio-tools protoc from " + grpc_tools.protoc.__file__ + "\n") 51 | else: 52 | sys.stderr.write("Using protoc from system path\n") 53 | 54 | invoke_protoc(['protoc', '--version']) 55 | except Exception as e: 56 | sys.stderr.write("Failed to determine protoc version: " + str(e) + "\n") 57 | 58 | try: 59 | import google.protobuf 60 | sys.stderr.write("Python version " + sys.version + "\n") 61 | sys.stderr.write("Using python-protobuf from " + google.protobuf.__file__ + "\n") 62 | sys.stderr.write("Python-protobuf version: " + google.protobuf.__version__ + "\n") 63 | except Exception as e: 64 | sys.stderr.write("Failed to determine python-protobuf version: " + str(e) + "\n") 65 | 66 | if __name__ == '__main__': 67 | print_versions() 68 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/dto/WxPpMsgDTO.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.dto; 2 | 3 | import com.alibaba.fastjson2.JSONObject; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * DTO-个微信消息 11 | * 12 | * @author chandler 13 | * @date 2024-09-26 19:56 14 | */ 15 | @Data 16 | public class WxPpMsgDTO { 17 | 18 | /** 19 | * 是否自己发送的 20 | */ 21 | @ApiModelProperty(value = "是否自己发送的") 22 | private Boolean isSelf; 23 | 24 | /** 25 | * 是否群消息 26 | */ 27 | @ApiModelProperty(value = "是否群消息") 28 | private Boolean isGroup; 29 | 30 | /** 31 | * 消息id 32 | */ 33 | @ApiModelProperty(value = "消息id") 34 | private Long id; 35 | 36 | /** 37 | * 消息类型 38 | */ 39 | @ApiModelProperty(value = "消息类型") 40 | private Integer type; 41 | 42 | /** 43 | * 消息类型 44 | */ 45 | @ApiModelProperty(value = "消息类型") 46 | private Integer ts; 47 | 48 | /** 49 | * 群id(如果是群消息的话) 50 | */ 51 | @ApiModelProperty(value = "群id(如果是群消息的话)") 52 | private String roomId; 53 | 54 | /** 55 | * 消息内容 56 | */ 57 | @ApiModelProperty(value = "消息内容") 58 | private String content; 59 | 60 | /** 61 | * 转为JSON的内容 62 | * 对应DLL中的content原始内容 63 | */ 64 | @ApiModelProperty(value = "转为JSON的内容") 65 | private JSONObject jsonContent; 66 | 67 | /** 68 | * 引用内容 69 | * 对应DLL中的content原始内容 70 | */ 71 | @JsonIgnore 72 | @ApiModelProperty(value = "消息内容XML") 73 | private String quoteContent; 74 | 75 | /** 76 | * 消息发送者 77 | */ 78 | @ApiModelProperty(value = "消息发送者") 79 | private String sender; 80 | 81 | /** 82 | * 签名 83 | */ 84 | @ApiModelProperty(value = "签名") 85 | private String sign; 86 | 87 | /** 88 | * 缩略图 89 | */ 90 | @ApiModelProperty(value = "缩略图") 91 | private String thumb; 92 | 93 | /** 94 | * 附加内容 95 | */ 96 | @ApiModelProperty(value = "附加内容") 97 | private String extra; 98 | 99 | /** 100 | * 消息xml 101 | */ 102 | @ApiModelProperty(value = "消息xml") 103 | private String xml; 104 | 105 | } 106 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/vo/response/WxPpWcfContactsResp.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity.vo.response; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * 请求出参-个微WCF联系人 9 | * 10 | * @author chandler 11 | * @date 2024/10/02 17:01 12 | */ 13 | @Data 14 | @ApiModel(value = "wxPpWcfContactsResp", description = "个微WCF联系人查询请求出参") 15 | public class WxPpWcfContactsResp { 16 | 17 | /** 18 | * 微信内部识别号UID 19 | * 原始微信账号ID,以"wxid_"开头,初始默认的微信ID=微信号。 20 | */ 21 | @ApiModelProperty(value = "微信内部识别号UID") 22 | private String weChatUid; 23 | 24 | /** 25 | * 微信号 26 | */ 27 | @ApiModelProperty(value = "微信号") 28 | private String weChatNo; 29 | 30 | /** 31 | * 微信昵称 32 | */ 33 | @ApiModelProperty(value = "微信昵称") 34 | private String weChatNickname; 35 | 36 | /** 37 | * 联系人备注 38 | */ 39 | @ApiModelProperty(value = "联系人备注") 40 | private String weChatRemark; 41 | 42 | /** 43 | * 国家 44 | */ 45 | @ApiModelProperty(value = "国家") 46 | private String country; 47 | 48 | /** 49 | * 国家拼音 50 | */ 51 | @ApiModelProperty(value = "国家拼音") 52 | private String countryPinyin; 53 | 54 | /** 55 | * 省/州 56 | */ 57 | @ApiModelProperty(value = "省/州") 58 | private String province; 59 | 60 | /** 61 | * 省/州拼音 62 | */ 63 | @ApiModelProperty(value = "省/州拼音") 64 | private String provincePinyin; 65 | 66 | /** 67 | * 城市 68 | */ 69 | @ApiModelProperty(value = "城市") 70 | private String city; 71 | 72 | /** 73 | * 城市拼音 74 | */ 75 | @ApiModelProperty(value = "城市拼音") 76 | private String cityPinyin; 77 | 78 | /** 79 | * 性别 80 | */ 81 | @ApiModelProperty(value = "性别") 82 | private String sex; 83 | 84 | /** 85 | * 性别-翻译 86 | */ 87 | @ApiModelProperty(value = "性别-翻译") 88 | private String sexLabel; 89 | 90 | /** 91 | * 类型 92 | */ 93 | @ApiModelProperty(value = "类型") 94 | private String type; 95 | 96 | /** 97 | * 类型-翻译 98 | */ 99 | @ApiModelProperty(value = "类型-翻译") 100 | private String typeLabel; 101 | 102 | } 103 | -------------------------------------------------------------------------------- /WeChatFerry/sdk/sdk.cpp: -------------------------------------------------------------------------------- 1 | #include "Shlwapi.h" 2 | #include "framework.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include "injector.h" 8 | #include "sdk.h" 9 | #include "util.h" 10 | 11 | static BOOL injected = false; 12 | static HANDLE wcProcess = NULL; 13 | static HMODULE spyBase = NULL; 14 | static WCHAR spyDllPath[MAX_PATH] = { 0 }; 15 | 16 | static int GetDllPath(bool debug, wchar_t *dllPath) 17 | { 18 | GetModuleFileName(GetModuleHandle(WCFSDKDLL), dllPath, MAX_PATH); 19 | PathRemoveFileSpec(dllPath); 20 | if (debug) { 21 | PathAppend(dllPath, WCFSPYDLL_DEBUG); 22 | } else { 23 | PathAppend(dllPath, WCFSPYDLL); 24 | } 25 | 26 | if (!PathFileExists(dllPath)) { 27 | MessageBox(NULL, dllPath, L"文件不存在", 0); 28 | return ERROR_FILE_NOT_FOUND; 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | int WxInitSDK(bool debug, int port) 35 | { 36 | int status = 0; 37 | DWORD wcPid = 0; 38 | 39 | status = GetDllPath(debug, spyDllPath); 40 | if (status != 0) { 41 | return status; 42 | } 43 | 44 | status = OpenWeChat(&wcPid); 45 | if (status != 0) { 46 | MessageBox(NULL, L"打开微信失败", L"WxInitSDK", 0); 47 | return status; 48 | } 49 | 50 | if (!IsProcessX64(wcPid)) { 51 | MessageBox(NULL, L"只支持 64 位微信", L"WxInitSDK", 0); 52 | return -1; 53 | } 54 | 55 | Sleep(2000); // 等待微信打开 56 | wcProcess = InjectDll(wcPid, spyDllPath, &spyBase); 57 | if (wcProcess == NULL) { 58 | MessageBox(NULL, L"注入失败", L"WxInitSDK", 0); 59 | return -1; 60 | } 61 | 62 | PortPath_t pp = { 0 }; 63 | pp.port = port; 64 | sprintf_s(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); 65 | 66 | if (!CallDllFuncEx(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) { 67 | MessageBox(NULL, L"初始化失败", L"WxInitSDK", 0); 68 | return -1; 69 | } 70 | 71 | injected = true; 72 | return 0; 73 | } 74 | 75 | int WxDestroySDK() 76 | { 77 | if (!injected) { 78 | return -1; 79 | } 80 | 81 | if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "CleanupSpy", NULL)) { 82 | return -2; 83 | } 84 | 85 | if (!EjectDll(wcProcess, spyBase)) { 86 | return -3; // TODO: Unify error codes 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.exception; 2 | 3 | import java.text.MessageFormat; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.web.bind.MethodArgumentNotValidException; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | import com.wechat.ferry.entity.TResponse; 13 | import com.wechat.ferry.enums.ResponseCodeEnum; 14 | 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * 全局统一异常 19 | * 20 | * @author Simith 21 | * @date 2021/11/23 23:20 22 | */ 23 | @Slf4j 24 | @Order(-1) 25 | // 表示当前类为全局异常处理器 26 | @RestControllerAdvice 27 | public class GlobalExceptionHandler { 28 | 29 | /** 30 | * 通用异常-系统级别未知异常 31 | * 32 | * @param e 异常信息 33 | * @return TResponse 34 | * @author Simith 35 | * @date 2021/11/23 23:22 36 | */ 37 | @ExceptionHandler(Exception.class) 38 | public TResponse handleException(Exception e) { 39 | log.error("全局异常信息 ex={}", e.getMessage(), e); 40 | // 打印堆栈信息 41 | e.printStackTrace(); 42 | String message = ResponseCodeEnum.FAILED.getMsg() + ":" + e.getMessage(); 43 | return new TResponse<>(ResponseCodeEnum.FAILED, message); 44 | } 45 | 46 | /** 47 | * 参数异常 48 | * 49 | * @author chandler 50 | * @date 2023/4/3 23:26 51 | * @param request 请求入参 52 | * @param e 异常消息 53 | * @return TResponse 返回体 54 | */ 55 | @ExceptionHandler(value = {MethodArgumentNotValidException.class}) 56 | public TResponse handleValidationException(HttpServletRequest request, MethodArgumentNotValidException e) { 57 | log.error("[请求体参数校验不通过]", e); 58 | String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); 59 | return new TResponse<>(ResponseCodeEnum.PARAM_ERROR, message); 60 | } 61 | 62 | /** 63 | * 自定义错误异常 64 | * 65 | * @param e 异常信息 66 | * @return TResponse 67 | * @author Simith 68 | * @date 2021/11/23 23:43 69 | */ 70 | @ExceptionHandler(BizException.class) 71 | public TResponse handleBizException(BizException e) { 72 | // 打印错误 73 | e.printStackTrace(); 74 | // 获取错误码 75 | String message = MessageFormat.format(e.getMessage(), e.getArg()); 76 | return new TResponse<>(ResponseCodeEnum.FAILED, message); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /WeChatFerry/spy/spy.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // ����(���壬�й�) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) 19 | LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED 20 | #pragma code_page(936) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION 39,3,5,0 55 | PRODUCTVERSION 3,9,11,25 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x2L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "080404b0" 69 | BEGIN 70 | VALUE "CompanyName", "WeChatFerry" 71 | VALUE "FileDescription", "WeChatFerry" 72 | VALUE "FileVersion", "39.3.5.0" 73 | VALUE "InternalName", "spy.dll" 74 | VALUE "LegalCopyright", "Copyright (C) 2023" 75 | VALUE "OriginalFilename", "spy.dll" 76 | VALUE "ProductName", "WeChatFerry" 77 | VALUE "ProductVersion", "3.9.11.25" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x804, 1200 83 | END 84 | END 85 | 86 | #endif // ����(���壬�й�) resources 87 | ///////////////////////////////////////////////////////////////////////////// 88 | 89 | 90 | 91 | #ifndef APSTUDIO_INVOKED 92 | ///////////////////////////////////////////////////////////////////////////// 93 | // 94 | // Generated from the TEXTINCLUDE 3 resource. 95 | // 96 | 97 | 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | 101 | -------------------------------------------------------------------------------- /clients/python/test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | from queue import Empty 6 | from threading import Thread 7 | from time import sleep 8 | 9 | from wcferry import Wcf 10 | 11 | logging.basicConfig(level='DEBUG', format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") 12 | LOG = logging.getLogger("Demo") 13 | 14 | 15 | def process_msg(wcf: Wcf): 16 | """处理接收到的消息""" 17 | while wcf.is_receiving_msg(): 18 | try: 19 | msg = wcf.get_msg() 20 | LOG.info(msg) # 简单打印 21 | except Empty: 22 | continue # Empty message 23 | except Exception as e: 24 | LOG.error(f"Receiving message error: {e}") 25 | 26 | 27 | def main(): 28 | LOG.info("Start demo...") 29 | wcf = Wcf(debug=True) # 默认连接本地服务 30 | 31 | sleep(5) # 等微信加载好,以免信息显示异常 32 | LOG.info(f"已经登录: {True if wcf.is_login() else False}") 33 | LOG.info(f"wxid: {wcf.get_self_wxid()}") 34 | 35 | # 允许接收消息 36 | # wcf.enable_recv_msg(LOG.info) # deprecated 37 | 38 | # 允许接收消息 39 | wcf.enable_receiving_msg(pyq=True) # 同时允许接收朋友圈消息 40 | Thread(target=process_msg, name="GetMessage", args=(wcf,), daemon=True).start() 41 | 42 | # wcf.disable_recv_msg() # 当需要停止接收消息时调用 43 | sleep(5) 44 | ret = wcf.send_text("Hello world.", "filehelper") 45 | LOG.info(f"send_text: {ret}") 46 | 47 | sleep(5) 48 | # 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\) 49 | ret = wcf.send_image("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper") 50 | LOG.info(f"send_image: {ret}") 51 | 52 | sleep(5) 53 | # 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\) 54 | ret = wcf.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper") 55 | LOG.info(f"send_file: {ret}") 56 | 57 | sleep(5) 58 | LOG.info(f"Message types:\n{wcf.get_msg_types()}") 59 | LOG.info(f"Contacts:\n{wcf.get_contacts()}") 60 | 61 | sleep(5) 62 | LOG.info(f"DBs:\n{wcf.get_dbs()}") 63 | LOG.info(f"Tables:\n{wcf.get_tables('db')}") 64 | LOG.info(f"Results:\n{wcf.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}") 65 | 66 | # 需要真正的 V3、V4 信息 67 | # wcf.accept_new_friend("v3", "v4") 68 | 69 | # 添加群成员,填写正确的群 ID 和成员 wxid 70 | # ret = wcf.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 71 | # LOG.info(f"add_chatroom_members: {ret}") 72 | 73 | # 删除群成员,填写正确的群 ID 和成员 wxid 74 | # ret = wcf.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 75 | # LOG.info(f"add_chatroom_members: {ret}") 76 | 77 | sleep(5) 78 | wcf.refresh_pyq(0) # 刷新朋友圈第一页 79 | # wcf.refresh_pyq(id) # 从 id 开始刷新朋友圈 80 | 81 | return wcf 82 | 83 | 84 | if __name__ == "__main__": 85 | wcf = main() 86 | 87 | # 一直运行 88 | wcf.keep_running() 89 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgFactory.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.strategy.msg.receive; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.annotation.Resource; 7 | 8 | import org.springframework.beans.factory.InitializingBean; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.stereotype.Component; 11 | 12 | import com.wechat.ferry.enums.ReceiveMsgChannelEnum; 13 | import com.wechat.ferry.exception.BizException; 14 | 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * 策略Context-消息处理-接收消息 19 | * 可以切换策略的Context(这里实际是Factory)类 20 | * 21 | * @author chandler 22 | * @date 2024-12-25 14:08 23 | */ 24 | @Slf4j 25 | @Component 26 | public class ReceiveMsgFactory implements InitializingBean { 27 | 28 | private static final Map strategyContainerMap = new HashMap<>(); 29 | 30 | /** 31 | * spring的上下文 32 | */ 33 | @Resource 34 | private ApplicationContext applicationContext; 35 | 36 | /** 37 | * 实现InitializingBean的方法会在启动的时候执行afterPropertiesSet()方法 38 | * 将Strategy的类都按照定义好的规则(fetchKey),放入Map中 39 | */ 40 | @Override 41 | public void afterPropertiesSet() { 42 | // 初始化时把所有的策略bean放进ioc,用于使用的时候获取 43 | Map strategyMap = applicationContext.getBeansOfType(ReceiveMsgStrategy.class); 44 | strategyMap.forEach((k, v) -> { 45 | String type = v.getStrategyType(); 46 | log.debug("[策略Context]-[MessageNoticeSendFactory]-策略类型加载:{}", type); 47 | strategyContainerMap.putIfAbsent(type, v); 48 | }); 49 | } 50 | 51 | /** 52 | * 根据策略类型获取不同处理策略类 53 | * 54 | * @param strategyType 策略类型 55 | * @return 策略类 56 | */ 57 | public static ReceiveMsgStrategy getStrategy(String strategyType) { 58 | log.debug("[策略Context]-[ReceiveMsgStrategy]-当前策略类型:{}", strategyType); 59 | // 策略类对应的枚举 60 | if (!ReceiveMsgChannelEnum.codeMap.containsKey(strategyType)) { 61 | // 当前的渠道类型未匹配到 62 | log.error("入参中的策略类型:{}不在枚举(ReceiveMsgChannelEnum)定义范围内,请检查数据合法性!", strategyType); 63 | // TODO 正常所有的策略都应该在枚举中定义,但考虑到有些人是把项目集成到自己系统中,部分自己的策略类未在枚举中定义,所以这里不抛异常,但是我们建议开启 64 | // throw new BizException("当前策略未在枚举中定义,请先在枚举中指定"); 65 | } 66 | ReceiveMsgStrategy handler = strategyContainerMap.get(strategyType); 67 | if (handler == null) { 68 | log.error("[策略Context]-[MessageNoticeSendFactory]-策略类型:{}-未找到合适的处理器!", strategyType); 69 | throw new BizException("未找到合适的处理器!"); 70 | } 71 | return handler; 72 | } 73 | 74 | /** 75 | * 获取全部策略 76 | * 用于需要全部执行的情况 77 | * 78 | * @return 所有的策略类 79 | */ 80 | public static Map getAllStrategyContainers() { 81 | return strategyContainerMap; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/entity/TResponse.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.entity; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | import com.wechat.ferry.enums.ResponseCodeEnum; 8 | 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.Data; 11 | import lombok.ToString; 12 | import lombok.experimental.Accessors; 13 | 14 | /** 15 | * 返回类封装 16 | */ 17 | @Data 18 | @ToString 19 | @Accessors(chain = true) 20 | public class TResponse { 21 | 22 | private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 23 | 24 | /** 25 | * 状态码 26 | */ 27 | @ApiModelProperty(value = "状态码") 28 | private String code; 29 | 30 | /** 31 | * 返回信息 32 | */ 33 | @ApiModelProperty(value = "返回信息") 34 | private String msg; 35 | 36 | /** 37 | * 响应时间 38 | */ 39 | @ApiModelProperty(value = "响应时间") 40 | private String time; 41 | 42 | /** 43 | * 响应数据 44 | */ 45 | @ApiModelProperty(value = "响应数据") 46 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 47 | private T data; 48 | 49 | /** 50 | * 返回类 51 | * 52 | * @author chandler 53 | * @date 2023/4/5 11:31 54 | * @param t 返回码类 55 | * @param data 返回数据 56 | * @return TResponse对象 57 | */ 58 | public TResponse(IResponse t, T data) { 59 | this(t); 60 | this.data = data; 61 | } 62 | 63 | /** 64 | * 返回类 65 | * 66 | * @author chandler 67 | * @date 2023/4/5 11:31 68 | * @param t 返回码类 69 | * @param msg 返回信息 70 | * @return TResponse对象 71 | */ 72 | public TResponse(IResponse t, String msg) { 73 | this.code = t.getCode(); 74 | this.msg = msg; 75 | this.time = LocalDateTime.now().format(FORMATTER); 76 | } 77 | 78 | /** 79 | * 返回类 80 | * 81 | * @author chandler 82 | * @date 2023/4/5 11:31 83 | * @param t 返回码类 84 | * @param msg 返回信息 85 | * @return TResponse对象 86 | */ 87 | public TResponse(IResponse t, T data, String msg) { 88 | this(t, data); 89 | // 重写返回信息-替换默认的信息 90 | this.msg = msg; 91 | } 92 | 93 | public TResponse(IResponse t) { 94 | this.code = t.getCode(); 95 | this.msg = t.getMsg(); 96 | this.time = LocalDateTime.now().format(FORMATTER); 97 | } 98 | 99 | public static TResponse ok(IResponse t) { 100 | return new TResponse<>(t); 101 | } 102 | 103 | public static TResponse ok(IResponse t, T data) { 104 | return new TResponse<>(t, data); 105 | } 106 | 107 | public static TResponse fail(String msg) { 108 | return new TResponse<>(ResponseCodeEnum.FAILED, msg); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /clients/java/wcferry/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /clients/gohttp/go.mod: -------------------------------------------------------------------------------- 1 | module wechat-rest 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/opentdp/go-helper v0.7.2 7 | github.com/opentdp/wrest-chat v0.27.0 8 | ) 9 | 10 | require ( 11 | filippo.io/edwards25519 v1.1.0 // indirect 12 | github.com/Microsoft/go-winio v0.6.2 // indirect 13 | github.com/VividCortex/ewma v1.2.0 // indirect 14 | github.com/bytedance/sonic v1.11.6 // indirect 15 | github.com/bytedance/sonic/loader v0.1.1 // indirect 16 | github.com/cheggaaa/pb/v3 v3.1.5 // indirect 17 | github.com/clbanning/mxj v1.8.4 // indirect 18 | github.com/cloudwego/base64x v0.1.4 // indirect 19 | github.com/cloudwego/iasm v0.2.0 // indirect 20 | github.com/dustin/go-humanize v1.0.1 // indirect 21 | github.com/fatih/color v1.16.0 // indirect 22 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 23 | github.com/gin-contrib/sse v0.1.0 // indirect 24 | github.com/gin-gonic/gin v1.10.0 // indirect 25 | github.com/glebarez/go-sqlite v1.22.0 // indirect 26 | github.com/glebarez/sqlite v1.11.0 // indirect 27 | github.com/go-playground/locales v0.14.1 // indirect 28 | github.com/go-playground/universal-translator v0.18.1 // indirect 29 | github.com/go-playground/validator/v10 v10.20.0 // indirect 30 | github.com/go-sql-driver/mysql v1.8.1 // indirect 31 | github.com/goccy/go-json v0.10.2 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/gorilla/websocket v1.5.1 // indirect 34 | github.com/jinzhu/inflection v1.0.0 // indirect 35 | github.com/jinzhu/now v1.1.5 // indirect 36 | github.com/json-iterator/go v1.1.12 // indirect 37 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 38 | github.com/kr/pretty v0.2.0 // indirect 39 | github.com/leodido/go-urn v1.4.0 // indirect 40 | github.com/mattn/go-colorable v0.1.13 // indirect 41 | github.com/mattn/go-isatty v0.0.20 // indirect 42 | github.com/mattn/go-runewidth v0.0.15 // indirect 43 | github.com/mitchellh/mapstructure v1.5.0 // indirect 44 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 45 | github.com/modern-go/reflect2 v1.0.2 // indirect 46 | github.com/ncruces/go-strftime v0.1.9 // indirect 47 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 48 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 49 | github.com/rivo/uniseg v0.4.7 // indirect 50 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 51 | github.com/ugorji/go/codec v1.2.12 // indirect 52 | go.nanomsg.org/mangos/v3 v3.4.2 // indirect 53 | golang.org/x/arch v0.8.0 // indirect 54 | golang.org/x/crypto v0.23.0 // indirect 55 | golang.org/x/net v0.25.0 // indirect 56 | golang.org/x/sync v0.7.0 // indirect 57 | golang.org/x/sys v0.20.0 // indirect 58 | golang.org/x/text v0.15.0 // indirect 59 | google.golang.org/protobuf v1.34.1 // indirect 60 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 61 | gopkg.in/yaml.v3 v3.0.1 // indirect 62 | gorm.io/driver/mysql v1.5.6 // indirect 63 | gorm.io/gorm v1.25.10 // indirect 64 | modernc.org/libc v1.50.5 // indirect 65 | modernc.org/mathutil v1.6.0 // indirect 66 | modernc.org/memory v1.8.0 // indirect 67 | modernc.org/sqlite v1.29.9 // indirect 68 | ) 69 | -------------------------------------------------------------------------------- /clients/pyauto/demo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | from time import sleep 6 | from wcfauto import Register, Wcf, WxMsg 7 | 8 | logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s") 9 | LOG = logging.getLogger("Demo") 10 | 11 | 12 | def main(): 13 | receiver = Register() 14 | 15 | @receiver.message_register(isDivision=True, isGroup=True, isPyq=False) 16 | def process_msg(bot: Wcf, msg: WxMsg): 17 | """ 18 | 同步消息函数装饰器 19 | """ 20 | LOG.info(f"收到消息: {msg}") 21 | 22 | sleep(5) # 等微信加载好,以免信息显示异常 23 | LOG.info(f"已经登录: {True if bot.is_login() else False}") 24 | LOG.info(f"wxid: {bot.get_self_wxid()}") 25 | 26 | # bot.disable_recv_msg() # 当需要停止接收消息时调用 27 | sleep(5) 28 | ret = bot.send_text("Hello world.", "filehelper") 29 | LOG.info(f"send_text: {ret}") 30 | 31 | sleep(5) 32 | # 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\) 33 | ret = bot.send_image( 34 | "https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper") 35 | LOG.info(f"send_image: {ret}") 36 | 37 | sleep(5) 38 | # 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\) 39 | ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper") 40 | LOG.info(f"send_file: {ret}") 41 | 42 | sleep(5) 43 | LOG.info(f"Message types:\n{bot.get_msg_types()}") 44 | LOG.info(f"Contacts:\n{bot.get_contacts()}") 45 | 46 | sleep(5) 47 | LOG.info(f"DBs:\n{bot.get_dbs()}") 48 | LOG.info(f"Tables:\n{bot.get_tables('db')}") 49 | LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}") 50 | 51 | # 需要真正的 V3、V4 信息 52 | # bot.accept_new_friend("v3", "v4") 53 | 54 | # 添加群成员,填写正确的群 ID 和成员 wxid 55 | # ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 56 | # LOG.info(f"add_chatroom_members: {ret}") 57 | 58 | # 删除群成员,填写正确的群 ID 和成员 wxid 59 | # ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 60 | # LOG.info(f"add_chatroom_members: {ret}") 61 | 62 | sleep(5) 63 | bot.refresh_pyq(0) # 刷新朋友圈第一页 64 | # bot.refresh_pyq(id) # 从 id 开始刷新朋友圈 65 | 66 | @receiver.async_message_register() 67 | async def async_process_msg(bot: Wcf, msg: WxMsg): 68 | """ 69 | 异步消息函数装饰器 70 | """ 71 | print(msg) 72 | 73 | @receiver.group_changed_register(allow_other_receive=False) 74 | async def group_changed(bot: Wcf, msg: WxMsg): 75 | """ 76 | 群组信息变化函数装饰器 77 | """ 78 | print(msg) 79 | 80 | @receiver.revoke_message_register(allow_other_receive=False) 81 | async def group_changed(bot: Wcf, msg: WxMsg): 82 | """ 83 | 撤回消息函数装饰器 84 | """ 85 | print(msg) 86 | print(msg.get_revoke_msg()) 87 | 88 | def judge(msg: WxMsg): 89 | """ 90 | 消息判断函数 91 | """ 92 | return False 93 | 94 | 95 | @receiver.custom_message_register(register_name='custom', msg_judge_func=judge, allow_other_receive=False) 96 | async def group_changed(bot: Wcf, msg: WxMsg): 97 | """ 98 | 自定义消息接收函数装饰器 99 | """ 100 | print(msg) 101 | 102 | # 开始接受消息 103 | receiver.run() 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/enums/WcfMsgTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 枚举-WCF消息类型 8 | * 9 | * @author chandler 10 | * @date 2024/10/01 15:55 11 | */ 12 | @Getter 13 | @AllArgsConstructor 14 | public enum WcfMsgTypeEnum { 15 | 16 | /** 17 | * 0-朋友圈消息 18 | */ 19 | FRIEND_CIRCLE_MSG("0", "朋友圈消息"), 20 | 21 | /** 22 | * 1-文字 23 | */ 24 | TEXT("1", "文字"), 25 | 26 | /** 27 | * 3-图片 28 | */ 29 | PICTURE("3", "图片"), 30 | 31 | /** 32 | * 34-语音 33 | */ 34 | VOICE("34", "语音"), 35 | 36 | /** 37 | * 37-好友确认 38 | */ 39 | FRIEND_CONFIRM("37", "好友确认"), 40 | 41 | /** 42 | * 40-可能认识的朋友消息-POSSIBLEFRIEND_MSG 43 | */ 44 | POSSIBLE_FRIEND_MSG("40", "可能认识的朋友"), 45 | 46 | /** 47 | * 42-名片 48 | */ 49 | VISITING_CARD("42", "名片"), 50 | 51 | /** 52 | * 43-视频 53 | */ 54 | VIDEO("43", "视频"), 55 | 56 | /** 57 | * 47-石头剪刀布 | 表情图片 58 | */ 59 | EMOJI_IMAGE("47", "石头剪刀布 | 表情图片"), 60 | 61 | /** 62 | * 48-位置 63 | */ 64 | POSITION("48", "位置"), 65 | 66 | /** 67 | * 49-共享实时位置、文件、转账、链接 68 | */ 69 | SHARE("49", "共享实时位置、文件、转账、链接"), 70 | 71 | /** 72 | * 50-语音电话消息-VOIPMSG 73 | */ 74 | VOIP_MSG("50", "语音电话消息"), 75 | 76 | /** 77 | * 51-微信初始化 78 | */ 79 | WECHAT_INIT("51", "微信初始化"), 80 | 81 | /** 82 | * 52-语音电话通知-VOIPNOTIFY 83 | */ 84 | VOIP_NOTIFY("52", "语音电话通知"), 85 | 86 | /** 87 | * 53-语音电话邀请-VOIPINVITE 88 | */ 89 | VOIP_INVITE("53", "语音电话邀请"), 90 | 91 | /** 92 | * 62-小视频 93 | */ 94 | SMALL_VIDEO("62", "小视频"), 95 | 96 | /** 97 | * 66-微信红包 98 | */ 99 | WECHAT_RED_ENVELOPE("66", "微信红包"), 100 | 101 | /** 102 | * 9999-系统通知-SYSNOTICE 103 | */ 104 | SYS_NOTICE("9999", "系统通知"), 105 | 106 | /** 107 | * 10000-红包、系统消息 108 | */ 109 | RED_ENVELOPE_SYS_NOTICE("10000", "红包、系统消息"), 110 | 111 | /** 112 | * 10002-撤回消息 113 | */ 114 | WITHDRAW_MSG("10002", "撤回消息"), 115 | 116 | /** 117 | * 1048625-搜狗表情 118 | */ 119 | SO_GOU_EMOJI("1048625", "搜狗表情"), 120 | 121 | /** 122 | * 16777265-链接 123 | */ 124 | LINK("16777265", "链接"), 125 | 126 | /** 127 | * 436207665-微信红包 128 | */ 129 | RED_ENVELOPE("436207665", "微信红包"), 130 | 131 | /** 132 | * 536936497-红包封面 133 | */ 134 | RED_ENVELOPE_COVER("536936497", "红包封面"), 135 | 136 | /** 137 | * 754974769-视频号视频 138 | */ 139 | VIDEO_NUMBER_VIDEO("754974769", "视频号视频"), 140 | 141 | /** 142 | * 771751985-视频号名片 143 | */ 144 | VIDEO_NUMBER_CARD("771751985", "视频号名片"), 145 | 146 | /** 147 | * 822083633-引用消息 148 | */ 149 | QUOTE_MSG("822083633", "引用消息"), 150 | 151 | /** 152 | * 922746929-拍一拍 153 | */ 154 | PAT_ONE_PAT("922746929", "拍一拍"), 155 | 156 | /** 157 | * 973078577-视频号直播 158 | */ 159 | VIDEO_NUMBER_LIVE("973078577", "视频号直播"), 160 | 161 | /** 162 | * 974127153-商品链接 163 | */ 164 | PRODUCT_LINK("974127153", "商品链接"), 165 | 166 | /** 167 | * 975175729-视频号直播-TODO 168 | */ 169 | UNKNOWN("975175729", "视频号直播"), 170 | 171 | /** 172 | * 1040187441-音乐链接 173 | */ 174 | MUSIC_LINK("1040187441", "音乐链接"), 175 | 176 | /** 177 | * 1090519089-文件 178 | */ 179 | FILE("1090519089", "文件"), 180 | 181 | /** 182 | * 未匹配上 183 | */ 184 | UN_MATCH("", null), 185 | 186 | // 结束 187 | ; 188 | 189 | private final String code; 190 | private final String name; 191 | 192 | } 193 | -------------------------------------------------------------------------------- /clients/go_wcf_http/app/robot.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | /* 4 | #cgo LDFLAGS: -L../ -lsdk 5 | #include 6 | #include 7 | 8 | extern int WxInitSDK(bool, int); 9 | extern int WxDestroySDK(); 10 | */ 11 | import "C" 12 | import ( 13 | "bytes" 14 | "fmt" 15 | "github.com/go-resty/resty/v2" 16 | "github.com/google/uuid" 17 | "go_wechatFerry/wcf" 18 | "io" 19 | "os" 20 | "path/filepath" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | var WxClient *wcf.Client 26 | 27 | // Message 组装成一个结构体展示消息 28 | type Message struct { 29 | IsSelf bool `json:"is_self,omitempty"` 30 | IsGroup bool `json:"is_group,omitempty"` 31 | MessageId uint64 `json:"message_id,omitempty"` 32 | Type uint32 `json:"type,omitempty"` 33 | Ts uint32 `json:"ts,omitempty"` 34 | RoomId string `json:"room_id,omitempty"` 35 | Content string `json:"content,omitempty"` 36 | WxId string `json:"wx_id,omitempty"` 37 | Sign string `json:"sign,omitempty"` 38 | Thumb string `json:"thumb,omitempty"` 39 | Extra string `json:"extra,omitempty"` 40 | Xml string `json:"xml,omitempty"` 41 | } 42 | 43 | // WechatFerryInit 调用sdk.dll中的WxInitSdk 进行启动微信并注入 44 | func WechatFerryInit() { 45 | // 调试模式 端口 46 | initSuccess := C.WxInitSDK(C.bool(false), C.int(10086)) 47 | if initSuccess == 0 { 48 | fmt.Println("SDK 初始化成功") 49 | } else { 50 | fmt.Println("SDK 初始化失败") 51 | } 52 | time.Sleep(time.Millisecond * 5000) 53 | // 连接服务器 54 | client, errs := wcf.NewWCF("") 55 | if errs != nil { 56 | return 57 | } 58 | // 一定要在这里判断是否登录成功 否则会导致用户列表获取失败 59 | for true { 60 | if client.IsLogin() == true { 61 | fmt.Println("登录成功...等待初始化中...") 62 | time.Sleep(2000 * time.Millisecond) 63 | break 64 | } 65 | time.Sleep(1000 * time.Millisecond) 66 | } 67 | WxClient = client 68 | ContactsInit() 69 | fmt.Println("初始化完成") 70 | } 71 | 72 | // MessageProcess 在这里可以继续写代码了 73 | func MessageProcess(msg Message) { 74 | // 方法都在WxClient中 75 | //WxClient.SendTxt("测试","","") 76 | fmt.Println(msg) 77 | } 78 | 79 | // ContactsInit 通讯录初始化 80 | func ContactsInit() { 81 | var contactsMap []map[string]string 82 | contacts := WxClient.GetContacts() 83 | for _, v := range contacts { 84 | gender := "" 85 | if v.Gender == 1 { 86 | gender = "男" 87 | } 88 | if v.Gender == 2 { 89 | gender = "女" 90 | } 91 | contactsMaps := map[string]string{ 92 | "wxId": v.Wxid, 93 | "code": v.Code, 94 | "remark": v.Remark, 95 | "name": v.Name, 96 | "country": v.Country, 97 | "province": v.Province, 98 | "city": v.City, 99 | "gender": gender, 100 | } 101 | contactsMap = append(contactsMap, contactsMaps) 102 | } 103 | WxClient.ContactsMap = contactsMap 104 | } 105 | 106 | // DownloadFile 下载文件 107 | func DownloadFile(url string, fileType string, suffix string) (string, error) { 108 | fmt.Println(url) 109 | // 发送HTTP请求获取文件 110 | resp, err := resty.New().R().Get(url) 111 | if err != nil { 112 | return "", err 113 | } 114 | 115 | // 获取当前日期 116 | currentTime := time.Now() 117 | datePath := filepath.Join("./resource/static/"+fileType, currentTime.Format("2006-01-02")) 118 | // 创建目录 119 | if err := os.MkdirAll(datePath, os.ModePerm); err != nil { 120 | return "", err 121 | } 122 | 123 | // 生成唯一的文件名 124 | fileName := uuid.New().String() + "." + suffix 125 | filePath := filepath.Join(datePath, fileName) 126 | 127 | // 创建文件 128 | file, err := os.Create(filePath) 129 | if err != nil { 130 | return "", err 131 | } 132 | defer file.Close() 133 | // 将HTTP响应的Body复制到文件 134 | _, err = io.Copy(file, bytes.NewBuffer(resp.Body())) 135 | if err != nil { 136 | return "", err 137 | } 138 | currentDir, err := os.Getwd() 139 | if err != nil { 140 | return "", err 141 | } 142 | filePath = currentDir + "/" + filePath 143 | filePath = strings.Replace(filePath, "\\", "/", -1) 144 | return filePath, nil 145 | } 146 | -------------------------------------------------------------------------------- /clients/java/wcferry/src/main/java/com/iamteer/Main.java: -------------------------------------------------------------------------------- 1 | package com.iamteer; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class Main { 7 | private static Logger logger = LoggerFactory.getLogger(Main.class); 8 | 9 | public static void main(String[] args) { 10 | // 连接远程 RPC 11 | // Client client = new Client("127.0.0.1", 10086); 12 | 13 | // 本地启动 RPC 14 | Client client = new Client(); // 默认 10086 端口 15 | // Client client = new Client(10088,true); // 也可以指定端口 16 | 17 | // 是否已登录 18 | logger.info("isLogin: {}", client.isLogin()); 19 | 20 | // 登录账号 wxid 21 | logger.info("wxid: {}", client.getSelfWxid()); 22 | 23 | // 消息类型 24 | logger.info("message types: {}", client.getMsgTypes()); 25 | 26 | // 所有联系人(包括群聊、公众号、好友……) 27 | client.printContacts(client.getContacts()); 28 | 29 | // 获取数据库 30 | logger.info("dbs: {}", client.getDbNames()); 31 | 32 | // 获取数据库下的表 33 | String db = "MicroMsg.db"; 34 | logger.info("tables in {}: {}", db, client.getDbTables(db)); 35 | 36 | // 发送文本消息,aters 是要 @ 的 wxid,多个用逗号分隔;消息里@的数量要与aters里的数量对应 37 | client.sendText("Hello", "filehelper", ""); 38 | // client.sendText("Hello @某人1 @某人2", "xxxxxxxx@chatroom", "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2"); 39 | 40 | // 发送图片消息,图片必须要存在 41 | client.sendImage("C:\\Projs\\WeChatFerry\\TEQuant.jpeg", "filehelper"); 42 | 43 | // 发送文件消息,文件必须要存在 44 | client.sendFile("C:\\Projs\\WeChatFerry\\README.MD", "filehelper"); 45 | 46 | String xml = "叮当药房,24小时服务,28分钟送药到家!叮当快药首家承诺范围内28分钟送药到家!叮当快药核心区域内7*24小时全天候服务,送药上门!叮当快药官网为您提供快捷便利,正品低价,安全放心的购药、送药服务体验。view330https://mp.weixin.qq.com/mp/waerrpage?appid=wxc2edadc87077fa2a&type=upgrade&upgradetype=3#wechat_redirect7f6f49d301ebf47100199b8a4fcf4de4gh_c2b88a38c424@app叮当快药 药店送药到家夜间买药0jpgda0e08f5c7259d03da150d5e7ca6d9503057020100044b30490201000204e4c0232702032f4ef20204a6bace6f02046401f62d042430326337303430352d333734332d343362652d623335322d6233333566623266376334620204012400030201000405004c5376000db26456caf243fbd4efb99058a01d660db26456caf243fbd4efb99058a01d66161558100100pages/index/index.htmlgh_c2b88a38c424@appwxc2edadc87077fa2a1972http://wx.qlogo.cn/mmhead/Q3auHgzwzM4727n0NQ0ZIPQPlfp15m1WLsnrXbo1kLhFGcolgLyc0A/9601_wxc2edadc87077fa2a_29177e9a9b918cb9e75964f80bb8f32e_1677849476_0wxid_eob5qfcrv4zd2201"; 47 | client.sendXml("filehelper", xml, "", 0x21); 48 | 49 | // 发送表情消息,gif 必须要存在 50 | client.sendEmotion("C:\\Projs\\WeChatFerry\\emo.gif", "filehelper"); 51 | 52 | // 接收消息,并调用 printWxMsg 处理 53 | client.enableRecvMsg(100); 54 | Thread thread = new Thread(new Runnable() { 55 | public void run(){while(client.getIsReceivingMsg()){client.printWxMsg(client.getMsg());}} 56 | }); 57 | thread.start(); 58 | // client.diableRecvMsg(); // 需要停止时调用 59 | 60 | client.keepRunning(); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /WeChatFerry/spy/chatroom_mgmt.cpp: -------------------------------------------------------------------------------- 1 | #include "framework.h" 2 | #include 3 | #include 4 | 5 | #include "chatroom_mgmt.h" 6 | #include "log.h" 7 | #include "util.h" 8 | 9 | using namespace std; 10 | extern QWORD g_WeChatWinDllAddr; 11 | 12 | #define OS_GET_CHATROOM_MGR 0x1B83BD0 13 | #define OS_ADD_MEMBERS 0x2155100 14 | #define OS_DELETE_MEMBERS 0x2155740 15 | #define OS_INVITE_MEMBERS 0x2154AE0 16 | 17 | typedef QWORD (*GetChatRoomMgr_t)(); 18 | typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); 19 | typedef QWORD (*DelMemberFromChatRoom_t)(QWORD, QWORD, QWORD); 20 | typedef QWORD (*InviteMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); 21 | 22 | int AddChatroomMember(string roomid, string wxids) 23 | { 24 | int status = -1; 25 | 26 | if (roomid.empty() || wxids.empty()) { 27 | LOG_ERROR("Empty roomid or wxids."); 28 | return status; 29 | } 30 | 31 | GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); 32 | AddMemberToChatRoom_t AddMembers = (AddMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_ADD_MEMBERS); 33 | 34 | vector vMembers; 35 | vector vWxMembers; 36 | wstringstream wss(String2Wstring(wxids)); 37 | while (wss.good()) { 38 | wstring wstr; 39 | getline(wss, wstr, L','); 40 | vMembers.push_back(wstr); 41 | WxString wxMember(vMembers.back()); 42 | vWxMembers.push_back(wxMember); 43 | } 44 | 45 | QWORD temp[2] = { 0 }; 46 | WxString *pWxRoomid = NewWxStringFromStr(roomid); 47 | QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; 48 | 49 | QWORD mgr = GetChatRoomMgr(); 50 | status = (int)AddMembers(mgr, pMembers, (QWORD)pWxRoomid, (QWORD)temp); 51 | return status; 52 | } 53 | 54 | int DelChatroomMember(string roomid, string wxids) 55 | { 56 | int status = -1; 57 | 58 | if (roomid.empty() || wxids.empty()) { 59 | LOG_ERROR("Empty roomid or wxids."); 60 | return status; 61 | } 62 | 63 | GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); 64 | DelMemberFromChatRoom_t DelMembers = (DelMemberFromChatRoom_t)(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); 65 | 66 | vector vMembers; 67 | vector vWxMembers; 68 | wstringstream wss(String2Wstring(wxids)); 69 | while (wss.good()) { 70 | wstring wstr; 71 | getline(wss, wstr, L','); 72 | vMembers.push_back(wstr); 73 | WxString wxMember(vMembers.back()); 74 | vWxMembers.push_back(wxMember); 75 | } 76 | 77 | WxString *pWxRoomid = NewWxStringFromStr(roomid); 78 | QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; 79 | 80 | QWORD mgr = GetChatRoomMgr(); 81 | status = (int)DelMembers(mgr, pMembers, (QWORD)pWxRoomid); 82 | return status; 83 | } 84 | 85 | int InviteChatroomMember(string roomid, string wxids) 86 | { 87 | int status = -1; 88 | 89 | if (roomid.empty() || wxids.empty()) { 90 | LOG_ERROR("Empty roomid or wxids."); 91 | return status; 92 | } 93 | 94 | InviteMemberToChatRoom_t InviteMembers = (InviteMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); 95 | 96 | vector vMembers; 97 | vector vWxMembers; 98 | wstringstream wss(String2Wstring(wxids)); 99 | while (wss.good()) { 100 | wstring wstr; 101 | getline(wss, wstr, L','); 102 | vMembers.push_back(wstr); 103 | WxString wxMember(vMembers.back()); 104 | vWxMembers.push_back(wxMember); 105 | } 106 | QWORD temp[2] = { 0 }; 107 | wstring wsRoomid = String2Wstring(roomid); 108 | WxString *pWxRoomid = NewWxStringFromWstr(wsRoomid); 109 | QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; 110 | 111 | status = (int)InviteMembers((QWORD)wsRoomid.c_str(), pMembers, (QWORD)pWxRoomid, (QWORD)temp); 112 | return status; 113 | } 114 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | ${CONSOLE_LOG_PATTERN} 21 | 22 | 23 | 24 | 25 | 26 | ${log.path}/debug.log 27 | 28 | ${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz 29 | 50MB 30 | 30 31 | 32 | 33 | true 34 | 35 | 36 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 37 | utf-8 38 | 39 | 40 | 41 | 42 | 43 | ${log.path}/error.log 44 | 45 | ${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz 46 | 50MB 47 | 30 48 | 49 | 50 | true 51 | 52 | 53 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 54 | utf-8 55 | 56 | 57 | 58 | ERROR 59 | 60 | 61 | 62 | 73 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /.github/workflows/Build-WeChatFerry.yml: -------------------------------------------------------------------------------- 1 | name: Build-WeChatFerry 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.[0-9]+.[0-9]+" 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - name: 检出代码 14 | uses: actions/checkout@v4 15 | 16 | - name: 获取版本号和微信版本号 17 | run: | 18 | $version_full = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "FileVersion", "(.*)"').Matches.Groups[1].Value.Trim() 19 | $wechat_version = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "ProductVersion", "(.*)"').Matches.Groups[1].Value.Trim() 20 | $version = $version_full -replace '(\d+\.\d+\.\d+)\.\d+', '$1' 21 | echo "version=$version" >> $env:GITHUB_ENV 22 | echo "wechat_version=$wechat_version" >> $env:GITHUB_ENV 23 | echo "Program Version: $version" 24 | echo "WeChat Version: $wechat_version" 25 | shell: pwsh 26 | 27 | - name: 设置 Visual Studio 2019 28 | uses: microsoft/setup-msbuild@v2 29 | with: 30 | vs-version: "16.0" # 16.x 对应 Visual Studio 2019 31 | 32 | - name: 设置 Python 3 33 | uses: actions/setup-python@v5 34 | with: 35 | python-version: "3.9" 36 | 37 | - name: 安装 Python 依赖项 38 | run: | 39 | python -m pip install --upgrade pip 40 | pip install grpcio-tools==1.48.2 41 | 42 | - name: 设置缓存 43 | id: cache-vcpkg 44 | uses: actions/cache@v4 45 | with: 46 | path: | 47 | C:/Tools/vcpkg 48 | ${{ github.workspace }}/WeChatFerry/vcpkg_installed 49 | key: vcpkg-${{ hashFiles('WeChatFerry/vcpkg.json') }} 50 | restore-keys: | 51 | vcpkg- 52 | 53 | - name: 安装 vcpkg 并初始化依赖项 54 | run: | 55 | # 设置 vcpkg 目录 56 | if (!(Test-Path -Path 'C:/Tools')) { 57 | New-Item -ItemType Directory -Force -Path 'C:/Tools' 58 | } 59 | cd C:/Tools 60 | 61 | # 克隆并引导 vcpkg 62 | if (!(Test-Path -Path 'C:/Tools/vcpkg')) { 63 | git clone https://github.com/microsoft/vcpkg 64 | } 65 | .\vcpkg\bootstrap-vcpkg.bat 66 | 67 | # 设置 VCPKG_ROOT 环境变量 68 | echo "VCPKG_ROOT=C:/Tools/vcpkg" >> $GITHUB_ENV 69 | $env:VCPKG_ROOT = 'C:/Tools/vcpkg' 70 | 71 | # 返回到项目目录并安装依赖 72 | cd ${{ github.workspace }}/WeChatFerry 73 | C:/Tools/vcpkg/vcpkg install --triplet x64-windows-static 74 | 75 | # 将 vcpkg 与 Visual Studio 集成 76 | C:/Tools/vcpkg/vcpkg integrate install 77 | 78 | - name: 解析并构建配置 79 | run: | 80 | $configurations = "Release,Debug".Split(',') 81 | foreach ($config in $configurations) { 82 | Write-Host "Building configuration: $config" 83 | msbuild WeChatFerry/WeChatFerry.sln ` 84 | /p:Configuration=$config ` 85 | /p:Platform="x64" ` 86 | /p:VcpkgTriplet="x64-windows-static" ` 87 | /p:VcpkgEnableManifest=true ` 88 | /verbosity:minimal 89 | } 90 | shell: pwsh 91 | 92 | - name: 打包输出文件及下载 WeChat 安装包 93 | run: | 94 | New-Item -ItemType Directory -Force -Path "WeChatFerry/tmp" 95 | Compress-Archive -Path "WeChatFerry/Out/sdk.dll", "WeChatFerry/Out/spy.dll", "WeChatFerry/Out/spy_debug.dll" -DestinationPath "WeChatFerry/tmp/v${{ env.version }}.zip" 96 | Invoke-WebRequest -Uri "https://github.com/tom-snow/wechat-windows-versions/releases/download/v${{ env.wechat_version }}/WeChatSetup-${{ env.wechat_version }}.exe" -OutFile "WeChatFerry/tmp/WeChatSetup-${{ env.wechat_version }}.exe" 97 | shell: pwsh 98 | 99 | - name: 列出预发布文件 100 | run: | 101 | Get-ChildItem -Path "WeChatFerry/tmp" -Recurse 102 | 103 | - name: 发布固件到 Github Releases 104 | uses: ncipollo/release-action@main 105 | with: 106 | name: v${{ env.version }} 107 | tag: v${{ env.version }} 108 | token: ${{ secrets.REPO_TOKEN }} 109 | allowUpdates: true 110 | artifacts: "WeChatFerry/tmp/*" 111 | body: | 112 | 程序版本:`v${{ env.version }}` 113 | 配套微信版本:`${{ env.wechat_version }}` 114 | [📖 Python 文档](https://wechatferry.readthedocs.io/) 115 | -------------------------------------------------------------------------------- /clients/java/wechat-ferry-mvn/src/main/java/com/wechat/ferry/service/impl/WeChatMsgServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.wechat.ferry.service.impl; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.util.CollectionUtils; 9 | import org.springframework.util.ObjectUtils; 10 | 11 | import com.alibaba.fastjson2.JSON; 12 | import com.alibaba.fastjson2.JSONObject; 13 | import com.wechat.ferry.config.WeChatFerryProperties; 14 | import com.wechat.ferry.entity.dto.WxPpMsgDTO; 15 | import com.wechat.ferry.service.WeChatMsgService; 16 | import com.wechat.ferry.strategy.msg.receive.ReceiveMsgFactory; 17 | import com.wechat.ferry.strategy.msg.receive.ReceiveMsgStrategy; 18 | import com.wechat.ferry.utils.HttpClientUtil; 19 | 20 | import lombok.extern.slf4j.Slf4j; 21 | 22 | /** 23 | * 业务实现层-消息处理 24 | * 25 | * @author chandler 26 | * @date 2024-10-01 14:35 27 | */ 28 | @Slf4j 29 | @Service 30 | public class WeChatMsgServiceImpl implements WeChatMsgService { 31 | 32 | @Resource 33 | private WeChatFerryProperties weChatFerryProperties; 34 | 35 | @Override 36 | public void receiveMsg(String jsonString) { 37 | // 转发接口处理 38 | receiveMsgCallback(jsonString); 39 | // 转为JSON对象 40 | WxPpMsgDTO dto = JSON.parseObject(jsonString, WxPpMsgDTO.class); 41 | // 有开启的群聊配置 42 | if (!CollectionUtils.isEmpty(weChatFerryProperties.getOpenMsgGroups())) { 43 | // 指定处理的群聊 44 | if (weChatFerryProperties.getOpenMsgGroups().contains(dto.getRoomId()) || weChatFerryProperties.getOpenMsgGroups().contains("ALL")) { 45 | // TODO 模式有多种 1-根据消息类型单独调用某一个 2-全部调用,各业务类中自己决定是否继续 46 | if (true) { 47 | // 因为一种消息允许进行多种处理,这里采用执行所有策略,请自行在各策略中判断是否需要执行 48 | for (ReceiveMsgStrategy value : ReceiveMsgFactory.getAllStrategyContainers().values()) { 49 | value.doHandle(dto); 50 | } 51 | } else { 52 | // 单独调用某一种 53 | // 这里自己把消息类型转为自己的枚举类型 54 | String handleType = "1"; 55 | ReceiveMsgStrategy receiveMsgStrategy = ReceiveMsgFactory.getStrategy(handleType); 56 | receiveMsgStrategy.doHandle(dto); 57 | } 58 | } 59 | } 60 | log.debug("[收到消息]-[消息内容]-打印:{}", dto); 61 | } 62 | 63 | private void receiveMsgCallback(String jsonString) { 64 | // 开启回调,且回调地址不为空 65 | if (weChatFerryProperties.getReceiveMsgCallbackSwitch() && !CollectionUtils.isEmpty(weChatFerryProperties.getReceiveMsgCallbackUrls())) { 66 | for (String receiveMsgFwdUrl : weChatFerryProperties.getReceiveMsgCallbackUrls()) { 67 | if (!receiveMsgFwdUrl.startsWith("http")) { 68 | continue; 69 | } 70 | try { 71 | String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString); 72 | if (judgeSuccess(responseStr)) { 73 | log.error("[接收消息]-消息回调至外部接口,获取响应状态失败!-URL:{}", receiveMsgFwdUrl); 74 | } 75 | log.debug("[接收消息]-[回调接收到的消息]-回调消息至:{}", receiveMsgFwdUrl); 76 | } catch (Exception e) { 77 | log.error("[接收消息]-消息回调接口[{}]服务异常:", receiveMsgFwdUrl, e); 78 | } 79 | } 80 | } 81 | } 82 | 83 | private Boolean judgeSuccess(String responseStr) { 84 | // 默认为通过 85 | boolean passFlag = false; 86 | if (!ObjectUtils.isEmpty(responseStr)) { 87 | JSONObject jSONObject = JSONObject.parseObject(responseStr); 88 | if (!ObjectUtils.isEmpty(jSONObject) && !CollectionUtils.isEmpty(weChatFerryProperties.getThirdPartyOkCodes())) { 89 | Map codeMap = weChatFerryProperties.getThirdPartyOkCodes(); 90 | for (Map.Entry entry : codeMap.entrySet()) { 91 | if (!ObjectUtils.isEmpty(jSONObject.get(entry.getKey())) && jSONObject.get(entry.getKey()).equals(entry.getValue())) { 92 | passFlag = true; 93 | break; 94 | } 95 | } 96 | } 97 | } 98 | return passFlag; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /clients/pyauto/README.MD: -------------------------------------------------------------------------------- 1 | # WcfAuto 客户端(基于 python 客户端) 2 | [![PyPi](https://img.shields.io/pypi/v/wcferry.svg)](https://pypi.python.org/pypi/wcferry) [![Downloads](https://static.pepy.tech/badge/wcferry)](https://pypi.python.org/pypi/wcferry) [![Documentation Status](https://readthedocs.org/projects/wechatferry/badge/?version=latest)](https://wechatferry.readthedocs.io/zh/latest/?badge=latest) 3 | 4 | |[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/vAGpn1C9stI8Xzt1hUJhLA)| 5 | |:-:|:-:|:-:| 6 | 7 | 🤖示例机器人框架:[WeChatRobot](https://github.com/lich0821/WeChatRobot)。 8 | 9 | ## 快速开始 10 | ```sh 11 | pip install --upgrade wcfauto 12 | ``` 13 | 14 | ### Demo: 15 | ```py 16 | #! /usr/bin/env python3 17 | # -*- coding: utf-8 -*- 18 | 19 | import logging 20 | from time import sleep 21 | from wcfauto import Register, Wcf, WxMsg 22 | 23 | logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s") 24 | LOG = logging.getLogger("Demo") 25 | 26 | 27 | def main(): 28 | receiver = Register() 29 | 30 | @receiver.message_register(isDivision=True, isGroup=True, isPyq=False) 31 | def process_msg(bot: Wcf, msg: WxMsg): 32 | """ 33 | 同步消息函数装饰器 34 | """ 35 | LOG.info(f"收到消息: {msg}") 36 | 37 | sleep(5) # 等微信加载好,以免信息显示异常 38 | LOG.info(f"已经登录: {True if bot.is_login() else False}") 39 | LOG.info(f"wxid: {bot.get_self_wxid()}") 40 | 41 | # bot.disable_recv_msg() # 当需要停止接收消息时调用 42 | sleep(5) 43 | ret = bot.send_text("Hello world.", "filehelper") 44 | LOG.info(f"send_text: {ret}") 45 | 46 | sleep(5) 47 | # 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\) 48 | ret = bot.send_image( 49 | "https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper") 50 | LOG.info(f"send_image: {ret}") 51 | 52 | sleep(5) 53 | # 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\) 54 | ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper") 55 | LOG.info(f"send_file: {ret}") 56 | 57 | sleep(5) 58 | LOG.info(f"Message types:\n{bot.get_msg_types()}") 59 | LOG.info(f"Contacts:\n{bot.get_contacts()}") 60 | 61 | sleep(5) 62 | LOG.info(f"DBs:\n{bot.get_dbs()}") 63 | LOG.info(f"Tables:\n{bot.get_tables('db')}") 64 | LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}") 65 | 66 | # 需要真正的 V3、V4 信息 67 | # bot.accept_new_friend("v3", "v4") 68 | 69 | # 添加群成员,填写正确的群 ID 和成员 wxid 70 | # ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 71 | # LOG.info(f"add_chatroom_members: {ret}") 72 | 73 | # 删除群成员,填写正确的群 ID 和成员 wxid 74 | # ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") 75 | # LOG.info(f"add_chatroom_members: {ret}") 76 | 77 | sleep(5) 78 | bot.refresh_pyq(0) # 刷新朋友圈第一页 79 | # bot.refresh_pyq(id) # 从 id 开始刷新朋友圈 80 | 81 | @receiver.async_message_register() 82 | async def async_process_msg(bot: Wcf, msg: WxMsg): 83 | """ 84 | 异步消息函数装饰器 85 | """ 86 | print(msg) 87 | 88 | @receiver.group_changed_register(allow_other_receive=False) 89 | async def group_changed(bot: Wcf, msg: WxMsg): 90 | """ 91 | 群组信息变化函数装饰器 92 | """ 93 | print(msg) 94 | 95 | @receiver.revoke_message_register(allow_other_receive=False) 96 | async def group_changed(bot: Wcf, msg: WxMsg): 97 | """ 98 | 撤回消息函数装饰器 99 | """ 100 | print(msg) 101 | print(msg.get_revoke_msg()) 102 | 103 | def judge(msg: WxMsg): 104 | """ 105 | 消息判断函数 106 | """ 107 | return False 108 | 109 | 110 | @receiver.custom_message_register(register_name='custom', msg_judge_func=judge, allow_other_receive=False) 111 | async def group_changed(bot: Wcf, msg: WxMsg): 112 | """ 113 | 自定义消息接收函数装饰器 114 | """ 115 | print(msg) 116 | 117 | # 开始接受消息 118 | receiver.run() 119 | 120 | 121 | if __name__ == "__main__": 122 | main() 123 | 124 | 125 | ``` 126 | 127 | |![碲矿](https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/TEQuant.jpg)|![赞赏](https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg)| 128 | |:-:|:-:| 129 | |后台回复 `WCF` 加群交流|如果你觉得有用| 130 | -------------------------------------------------------------------------------- /WeChatFerry/rpc/tool/proto/nanopb_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: nanopb.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb') 18 | 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'nanopb_pb2', globals()) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt) 23 | google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt) 24 | google_dot_protobuf_dot_descriptor__pb2.EnumOptions.RegisterExtension(nanopb_enumopt) 25 | google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(nanopb) 26 | 27 | DESCRIPTOR._options = None 28 | DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopb' 29 | _FIELDTYPE._serialized_start=985 30 | _FIELDTYPE._serialized_end=1090 31 | _INTSIZE._serialized_start=1092 32 | _INTSIZE._serialized_end=1160 33 | _TYPENAMEMANGLING._serialized_start=1162 34 | _TYPENAMEMANGLING._serialized_end=1252 35 | _DESCRIPTORSIZE._serialized_start=1254 36 | _DESCRIPTORSIZE._serialized_end=1323 37 | _NANOPBOPTIONS._serialized_start=51 38 | _NANOPBOPTIONS._serialized_end=983 39 | # @@protoc_insertion_point(module_scope) 40 | --------------------------------------------------------------------------------