From d8e17e0b375b5c392dcb31e62125c3494f17db0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E6=B5=B7?= Date: Fri, 19 Jan 2024 12:04:34 +0800 Subject: [PATCH] Impl websocket and change all http-get to http-post --- clients/gohttp/README.md | 54 +- clients/gohttp/args/args.go | 45 +- clients/gohttp/args/build.go | 31 + clients/gohttp/args/unmarshal.go | 16 +- clients/gohttp/config.yml | 19 +- clients/gohttp/go.mod | 10 +- clients/gohttp/go.sum | 12 +- clients/gohttp/httpd/README.md | 6 +- clients/gohttp/httpd/midware/guard.go | 8 +- clients/gohttp/httpd/server.go | 6 +- clients/gohttp/httpd/wcfrest/controller.go | 710 +++++-- clients/gohttp/httpd/wcfrest/receiver.go | 73 +- clients/gohttp/httpd/wcfrest/router.go | 39 +- clients/gohttp/httpd/wcfrest/types.go | 47 - clients/gohttp/public/assets/icon.png | Bin 0 -> 44615 bytes clients/gohttp/public/assets/image/icon.svg | 5 - clients/gohttp/public/assets/logo.png | Bin 0 -> 45304 bytes clients/gohttp/public/index.html | 4 +- clients/gohttp/public/swag/swagger.json | 1108 ----------- .../public/{swag => swagger}/index.html | 2 +- clients/gohttp/public/swagger/swagger.json | 1701 +++++++++++++++++ clients/gohttp/{start.bat => start-dev.bat} | 2 + 22 files changed, 2449 insertions(+), 1449 deletions(-) create mode 100644 clients/gohttp/args/build.go delete mode 100644 clients/gohttp/httpd/wcfrest/types.go create mode 100644 clients/gohttp/public/assets/icon.png delete mode 100644 clients/gohttp/public/assets/image/icon.svg create mode 100644 clients/gohttp/public/assets/logo.png delete mode 100644 clients/gohttp/public/swag/swagger.json rename clients/gohttp/public/{swag => swagger}/index.html (91%) create mode 100644 clients/gohttp/public/swagger/swagger.json rename clients/gohttp/{start.bat => start-dev.bat} (97%) diff --git a/clients/gohttp/README.md b/clients/gohttp/README.md index d952f33..a42688a 100644 --- a/clients/gohttp/README.md +++ b/clients/gohttp/README.md @@ -1,44 +1,54 @@ -# 微信 REST API +# WeChat Rest 基于 [WeChatFerry RPC](https://github.com/lich0821/WeChatFerry/tree/master/WeChatFerry) 实现,主要特性如下: - 使用 Go 语言编写,无运行时依赖 -- 基于 HTTP 提供操作接口,无缝对接大多数编程语言 -- 支持 HTTP 接口授权,参见 [配置说明](#配置说明) -- 消息中的 Xml 尽可能转为 Object +- 提供 HTTP 接口,便于对接各类编程语言 +- 提供 Websocket 接口,接收推送的新消息 +- 支持 HTTP/WS 接口授权,参见 [配置说明](#配置说明) +- 支持作为 SDK 使用,参见 [wcferry/README.md](https://github.com/opentdp/wechat-rest/wcferry/README.md) +- 内置 AI 机器人,参见 [wclient/README.md](https://github.com/opentdp/wechat-rest/wclient/README.md) +- 内置 Web 管理界面,参见 `http://localhost:7600/` +- 内置 Api 调试工具,参见 `http://localhost:7600/swagger/` +- 尽可能将消息中的 Xml 转为 Object,便于前端解析 -> 此源码仅提供 HTTP REST API 能力,其他能力可参考 [wechat-rest](https://github.com/opentdp/wechat-rest) 相关说明 +> 为保证客户端纯粹性,此包仅提供 HTTP 和 Websocket 接口能力,完整功能可参考 [wechat-rest](https://github.com/opentdp/wechat-rest) 项目说明 -## 使用方法 +## 快速开始 -1、下载并安装 [WeChatSetup-3.9.2.23](https://github.com/opentdp/wechat-rest/releases/download/v0.0.1/WeChatSetup-3.9.2.23.exe),其他版本不支持 +1、下载并安装 [WeChatSetup-3.9.2.23.exe](https://github.com/opentdp/wechat-rest/releases/download/v0.0.1/WeChatSetup-3.9.2.23.exe) 和 [wechat-rest.zip](https://github.com/opentdp/wechat-rest/releases) -2、下载 [WeChatFerry](https://github.com/lich0821/WeChatFerry/releases),解压后,将2个dll文件复制到当前目录,其他文件可忽略 +- 非开发者请直接下载编译好的二进制文件,不要下载源码 -3、双击 `start.bat` 将自动启动微信和接口服务,扫码登录 +2、双击 `wrest.exe` 将自动启动微信和接口服务,扫码登录微信 -> 初始化时出现 **Attempt to access invalid address** 错误信息可以忽略 +- 初始化时若出现 *Attempt to access invalid address* 信息可忽略 -4、浏览器打开 `http://localhost:7600` 查看支持的接口 +3、修改 [config.yml](./config.yml) 配置机器人参数,重启 **wrest.exe** 后生效 + +- 请使用 `Ctrl + C` 终止 **wrest.exe**,切勿直接关闭任务窗口 +- 重启时,提示端口被占用,请退出微信后重试 ## 配置说明 -启动 `wrest` 时将自动创建一个默认配置文件,完整配置说明可参考开源仓库中的 [config.yml](./config.yml) +启动时将自动创建一个默认配置文件,完整配置可参考开源仓库中的 [config.yml](./config.yml) -- 应使用 `Ctrl + C` 终止 **wrest**,而非直接关闭 **wrest** 窗口 -- 若设置了 `token`,请求时需携带 **header** 信息: `Authorization: Bearer $token` +- 如设置了 `token`,请求接口时需携带 **header** 信息: `Authorization: Bearer $token` ## 开发说明 -### 编译须知 +- 查看和调试*HTTP*接口文档,请使用浏览器打开 `http://localhost:7600` -由于微信和WCF均为32位应用,所以`go`也必须以`32`位模式编译,务必设置 `GOARCH` 环境变量为 `386` +- 由于微信和*WCF*均为32位应用,对接*bot*和*sdk*部分,必须设置环境变量 `GOARCH=386` -### 生成 OpenApi 文档 +### API 模块 -```shell -go get github.com/swaggo/swag/cmd/swag -go install github.com/swaggo/swag/cmd/swag +实现了 HTTP 接口,详情查看 [httpd/README.md](https://github.com/opentdp/wechat-rest/httpd/README.md) -swag init --parseDependency -g httpd/server.go -o public/swag -ot json -``` +### BOT 模块 + +实现了群聊机器人,详情查看 [wclient/README.md](https://github.com/opentdp/wechat-rest/wclient/README.md) + +### SDK 模块 + +实现了 WCF 客户端,详情查看 [wcferry/README.md](https://github.com/opentdp/wechat-rest/wcferry/README.md) diff --git a/clients/gohttp/args/args.go b/clients/gohttp/args/args.go index edb5a3b..0a44cae 100644 --- a/clients/gohttp/args/args.go +++ b/clients/gohttp/args/args.go @@ -1,31 +1,8 @@ package args -import ( - "embed" -) +// 日志配置 -// 调试模式 - -var Debug bool - -// 嵌入目录 - -var Efs *embed.FS - -// Http 服务参数 - -var Httpd = struct { - Address string `yaml:"address"` - Token string `yaml:"token"` - Swag bool `yaml:"swag"` -}{ - Address: "127.0.0.1:7600", - Swag: true, -} - -// 日志参数 - -var Logger = struct { +var Log = struct { Dir string `yaml:"dir"` Level string `yaml:"level"` Target string `yaml:"target"` @@ -35,14 +12,24 @@ var Logger = struct { Target: "stdout", } -// Wcf 服务参数 +// Web 服务 + +var Web = struct { + Address string `yaml:"address"` + Swagger bool `yaml:"swagger"` + Token string `yaml:"token"` +}{ + Address: "127.0.0.1:7600", + Swagger: true, +} + +// Wcf 服务 var Wcf = struct { Address string `yaml:"address"` - SdkLibrary string `yaml:"sdkLibrary"` WeChatAuto bool `yaml:"wechatAuto"` MsgPrinter bool `yaml:"msgPrinter"` }{ - Address: "127.0.0.1:10080", - SdkLibrary: "sdk.dll", + Address: "127.0.0.1:7601", + WeChatAuto: true, } diff --git a/clients/gohttp/args/build.go b/clients/gohttp/args/build.go new file mode 100644 index 0000000..376fb04 --- /dev/null +++ b/clients/gohttp/args/build.go @@ -0,0 +1,31 @@ +package args + +import ( + "embed" +) + +// 调试模式 + +var Debug bool + +// 嵌入目录 + +var Efs *embed.FS + +// 版本信息 + +const Version = "0.10.0" +const BuildVersion = "240106" + +// 应用描述 + +const AppName = "TDP Wrest" +const AppSummary = "智能聊天机器人" + +// 输出说明 +func init() { + + println(AppName, AppSummary) + println("Version:", Version, "build", BuildVersion) + +} diff --git a/clients/gohttp/args/unmarshal.go b/clients/gohttp/args/unmarshal.go index 492cd1f..18caabe 100644 --- a/clients/gohttp/args/unmarshal.go +++ b/clients/gohttp/args/unmarshal.go @@ -12,9 +12,9 @@ func (c *Config) Unmarshal() { // 读取默认配置 mp := map[string]any{ - "httpd": &Httpd, - "logger": &Logger, - "wcf": &Wcf, + "log": &Log, + "web": &Web, + "wcf": &Wcf, } c.Koanf.Load(confmap.Provider(mp, "."), nil) @@ -27,14 +27,14 @@ func (c *Config) Unmarshal() { // 初始化日志 - if Logger.Dir != "" && Logger.Dir != "." { - os.MkdirAll(Logger.Dir, 0755) + if Log.Dir != "" && Log.Dir != "." { + os.MkdirAll(Log.Dir, 0755) } logman.SetDefault(&logman.Config{ - Level: Logger.Level, - Target: Logger.Target, - Storage: Logger.Dir, + Level: Log.Level, + Target: Log.Target, + Storage: Log.Dir, Filename: "wrest", }) diff --git a/clients/gohttp/config.yml b/clients/gohttp/config.yml index b5859ea..5bf5f81 100644 --- a/clients/gohttp/config.yml +++ b/clients/gohttp/config.yml @@ -1,18 +1,17 @@ -# HTTP 接口 -httpd: - address: 127.0.0.1:7600 # Api 监听地址 - token: "" # 使用 Token 验证请求 - swag: true # 是否启用 OpenApi 文档 - # 运行日志 -logger: +log: dir: logs # 日志目录 level: info # 日志级别 target: stdout # 日志输出方式 -# WeChat Ferry +# Web 服务 +web: + address: 127.0.0.1:7600 # 监听地址 + swagger: true # 是否启用 OpenApi 文档 + token: "" # 使用 Token 验证请求 + +# Wcf 服务 wcf: - address: 127.0.0.1:10080 # Rpc 监听地址 - sdkLibrary: wcferry/libs/sdk.dll # Sdk 路径 + address: 127.0.0.1:7601 # Rpc 监听地址 wechatAuto: true # 是否跟随启停微信 msgPrinter: false # 是否打印收到的消息 diff --git a/clients/gohttp/go.mod b/clients/gohttp/go.mod index 38bfdbb..07e396b 100644 --- a/clients/gohttp/go.mod +++ b/clients/gohttp/go.mod @@ -6,8 +6,10 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/knadh/koanf v1.5.0 github.com/knadh/koanf/v2 v2.0.1 - github.com/opentdp/go-helper v0.5.5-0.20240109013403-7323088c3f39 - github.com/opentdp/wechat-rest v0.8.1 + github.com/mitchellh/mapstructure v1.5.0 + github.com/opentdp/go-helper v0.5.7 + github.com/opentdp/wechat-rest v0.10.1 + golang.org/x/net v0.20.0 ) require ( @@ -24,7 +26,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.16.0 // indirect + github.com/go-playground/validator/v10 v10.17.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect @@ -35,7 +37,6 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -48,7 +49,6 @@ require ( golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect diff --git a/clients/gohttp/go.sum b/clients/gohttp/go.sum index 212ce9e..c3af5e0 100644 --- a/clients/gohttp/go.sum +++ b/clients/gohttp/go.sum @@ -91,8 +91,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= -github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= +github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -248,10 +248,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/opentdp/go-helper v0.5.5-0.20240109013403-7323088c3f39 h1:qIzCCWpIPhzZq9ZXevYuqBV3w4F/D8/SK+W6UjKhSf0= -github.com/opentdp/go-helper v0.5.5-0.20240109013403-7323088c3f39/go.mod h1:9m+t/2x1CmjZ9YQP37+xECCO84eqMnsxnRE/7y37GjE= -github.com/opentdp/wechat-rest v0.8.1 h1:2M7BFYCotM3gJkGxJ3mOczmftByxqRNSj4Q2Ec96Dws= -github.com/opentdp/wechat-rest v0.8.1/go.mod h1:F3rutIf4PahHxPGuAm3wlM6d1S7nznaU/ZNrk0dMEDA= +github.com/opentdp/go-helper v0.5.7 h1:6NPodNpmc37qt7urS6n/mA2Jk3DQTHA0rlh2qXa8dKI= +github.com/opentdp/go-helper v0.5.7/go.mod h1:9m+t/2x1CmjZ9YQP37+xECCO84eqMnsxnRE/7y37GjE= +github.com/opentdp/wechat-rest v0.10.1 h1:P+yIKang4ckAOB10+7kbm/2V/0Punx2ElxDLL+Dp0Cw= +github.com/opentdp/wechat-rest v0.10.1/go.mod h1:JCrwAaKhosZ9oyEhN6R09XA4ACP31xn/0VL3DOY5VF0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= diff --git a/clients/gohttp/httpd/README.md b/clients/gohttp/httpd/README.md index da652f9..31f2b22 100644 --- a/clients/gohttp/httpd/README.md +++ b/clients/gohttp/httpd/README.md @@ -37,5 +37,9 @@ go get github.com/swaggo/swag/cmd/swag go install github.com/swaggo/swag/cmd/swag -swag init --parseDependency -g httpd/server.go -o public/swag -ot json +swag init --parseDependency -g httpd/server.go -o public/swagger -ot json ``` + +## 生成 OpenApi 客户端 + +将生成的 `swagger.json` 上传至 `https://editor.swagger.io` 生成对应的客户端 diff --git a/clients/gohttp/httpd/midware/guard.go b/clients/gohttp/httpd/midware/guard.go index 3d81eb8..1f004b5 100644 --- a/clients/gohttp/httpd/midware/guard.go +++ b/clients/gohttp/httpd/midware/guard.go @@ -12,13 +12,15 @@ func ApiGuard(c *gin.Context) { token := "" + // 取回 Token authcode := c.GetHeader("Authorization") parts := strings.SplitN(authcode, " ", 2) if len(parts) == 2 && parts[0] == "Bearer" { token = parts[1] } - if token != args.Httpd.Token { + // 校验 Token + if token != args.Web.Token { c.Set("Error", gin.H{"Code": 401, "Message": "操作未授权"}) c.Set("ExitCode", 401) c.Abort() @@ -26,9 +28,9 @@ func ApiGuard(c *gin.Context) { } -func SwagGuard(c *gin.Context) { +func SwaggerGuard(c *gin.Context) { - if !args.Httpd.Swag && strings.HasPrefix(c.Request.URL.Path, "/swag") { + if !args.Web.Swagger && strings.HasPrefix(c.Request.URL.Path, "/swagger") { c.Header("Content-Type", "text/html; charset=utf-8") c.String(200, "功能已禁用") c.Abort() diff --git a/clients/gohttp/httpd/server.go b/clients/gohttp/httpd/server.go index 70dad6b..64a9930 100644 --- a/clients/gohttp/httpd/server.go +++ b/clients/gohttp/httpd/server.go @@ -9,7 +9,7 @@ import ( ) // @title WeChat Rest Api -// @version v0.5.0 +// @version v0.10.0 // @description 基于 WeChatFerry RPC 实现的微信接口,使用 Go 语言编写,无第三方运行时依赖,易于对接任意编程语言。 // @contact.name WeChatRest // @contact.url https://github.com/opentdp/wechat-rest @@ -29,12 +29,12 @@ func Server() { wcfrest.Route(api) // Swagger 守卫 - httpd.Use(midware.SwagGuard) + httpd.Use(midware.SwaggerGuard) // 前端文件路由 httpd.StaticEmbed("/", "public", args.Efs) // 启动 HTTP 服务 - httpd.Server(args.Httpd.Address) + httpd.Server(args.Web.Address) } diff --git a/clients/gohttp/httpd/wcfrest/controller.go b/clients/gohttp/httpd/wcfrest/controller.go index bff2b95..747b34a 100644 --- a/clients/gohttp/httpd/wcfrest/controller.go +++ b/clients/gohttp/httpd/wcfrest/controller.go @@ -4,7 +4,9 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/opentdp/go-helper/strutil" + "github.com/mitchellh/mapstructure" + "github.com/opentdp/go-helper/logman" + "golang.org/x/net/websocket" "github.com/opentdp/wechat-rest/wcferry" ) @@ -13,10 +15,22 @@ type Controller struct { *wcferry.Client } +// 通用结果 +type CommonPayload struct { + // 是否成功 + Success bool `json:"success,omitempty"` + // 返回结果 + Result string `json:"result,omitempty"` + // 错误信息 + Error error `json:"error,omitempty"` +} + // @Summary 检查登录状态 // @Produce json // @Success 200 {object} bool -// @Router /is_login [get] +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /is_login [post] func (wc *Controller) isLogin(c *gin.Context) { c.Set("Payload", wc.CmdClient.IsLogin()) @@ -26,7 +40,9 @@ func (wc *Controller) isLogin(c *gin.Context) { // @Summary 获取登录账号wxid // @Produce json // @Success 200 {object} string -// @Router /self_wxid [get] +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /self_wxid [post] func (wc *Controller) getSelfWxid(c *gin.Context) { c.Set("Payload", wc.CmdClient.GetSelfWxid()) @@ -35,50 +51,45 @@ func (wc *Controller) getSelfWxid(c *gin.Context) { // @Summary 获取登录账号个人信息 // @Produce json -// @Success 200 {object} wcferry.UserInfo -// @Router /user_info [get] -func (wc *Controller) getUserInfo(c *gin.Context) { +// @Success 200 {object} UserInfoPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /self_info [post] +func (wc *Controller) getSelfInfo(c *gin.Context) { - c.Set("Payload", wc.CmdClient.GetUserInfo()) + c.Set("Payload", wc.CmdClient.GetSelfInfo()) } -// @Summary 获取完整通讯录 -// @Produce json -// @Success 200 {object} []wcferry.RpcContact -// @Router /contacts [get] -func (wc *Controller) getContacts(c *gin.Context) { - - c.Set("Payload", wc.CmdClient.GetContacts()) - +type UserInfoPayload struct { + // 用户 id + Wxid string `json:"wxid,omitempty"` + // 昵称 + Name string `json:"name,omitempty"` + // 手机号 + Mobile string `json:"mobile,omitempty"` + // 文件/图片等父路径 + Home string `json:"home,omitempty"` } -// @Summary 获取好友列表 +// @Summary 获取所有消息类型 // @Produce json -// @Success 200 {object} []wcferry.RpcContact -// @Router /friends [get] -func (wc *Controller) getFriends(c *gin.Context) { +// @Success 200 {object} map[int32]string +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /msg_types [post] +func (wc *Controller) getMsgTypes(c *gin.Context) { - c.Set("Payload", wc.CmdClient.GetFriends()) - -} - -// @Summary 根据wxid获取个人信息 -// @Produce json -// @Param wxid path string true "wxid" -// @Success 200 {object} wcferry.RpcContact -// @Router /user_info/{wxid} [get] -func (wc *Controller) getUserInfoByWxid(c *gin.Context) { - - wxid := c.Param("wxid") - c.Set("Payload", wc.CmdClient.GetInfoByWxid(wxid)) + c.Set("Payload", wc.CmdClient.GetMsgTypes()) } // @Summary 获取数据库列表 // @Produce json // @Success 200 {object} []string -// @Router /db_names [get] +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /db_names [post] func (wc *Controller) getDbNames(c *gin.Context) { c.Set("Payload", wc.CmdClient.GetDbNames()) @@ -87,20 +98,41 @@ func (wc *Controller) getDbNames(c *gin.Context) { // @Summary 获取数据库表列表 // @Produce json -// @Param db path string true "数据库名" -// @Success 200 {object} []wcferry.DbTable -// @Router /db_tables/{db} [get] +// @Param body body GetDbTablesRequest true "获取数据库表列表参数" +// @Success 200 {object} []DbTablePayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /db_tables [post] func (wc *Controller) getDbTables(c *gin.Context) { - db := c.Param("db") - c.Set("Payload", wc.CmdClient.GetDbTables(db)) + var req GetDbTablesRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + c.Set("Payload", wc.CmdClient.GetDbTables(req.Db)) + +} + +type DbTablePayload struct { + // 表名 + Name string `json:"name,omitempty"` + // 建表 SQL + Sql string `json:"sql,omitempty"` +} + +type GetDbTablesRequest struct { + // 数据库名称 + Db string `json:"db"` } // @Summary 执行数据库查询 // @Produce json -// @Param body body DbSqlQueryRequest true "数据库查询请求参数" -// @Success 200 {object} map[string]any +// @Param body body DbSqlQueryRequest true "数据库查询参数" +// @Success 200 {object} []map[string]any +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /db_query_sql [post] func (wc *Controller) dbSqlQuery(c *gin.Context) { @@ -114,38 +146,19 @@ func (wc *Controller) dbSqlQuery(c *gin.Context) { } -// @Summary 获取所有消息类型 -// @Produce json -// @Success 200 {object} map[int32]string -// @Router /msg_types [get] -func (wc *Controller) getMsgTypes(c *gin.Context) { - - c.Set("Payload", wc.CmdClient.GetMsgTypes()) - -} - -// @Summary 刷新朋友圈 -// @Produce json -// @Param id path int true "朋友圈id" -// @Success 200 {object} RespPayload -// @Router /refresh_pyq/{id} [get] -func (wc *Controller) refreshPyq(c *gin.Context) { - - id := c.Param("id") - pyqid := uint64(strutil.ToUint(id)) - - status := wc.CmdClient.RefreshPyq(pyqid) - - c.Set("Payload", RespPayload{ - Success: status == 1, - }) - +type DbSqlQueryRequest struct { + // 数据库名称 + Db string `json:"db"` + // 待执行的 SQL + Sql string `json:"sql"` } // @Summary 获取群列表 // @Produce json -// @Success 200 {object} []wcferry.RpcContact -// @Router /chatrooms [get] +// @Success 200 {object} []ContactPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /chatrooms [post] func (wc *Controller) getChatRooms(c *gin.Context) { c.Set("Payload", wc.CmdClient.GetChatRooms()) @@ -154,67 +167,102 @@ func (wc *Controller) getChatRooms(c *gin.Context) { // @Summary 获取群成员列表 // @Produce json -// @Param roomid path string true "群id" -// @Success 200 {object} []wcferry.RpcContact -// @Router /chatroom_members/{roomid} [get] +// @Param body body GetChatRoomMembersRequest true "获取群成员列表参数" +// @Success 200 {object} []ContactPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /chatroom_members [post] func (wc *Controller) getChatRoomMembers(c *gin.Context) { - roomid := c.Param("roomid") - c.Set("Payload", wc.CmdClient.GetChatRoomMembers(roomid)) - -} - -// @Summary 获取群成员昵称 -// @Produce json -// @Param wxid path string true "wxid" -// @Param roomid path string true "群id" -// @Success 200 {object} string -// @Router /alias_in_chatroom/{wxid}/{roomid} [get] -func (wc *Controller) getAliasInChatRoom(c *gin.Context) { - - wxid := c.Param("wxid") - roomid := c.Param("roomid") - c.Set("Payload", wc.CmdClient.GetAliasInChatRoom(wxid, roomid)) - -} - -// @Summary 邀请群成员 -// @Produce json -// @Param body body wcferry.MemberMgmt true "增删群成员请求参数" -// @Success 200 {object} RespPayload -// @Router /invite_chatroom_members [post] -func (wc *Controller) inviteChatroomMembers(c *gin.Context) { - - var req wcferry.MemberMgmt + var req GetChatRoomMembersRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return } - status := wc.CmdClient.InviteChatroomMembers(req.Roomid, req.Wxids) + c.Set("Payload", wc.CmdClient.GetChatRoomMembers(req.Roomid)) - c.Set("Payload", RespPayload{ +} + +type GetChatRoomMembersRequest struct { + // 群聊 id + Roomid string `json:"roomid"` +} + +// @Summary 获取群成员昵称 +// @Produce json +// @Param body body GetAliasInChatRoomRequest true "获取群成员昵称参数" +// @Success 200 {object} string +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /alias_in_chatroom [post] +func (wc *Controller) getAliasInChatRoom(c *gin.Context) { + + var req GetAliasInChatRoomRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + + c.Set("Payload", wc.CmdClient.GetAliasInChatRoom(req.Wxid, req.Roomid)) + +} + +type GetAliasInChatRoomRequest struct { + // 群聊 id + Roomid string `json:"roomid"` + // 用户 id + Wxid string `json:"wxid"` +} + +// @Summary 邀请群成员 +// @Produce json +// @Param body body ChatroomMembersRequest true "管理群成员参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /invite_chatroom_members [post] +func (wc *Controller) inviteChatroomMembers(c *gin.Context) { + + var req ChatroomMembersRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + + status := wc.CmdClient.InviteChatroomMembers(req.Roomid, strings.Join(req.Wxids, ",")) + + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type ChatroomMembersRequest struct { + // 群聊 id + Roomid string `json:"roomid"` + // 用户 id 列表 + Wxids []string `json:"wxids"` +} + // @Summary 添加群成员 // @Produce json -// @Param body body wcferry.MemberMgmt true "增删群成员请求参数" -// @Success 200 {object} RespPayload +// @Param body body ChatroomMembersRequest true "管理群成员参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /add_chatroom_members [post] func (wc *Controller) addChatRoomMembers(c *gin.Context) { - var req wcferry.MemberMgmt + var req ChatroomMembersRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return } - status := wc.CmdClient.AddChatRoomMembers(req.Roomid, req.Wxids) + status := wc.CmdClient.AddChatRoomMembers(req.Roomid, strings.Join(req.Wxids, ",")) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) @@ -222,20 +270,22 @@ func (wc *Controller) addChatRoomMembers(c *gin.Context) { // @Summary 删除群成员 // @Produce json -// @Param body body wcferry.MemberMgmt true "增删群成员请求参数" -// @Success 200 {object} RespPayload +// @Param body body ChatroomMembersRequest true "管理群成员参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /del_chatroom_members [post] func (wc *Controller) delChatRoomMembers(c *gin.Context) { - var req wcferry.MemberMgmt + var req ChatroomMembersRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return } - status := wc.CmdClient.DelChatRoomMembers(req.Roomid, req.Wxids) + status := wc.CmdClient.DelChatRoomMembers(req.Roomid, strings.Join(req.Wxids, ",")) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) @@ -243,72 +293,104 @@ func (wc *Controller) delChatRoomMembers(c *gin.Context) { // @Summary 撤回消息 // @Produce json -// @Param msgid path int true "消息id" -// @Success 200 {object} RespPayload -// @Router /revoke_msg/{msgid} [get] +// @Param body body RevokeMsgRequest true "撤回消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /revoke_msg [post] func (wc *Controller) revokeMsg(c *gin.Context) { - id := c.Param("msgid") - msgid := uint64(strutil.ToUint(id)) + var req RevokeMsgRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } - status := wc.CmdClient.RevokeMsg(msgid) + status := wc.CmdClient.RevokeMsg(req.Msgid) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type RevokeMsgRequest struct { + // 消息 id + Msgid uint64 `json:"msgid"` +} + // @Summary 转发消息 // @Produce json -// @Param body body wcferry.ForwardMsg true "转发消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body ForwardMsgRequest true "转发消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /forward_msg [post] func (wc *Controller) forwardMsg(c *gin.Context) { - var req wcferry.ForwardMsg + var req ForwardMsgRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return } - status := wc.CmdClient.ForwardMsg(req.Id, req.Receiver) + status := wc.CmdClient.ForwardMsg(req.Id, strings.Join(req.Receiver, ",")) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type ForwardMsgRequest struct { + // 待转发消息 id + Id uint64 `json:"id"` + // 转发接收人或群的 id 列表 + Receiver []string `json:"receiver"` +} + // @Summary 发送文本消息 // @Produce json -// @Param body body wcferry.TextMsg true "文本消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body SendTxtRequest true "发送文本消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /send_txt [post] func (wc *Controller) sendTxt(c *gin.Context) { - var req wcferry.TextMsg + var req SendTxtRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return } - status := wc.CmdClient.SendTxt(req.Msg, req.Receiver, req.Aters) + status := wc.CmdClient.SendTxt(req.Msg, req.Receiver, strings.Join(req.Aters, ",")) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 0, }) } +type SendTxtRequest struct { + // 消息内容 + Msg string `json:"msg"` + // 接收人或群的 id + Receiver string `json:"receiver"` + // 需要 At 的用户 id 列表 + Aters []string `json:"aters"` +} + // @Summary 发送图片消息 // @Produce json -// @Param body body wcferry.PathMsg true "图片消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body SendImgRequest true "发送图片消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /send_img [post] func (wc *Controller) sendImg(c *gin.Context) { - var req wcferry.PathMsg + var req SendImgRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -316,20 +398,29 @@ func (wc *Controller) sendImg(c *gin.Context) { status := wc.CmdClient.SendImg(req.Path, req.Receiver) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 0, }) } +type SendImgRequest struct { + // 图片路径 + Path string `json:"path"` + // 接收人或群的 id + Receiver string `json:"receiver"` +} + // @Summary 发送文件消息 // @Produce json -// @Param body body wcferry.PathMsg true "文件消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body SendFileRequest true "发送文件消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /send_file [post] func (wc *Controller) sendFile(c *gin.Context) { - var req wcferry.PathMsg + var req SendFileRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -337,20 +428,29 @@ func (wc *Controller) sendFile(c *gin.Context) { status := wc.CmdClient.SendFile(req.Path, req.Receiver) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 0, }) } +type SendFileRequest struct { + // 文件路径 + Path string `json:"path"` + // 接收人或群的 id + Receiver string `json:"receiver"` +} + // @Summary 发送卡片消息 // @Produce json -// @Param body body wcferry.RichText true "卡片消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body SendRichTextRequest true "发送卡片消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /send_rich_text [post] func (wc *Controller) sendRichText(c *gin.Context) { - var req wcferry.RichText + var req SendRichTextRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -358,20 +458,39 @@ func (wc *Controller) sendRichText(c *gin.Context) { status := wc.CmdClient.SendRichText(req.Name, req.Account, req.Title, req.Digest, req.Url, req.Thumburl, req.Receiver) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 0, }) } +type SendRichTextRequest struct { + // 左下显示的名字 + Name string `json:"name"` + // 填公众号 id 可以显示对应的头像(gh_ 开头的) + Account string `json:"account"` + // 标题,最多两行 + Title string `json:"title"` + // 摘要,三行 + Digest string `json:"digest"` + // 点击后跳转的链接 + Url string `json:"url"` + // 缩略图的链接 + Thumburl string `json:"thumburl"` + // 接收人或群的 id + Receiver string `json:"receiver"` +} + // @Summary 拍一拍群友 // @Produce json -// @Param body body wcferry.PatMsg true "拍一拍请求参数" -// @Success 200 {object} RespPayload +// @Param body body SendPatMsgRequest true "拍一拍群友参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /send_pat_msg [post] func (wc *Controller) sendPatMsg(c *gin.Context) { - var req wcferry.PatMsg + var req SendPatMsgRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -379,16 +498,25 @@ func (wc *Controller) sendPatMsg(c *gin.Context) { status := wc.CmdClient.SendPatMsg(req.Roomid, req.Wxid) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type SendPatMsgRequest struct { + // 群 id + Roomid string `json:"roomid"` + // 用户 id + Wxid string `json:"wxid"` +} + // @Summary 获取语音消息 // @Produce json -// @Param body body GetAudioMsgRequest true "语音消息请求参数" -// @Success 200 {object} RespPayload +// @Param body body GetAudioMsgRequest true "获取语音消息参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /get_audio_msg [post] func (wc *Controller) getAudioMsg(c *gin.Context) { @@ -400,14 +528,14 @@ func (wc *Controller) getAudioMsg(c *gin.Context) { if req.Timeout > 0 { resp, err := wc.CmdClient.GetAudioMsgTimeout(req.Msgid, req.Dir, req.Timeout) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: resp != "", Result: resp, Error: err, }) } else { resp := wc.CmdClient.GetAudioMsg(req.Msgid, req.Dir) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: resp != "", Result: resp, }) @@ -415,10 +543,21 @@ func (wc *Controller) getAudioMsg(c *gin.Context) { } +type GetAudioMsgRequest struct { + // 消息 id + Msgid uint64 `json:"msgid"` + // 存储路径 + Dir string `json:"path"` + // 超时重试次数 + Timeout int `json:"timeout"` +} + // @Summary 获取OCR识别结果 // @Produce json -// @Param body body GetOcrRequest true "文本请求参数" -// @Success 200 {object} RespPayload +// @Param body body GetOcrRequest true "获取OCR识别结果参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /get_ocr_result [post] func (wc *Controller) getOcrResult(c *gin.Context) { @@ -430,14 +569,14 @@ func (wc *Controller) getOcrResult(c *gin.Context) { if req.Timeout > 0 { resp, err := wc.CmdClient.GetOcrResultTimeout(req.Extra, req.Timeout) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: resp != "", Result: resp, Error: err, }) } else { resp, stat := wc.CmdClient.GetOcrResult(req.Extra) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: stat == 0, Result: resp, }) @@ -445,10 +584,19 @@ func (wc *Controller) getOcrResult(c *gin.Context) { } +type GetOcrRequest struct { + // 消息中的 extra 字段 + Extra string `json:"extra"` + // 超时重试次数 + Timeout int `json:"timeout"` +} + // @Summary 下载图片 // @Produce json // @Param body body DownloadImageRequest true "下载图片参数" -// @Success 200 {object} RespPayload +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /download_image [post] func (wc *Controller) downloadImage(c *gin.Context) { @@ -460,7 +608,7 @@ func (wc *Controller) downloadImage(c *gin.Context) { resp, err := wc.CmdClient.DownloadImage(req.Msgid, req.Extra, req.Dir, req.Timeout) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: resp != "", Result: resp, Error: err, @@ -468,10 +616,23 @@ func (wc *Controller) downloadImage(c *gin.Context) { } +type DownloadImageRequest struct { + // 消息 id + Msgid uint64 `json:"msgid"` + // 消息中的 extra 字段 + Extra string `json:"extra"` + // 存储路径 + Dir string `json:"dir"` + // 超时重试次数 + Timeout int `json:"timeout"` +} + // @Summary 下载附件 // @Produce json // @Param body body DownloadAttachRequest true "下载附件参数" -// @Success 200 {object} RespPayload +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /download_attach [post] func (wc *Controller) downloadAttach(c *gin.Context) { @@ -483,20 +644,175 @@ func (wc *Controller) downloadAttach(c *gin.Context) { status := wc.CmdClient.DownloadAttach(req.Msgid, req.Thumb, req.Extra) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 0, }) } +type DownloadAttachRequest struct { + // 消息 id + Msgid uint64 `json:"msgid"` + // 消息中的 thumb 字段 + Thumb string `json:"thumb"` + // 消息中的 extra 字段 + Extra string `json:"extra"` +} + +// @Summary 获取头像列表 +// @Produce json +// @Param body body GetAvatarsRequest true "获取头像列表参数" +// @Success 200 {object} []AvatarPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /avatars [post] +func (wc *Controller) getAvatars(c *gin.Context) { + + var req GetAvatarsRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + + sql := "SELECT usrName as UsrName, bigHeadImgUrl as BigHeadImgUrl, smallHeadImgUrl as SmallHeadImgUrl FROM ContactHeadImgUrl" + + if len(req.Wxids) > 0 { + for i, v := range req.Wxids { + req.Wxids[i] = strings.ReplaceAll(v, "'", "''") + } + sql += " WHERE usrName IN ('" + strings.Join(req.Wxids, "','") + "')" + } + + res := wc.CmdClient.DbSqlQuery("MicroMsg.db", sql) + + var result []AvatarPayload + if mapstructure.Decode(res, &result) == nil { + c.Set("Payload", result) + } else { + c.Set("Payload", res) + } + +} + +type GetAvatarsRequest struct { + // 用户 id 列表 + Wxids []string `json:"wxids"` +} + +type AvatarPayload struct { + // 用户 id + UsrName string `json:"usr_name,omitempty"` + // 大头像 url + BigHeadImgUrl string `json:"big_head_img_url,omitempty"` + // 小头像 url + SmallHeadImgUrl string `json:"small_head_img_url,omitempty"` +} + +// @Summary 获取完整通讯录 +// @Produce json +// @Success 200 {object} []ContactPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /contacts [post] +func (wc *Controller) getContacts(c *gin.Context) { + + c.Set("Payload", wc.CmdClient.GetContacts()) + +} + +type ContactPayload struct { + // 用户 id + Wxid string `json:"wxid,omitempty"` + // 微信号 + Code string `json:"code,omitempty"` + // 备注 + Remark string `json:"remark,omitempty"` + // 昵称 + Name string `json:"name,omitempty"` + // 国家 + Country string `json:"country,omitempty"` + // 省/州 + Province string `json:"province,omitempty"` + // 城市 + City string `json:"city,omitempty"` + // 性别 + Gender int32 `json:"gender,omitempty"` +} + +// @Summary 获取好友列表 +// @Produce json +// @Success 200 {object} []ContactPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /friends [post] +func (wc *Controller) getFriends(c *gin.Context) { + + c.Set("Payload", wc.CmdClient.GetFriends()) + +} + +// @Summary 根据wxid获取个人信息 +// @Produce json +// @Param body body GetInfoByWxidRequest true "根据wxid获取个人信息参数" +// @Success 200 {object} ContactPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /user_info [post] +func (wc *Controller) getInfoByWxid(c *gin.Context) { + + var req GetInfoByWxidRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + + c.Set("Payload", wc.CmdClient.GetInfoByWxid(req.Wxid)) + +} + +type GetInfoByWxidRequest struct { + // 用户 id + Wxid string `json:"wxid"` +} + +// @Summary 刷新朋友圈 +// @Produce json +// @Param body body RefreshPyqRequest true "刷新朋友圈参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /refresh_pyq [post] +func (wc *Controller) refreshPyq(c *gin.Context) { + + var req RefreshPyqRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.Set("Error", err) + return + } + + status := wc.CmdClient.RefreshPyq(req.Id) + + c.Set("Payload", CommonPayload{ + Success: status == 1, + }) + +} + +type RefreshPyqRequest struct { + // 分页 id + Id uint64 `json:"id"` +} + // @Summary 接受好友请求 // @Produce json -// @Param body body wcferry.Verification true "接受好友请求参数" -// @Success 200 {object} RespPayload +// @Param body body AcceptNewFriendRequest true "接受好友参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /accept_new_friend [post] func (wc *Controller) acceptNewFriend(c *gin.Context) { - var req wcferry.Verification + var req AcceptNewFriendRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -504,20 +820,31 @@ func (wc *Controller) acceptNewFriend(c *gin.Context) { status := wc.CmdClient.AcceptNewFriend(req.V3, req.V4, req.Scene) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type AcceptNewFriendRequest struct { + // 加密的用户名 + V3 string `json:"v3"` + // 验证信息 Ticket + V4 string `json:"v4"` + // 添加方式:17 名片,30 扫码 + Scene int32 `json:"scene"` +} + // @Summary 接受转账 // @Produce json -// @Param body body wcferry.Transfer true "接受转账请求参数" -// @Success 200 {object} RespPayload +// @Param body body ReceiveTransferRequest true "接受转账参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /receive_transfer [post] func (wc *Controller) receiveTransfer(c *gin.Context) { - var req wcferry.Transfer + var req ReceiveTransferRequest if err := c.ShouldBindJSON(&req); err != nil { c.Set("Error", err) return @@ -525,16 +852,27 @@ func (wc *Controller) receiveTransfer(c *gin.Context) { status := wc.CmdClient.ReceiveTransfer(req.Wxid, req.Tfid, req.Taid) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: status == 1, }) } +type ReceiveTransferRequest struct { + // 转账人 + Wxid string `json:"wxid,omitempty"` + // 转账id transferid + Tfid string `json:"tfid,omitempty"` + // Transaction id + Taid string `json:"taid,omitempty"` +} + // @Summary 开启推送消息到URL // @Produce json -// @Param body body ReceiverRequest true "消息推送请求参数" -// @Success 200 {object} RespPayload +// @Param body body ReceiverRequest true "推送消息到URL参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /enable_receiver [post] func (wc *Controller) enabledReceiver(c *gin.Context) { @@ -550,17 +888,24 @@ func (wc *Controller) enabledReceiver(c *gin.Context) { } err := wc.enableUrlReceiver(req.Url) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: err == nil, Error: err, }) } +type ReceiverRequest struct { + // 接收推送消息的 url + Url string `json:"url"` +} + // @Summary 关闭推送消息到URL // @Produce json -// @Param body body ReceiverRequest true "消息推送请求参数" -// @Success 200 {object} RespPayload +// @Param body body ReceiverRequest true "推送消息到URL参数" +// @Success 200 {object} CommonPayload +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" // @Router /disable_receiver [post] func (wc *Controller) disableReceiver(c *gin.Context) { @@ -571,9 +916,36 @@ func (wc *Controller) disableReceiver(c *gin.Context) { } err := wc.disableUrlReceiver(req.Url) - c.Set("Payload", RespPayload{ + c.Set("Payload", CommonPayload{ Success: err == nil, Error: err, }) } + +// @Summary 推送消息到Socket +// @Produce json +// @Tags websocket +// @Success 101 {string} string "Switching Protocols 响应" +// @Failure 400 {string} string "非法请求" +// @Failure 500 {string} string "内部服务器错误" +// @Router /socket_receiver [get] +func (wc *Controller) socketReceiver(c *gin.Context) { + + h := websocket.Handler(func(ws *websocket.Conn) { + wc.enableSocketReceiver(ws) + for { + var rq string + if err := websocket.Message.Receive(ws, &rq); err != nil { + logman.Error("read:error", "error", err) + break + } + } + wc.disableSocketReceiver(ws) + }) + + h.ServeHTTP(c.Writer, c.Request) + + c.Set("Payload", "连接已关闭") + +} diff --git a/clients/gohttp/httpd/wcfrest/receiver.go b/clients/gohttp/httpd/wcfrest/receiver.go index 542d988..772dbfa 100644 --- a/clients/gohttp/httpd/wcfrest/receiver.go +++ b/clients/gohttp/httpd/wcfrest/receiver.go @@ -5,41 +5,48 @@ import ( "github.com/opentdp/go-helper/logman" "github.com/opentdp/go-helper/request" + "golang.org/x/net/websocket" "github.com/opentdp/wechat-rest/wcferry" ) -var urlReceiverStat = false +var urlReceiverKey = "" var urlReceiverList = map[string]bool{} +var socketReceiverKey = "" +var socketReceiverList = map[*websocket.Conn]bool{} + func (wc *Controller) enableUrlReceiver(url string) error { - if !urlReceiverStat { - err := wc.EnrollReceiver(true, func(msg *wcferry.WxMsg) { + logman.Info("enable receiver", "url", url) + + if urlReceiverKey == "" { + key, err := wc.EnrollReceiver(true, func(msg *wcferry.WxMsg) { ret := wcferry.ParseWxMsg(msg) - for url := range urlReceiverList { - logman.Info("forward msg", "url", url, "Id", ret.Id) - go request.JsonPost(url, ret, request.H{}) + for u := range urlReceiverList { + logman.Info("call receiver", "url", u, "Id", ret.Id) + go request.JsonPost(u, ret, request.H{}) } }) if err != nil { return err } + urlReceiverKey = key } if _, ok := urlReceiverList[url]; ok { return errors.New("url already exists") } - urlReceiverStat = true urlReceiverList[url] = true - return nil } func (wc *Controller) disableUrlReceiver(url string) error { + logman.Info("disable receiver", "url", url) + if _, ok := urlReceiverList[url]; !ok { return errors.New("url not exists") } @@ -47,10 +54,52 @@ func (wc *Controller) disableUrlReceiver(url string) error { delete(urlReceiverList, url) if len(urlReceiverList) == 0 { - if err := wc.DisableReceiver(false); err != nil { - return err - } - urlReceiverStat = false + return wc.DisableReceiver(urlReceiverKey) + } + + return nil + +} + +func (wc *Controller) enableSocketReceiver(ws *websocket.Conn) error { + + logman.Info("enable receiver", "socket", ws.RemoteAddr().String()) + + if len(socketReceiverList) == 0 { + key, err := wc.EnrollReceiver(true, func(msg *wcferry.WxMsg) { + ret := wcferry.ParseWxMsg(msg) + for w := range socketReceiverList { + logman.Info("call receiver", "socket", ws.RemoteAddr().String(), "Id", ret.Id) + go websocket.JSON.Send(w, ret) + } + }) + if err != nil { + return err + } + socketReceiverKey = key + } + + if _, ok := socketReceiverList[ws]; ok { + return errors.New("socket already exists") + } + + socketReceiverList[ws] = true + return nil + +} + +func (wc *Controller) disableSocketReceiver(ws *websocket.Conn) error { + + logman.Info("disable receiver", "socket", ws.RemoteAddr().String()) + + if _, ok := socketReceiverList[ws]; !ok { + return errors.New("socket not exists") + } + + delete(socketReceiverList, ws) + + if len(socketReceiverList) == 0 { + return wc.DisableReceiver(socketReceiverKey) } return nil diff --git a/clients/gohttp/httpd/wcfrest/router.go b/clients/gohttp/httpd/wcfrest/router.go index 3fc3e62..3218f62 100644 --- a/clients/gohttp/httpd/wcfrest/router.go +++ b/clients/gohttp/httpd/wcfrest/router.go @@ -2,50 +2,53 @@ package wcfrest import ( "github.com/gin-gonic/gin" + "github.com/opentdp/wechat-rest/wclient" ) func Route(rg *gin.RouterGroup) { - ctrl := Controller{wclient.Register()} + ctrl := &Controller{wclient.Register()} - rg.GET("is_login", ctrl.isLogin) - rg.GET("self_wxid", ctrl.getSelfWxid) - rg.GET("user_info", ctrl.getUserInfo) - rg.GET("contacts", ctrl.getContacts) - rg.GET("friends", ctrl.getFriends) - rg.GET("user_info/:wxid", ctrl.getUserInfoByWxid) + rg.POST("is_login", ctrl.isLogin) + rg.POST("self_wxid", ctrl.getSelfWxid) + rg.POST("self_info", ctrl.getSelfInfo) + rg.POST("msg_types", ctrl.getMsgTypes) - rg.GET("db_names", ctrl.getDbNames) - rg.GET("db_tables/:db", ctrl.getDbTables) + rg.POST("db_names", ctrl.getDbNames) + rg.POST("db_tables", ctrl.getDbTables) rg.POST("db_query_sql", ctrl.dbSqlQuery) - rg.GET("msg_types", ctrl.getMsgTypes) - rg.GET("refresh_pyq/:id", ctrl.refreshPyq) - - rg.GET("chatrooms", ctrl.getChatRooms) - rg.GET("chatroom_members/:roomid", ctrl.getChatRoomMembers) - rg.GET("alias_in_chatroom/:wxid/:roomid", ctrl.getAliasInChatRoom) + rg.POST("chatrooms", ctrl.getChatRooms) + rg.POST("chatroom_members", ctrl.getChatRoomMembers) + rg.POST("alias_in_chatroom", ctrl.getAliasInChatRoom) rg.POST("invite_chatroom_members", ctrl.inviteChatroomMembers) rg.POST("add_chatroom_members", ctrl.addChatRoomMembers) rg.POST("del_chatroom_members", ctrl.delChatRoomMembers) - rg.GET("revoke_msg/:msgid", ctrl.revokeMsg) + rg.POST("revoke_msg", ctrl.revokeMsg) rg.POST("forward_msg", ctrl.forwardMsg) rg.POST("send_txt", ctrl.sendTxt) rg.POST("send_img", ctrl.sendImg) rg.POST("send_file", ctrl.sendFile) rg.POST("send_rich_text", ctrl.sendRichText) rg.POST("send_pat_msg", ctrl.sendPatMsg) - rg.POST("get_audio_msg", ctrl.getAudioMsg) - rg.POST("get_ocr_result", ctrl.getOcrResult) + rg.POST("audio_msg", ctrl.getAudioMsg) + rg.POST("ocr_result", ctrl.getOcrResult) rg.POST("download_image", ctrl.downloadImage) rg.POST("download_attach", ctrl.downloadAttach) + rg.POST("avatars", ctrl.getAvatars) + rg.POST("contacts", ctrl.getContacts) + rg.POST("friends", ctrl.getFriends) + rg.POST("user_info", ctrl.getInfoByWxid) + rg.POST("refresh_pyq", ctrl.refreshPyq) rg.POST("accept_new_friend", ctrl.acceptNewFriend) rg.POST("receive_transfer", ctrl.receiveTransfer) rg.POST("enable_receiver", ctrl.enabledReceiver) rg.POST("disable_receiver", ctrl.disableReceiver) + rg.GET("socket_receiver", ctrl.socketReceiver) + } diff --git a/clients/gohttp/httpd/wcfrest/types.go b/clients/gohttp/httpd/wcfrest/types.go deleted file mode 100644 index 79ef94b..0000000 --- a/clients/gohttp/httpd/wcfrest/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package wcfrest - -// 执行结果 -type RespPayload struct { - Success bool `json:"success,omitempty"` - Result string `json:"result,omitempty"` - Error error `json:"error,omitempty"` -} - -// 数据库查询参数 -type DbSqlQueryRequest struct { - Db string `json:"db"` - Sql string `json:"sql"` -} - -// 消息转发参数 -type ReceiverRequest struct { - Url string `json:"url"` -} - -// 获取音频消息参数 -type GetAudioMsgRequest struct { - Msgid uint64 `json:"msgid"` - Dir string `json:"path"` - Timeout int `json:"timeout"` -} - -// 获取OCR识别参数 -type GetOcrRequest struct { - Extra string `json:"extra"` - Timeout int `json:"timeout"` -} - -// 下载图片参数 -type DownloadImageRequest struct { - Msgid uint64 `json:"msgid"` - Extra string `json:"extra"` - Dir string `json:"dir"` - Timeout int `json:"timeout"` -} - -// 下载附件参数 -type DownloadAttachRequest struct { - Msgid uint64 `json:"msgid"` - Thumb string `json:"thumb"` - Extra string `json:"extra"` -} diff --git a/clients/gohttp/public/assets/icon.png b/clients/gohttp/public/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c0bb0371bd27b11b55f6e4556b1e65f982b6494c GIT binary patch literal 44615 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 L#K(N2Pb>Wgo@sQB literal 0 HcmV?d00001 diff --git a/clients/gohttp/public/assets/image/icon.svg b/clients/gohttp/public/assets/image/icon.svg deleted file mode 100644 index bfb4e71..0000000 --- a/clients/gohttp/public/assets/image/icon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/clients/gohttp/public/assets/logo.png b/clients/gohttp/public/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e0da086b9fad3a6e01795c065b9af1216d4589 GIT binary patch literal 45304 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 - + WeChat Rest diff --git a/clients/gohttp/public/swag/swagger.json b/clients/gohttp/public/swag/swagger.json deleted file mode 100644 index 865264c..0000000 --- a/clients/gohttp/public/swag/swagger.json +++ /dev/null @@ -1,1108 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "基于 WeChatFerry RPC 实现的微信接口,使用 Go 语言编写,无第三方运行时依赖,易于对接任意编程语言。", - "title": "WeChat Rest Api", - "contact": { - "name": "WeChatRest", - "url": "https://github.com/opentdp/wechat-rest" - }, - "license": { - "name": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "version": "v0.5.0" - }, - "basePath": "/api", - "paths": { - "/accept_new_friend": { - "post": { - "produces": [ - "application/json" - ], - "summary": "接受好友请求", - "parameters": [ - { - "description": "接受好友请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.Verification" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/add_chatroom_members": { - "post": { - "produces": [ - "application/json" - ], - "summary": "添加群成员", - "parameters": [ - { - "description": "增删群成员请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.MemberMgmt" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/alias_in_chatroom/{wxid}/{roomid}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取群成员昵称", - "parameters": [ - { - "type": "string", - "description": "wxid", - "name": "wxid", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "群id", - "name": "roomid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "string" - } - } - } - } - }, - "/chatroom_members/{roomid}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取群成员列表", - "parameters": [ - { - "type": "string", - "description": "群id", - "name": "roomid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/wcferry.RpcContact" - } - } - } - } - } - }, - "/chatrooms": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取群列表", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/wcferry.RpcContact" - } - } - } - } - } - }, - "/contacts": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取完整通讯录", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/wcferry.RpcContact" - } - } - } - } - } - }, - "/db_names": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取数据库列表", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - }, - "/db_query_sql": { - "post": { - "produces": [ - "application/json" - ], - "summary": "执行数据库查询", - "parameters": [ - { - "description": "数据库查询请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.DbSqlQueryRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/db_tables/{db}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取数据库表列表", - "parameters": [ - { - "type": "string", - "description": "数据库名", - "name": "db", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/wcferry.DbTable" - } - } - } - } - } - }, - "/del_chatroom_members": { - "post": { - "produces": [ - "application/json" - ], - "summary": "删除群成员", - "parameters": [ - { - "description": "增删群成员请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.MemberMgmt" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/disable_receiver": { - "post": { - "produces": [ - "application/json" - ], - "summary": "关闭推送消息到URL", - "parameters": [ - { - "description": "消息推送请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.ReceiverRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/download_attach": { - "post": { - "produces": [ - "application/json" - ], - "summary": "下载附件", - "parameters": [ - { - "description": "下载附件参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.DownloadAttachRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/download_image": { - "post": { - "produces": [ - "application/json" - ], - "summary": "下载图片", - "parameters": [ - { - "description": "下载图片参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.DownloadImageRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/enable_receiver": { - "post": { - "produces": [ - "application/json" - ], - "summary": "开启推送消息到URL", - "parameters": [ - { - "description": "消息推送请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.ReceiverRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/forward_msg": { - "post": { - "produces": [ - "application/json" - ], - "summary": "转发消息", - "parameters": [ - { - "description": "转发消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.ForwardMsg" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/friends": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取好友列表", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/wcferry.RpcContact" - } - } - } - } - } - }, - "/get_audio_msg": { - "post": { - "produces": [ - "application/json" - ], - "summary": "获取语音消息", - "parameters": [ - { - "description": "语音消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.GetAudioMsgRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/get_ocr_result": { - "post": { - "produces": [ - "application/json" - ], - "summary": "获取OCR识别结果", - "parameters": [ - { - "description": "文本请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcfrest.GetOcrRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/invite_chatroom_members": { - "post": { - "produces": [ - "application/json" - ], - "summary": "邀请群成员", - "parameters": [ - { - "description": "增删群成员请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.MemberMgmt" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/is_login": { - "get": { - "produces": [ - "application/json" - ], - "summary": "检查登录状态", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "boolean" - } - } - } - } - }, - "/msg_types": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取所有消息类型", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/receive_transfer": { - "post": { - "produces": [ - "application/json" - ], - "summary": "接受转账", - "parameters": [ - { - "description": "接受转账请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.Transfer" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/refresh_pyq/{id}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "刷新朋友圈", - "parameters": [ - { - "type": "integer", - "description": "朋友圈id", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/revoke_msg/{msgid}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "撤回消息", - "parameters": [ - { - "type": "integer", - "description": "消息id", - "name": "msgid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/self_wxid": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取登录账号wxid", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "string" - } - } - } - } - }, - "/send_file": { - "post": { - "produces": [ - "application/json" - ], - "summary": "发送文件消息", - "parameters": [ - { - "description": "文件消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.PathMsg" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/send_img": { - "post": { - "produces": [ - "application/json" - ], - "summary": "发送图片消息", - "parameters": [ - { - "description": "图片消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.PathMsg" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/send_pat_msg": { - "post": { - "produces": [ - "application/json" - ], - "summary": "拍一拍群友", - "parameters": [ - { - "description": "拍一拍请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.PatMsg" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/send_rich_text": { - "post": { - "produces": [ - "application/json" - ], - "summary": "发送卡片消息", - "parameters": [ - { - "description": "卡片消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.RichText" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/send_txt": { - "post": { - "produces": [ - "application/json" - ], - "summary": "发送文本消息", - "parameters": [ - { - "description": "文本消息请求参数", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/wcferry.TextMsg" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcfrest.RespPayload" - } - } - } - } - }, - "/user_info": { - "get": { - "produces": [ - "application/json" - ], - "summary": "获取登录账号个人信息", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcferry.UserInfo" - } - } - } - } - }, - "/user_info/{wxid}": { - "get": { - "produces": [ - "application/json" - ], - "summary": "根据wxid获取个人信息", - "parameters": [ - { - "type": "string", - "description": "wxid", - "name": "wxid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/wcferry.RpcContact" - } - } - } - } - } - }, - "definitions": { - "wcferry.DbTable": { - "type": "object", - "properties": { - "name": { - "description": "表名", - "type": "string" - }, - "sql": { - "description": "建表 SQL", - "type": "string" - } - } - }, - "wcferry.ForwardMsg": { - "type": "object", - "properties": { - "id": { - "description": "待转发消息 ID", - "type": "integer" - }, - "receiver": { - "description": "转发接收目标,群为 roomId,个人为 wxid", - "type": "string" - } - } - }, - "wcferry.MemberMgmt": { - "type": "object", - "properties": { - "roomid": { - "description": "要加的群ID", - "type": "string" - }, - "wxids": { - "description": "要加群的人列表,逗号分隔", - "type": "string" - } - } - }, - "wcferry.PatMsg": { - "type": "object", - "properties": { - "roomid": { - "description": "群 id", - "type": "string" - }, - "wxid": { - "description": "wxid", - "type": "string" - } - } - }, - "wcferry.PathMsg": { - "type": "object", - "properties": { - "path": { - "description": "要发送的图片的路径", - "type": "string" - }, - "receiver": { - "description": "消息接收人", - "type": "string" - } - } - }, - "wcferry.RichText": { - "type": "object", - "properties": { - "account": { - "description": "公众号 id", - "type": "string" - }, - "digest": { - "description": "摘要", - "type": "string" - }, - "name": { - "description": "显示名字", - "type": "string" - }, - "receiver": { - "description": "接收人", - "type": "string" - }, - "thumburl": { - "description": "缩略图", - "type": "string" - }, - "title": { - "description": "标题", - "type": "string" - }, - "url": { - "description": "链接", - "type": "string" - } - } - }, - "wcferry.RpcContact": { - "type": "object", - "properties": { - "city": { - "description": "城市", - "type": "string" - }, - "code": { - "description": "微信号", - "type": "string" - }, - "country": { - "description": "国家", - "type": "string" - }, - "gender": { - "description": "性别", - "type": "integer" - }, - "name": { - "description": "微信昵称", - "type": "string" - }, - "province": { - "description": "省/州", - "type": "string" - }, - "remark": { - "description": "备注", - "type": "string" - }, - "wxid": { - "description": "微信 id", - "type": "string" - } - } - }, - "wcferry.TextMsg": { - "type": "object", - "properties": { - "aters": { - "description": "要@的人列表,逗号分隔", - "type": "string" - }, - "msg": { - "description": "要发送的消息内容", - "type": "string" - }, - "receiver": { - "description": "消息接收人,当为群时可@", - "type": "string" - } - } - }, - "wcferry.Transfer": { - "type": "object", - "properties": { - "taid": { - "description": "Transaction id", - "type": "string" - }, - "tfid": { - "description": "转账id transferid", - "type": "string" - }, - "wxid": { - "description": "转账人", - "type": "string" - } - } - }, - "wcferry.UserInfo": { - "type": "object", - "properties": { - "home": { - "description": "文件/图片等父路径", - "type": "string" - }, - "mobile": { - "description": "手机号", - "type": "string" - }, - "name": { - "description": "昵称", - "type": "string" - }, - "wxid": { - "description": "微信ID", - "type": "string" - } - } - }, - "wcferry.Verification": { - "type": "object", - "properties": { - "scene": { - "description": "添加方式:17 名片,30 扫码", - "type": "integer" - }, - "v3": { - "description": "加密的用户名", - "type": "string" - }, - "v4": { - "description": "Ticket", - "type": "string" - } - } - }, - "wcfrest.DbSqlQueryRequest": { - "type": "object", - "properties": { - "db": { - "type": "string" - }, - "sql": { - "type": "string" - } - } - }, - "wcfrest.DownloadAttachRequest": { - "type": "object", - "properties": { - "extra": { - "type": "string" - }, - "msgid": { - "type": "integer" - }, - "thumb": { - "type": "string" - } - } - }, - "wcfrest.DownloadImageRequest": { - "type": "object", - "properties": { - "dir": { - "type": "string" - }, - "extra": { - "type": "string" - }, - "msgid": { - "type": "integer" - }, - "timeout": { - "type": "integer" - } - } - }, - "wcfrest.GetAudioMsgRequest": { - "type": "object", - "properties": { - "msgid": { - "type": "integer" - }, - "path": { - "type": "string" - }, - "timeout": { - "type": "integer" - } - } - }, - "wcfrest.GetOcrRequest": { - "type": "object", - "properties": { - "extra": { - "type": "string" - }, - "timeout": { - "type": "integer" - } - } - }, - "wcfrest.ReceiverRequest": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "wcfrest.RespPayload": { - "type": "object", - "properties": { - "error": {}, - "result": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } - } - } -} \ No newline at end of file diff --git a/clients/gohttp/public/swag/index.html b/clients/gohttp/public/swagger/index.html similarity index 91% rename from clients/gohttp/public/swag/index.html rename to clients/gohttp/public/swagger/index.html index 443938f..473c1bf 100644 --- a/clients/gohttp/public/swag/index.html +++ b/clients/gohttp/public/swagger/index.html @@ -5,7 +5,7 @@ - + WeChat Rest Document diff --git a/clients/gohttp/public/swagger/swagger.json b/clients/gohttp/public/swagger/swagger.json new file mode 100644 index 0000000..3b561d3 --- /dev/null +++ b/clients/gohttp/public/swagger/swagger.json @@ -0,0 +1,1701 @@ +{ + "swagger": "2.0", + "info": { + "description": "基于 WeChatFerry RPC 实现的微信接口,使用 Go 语言编写,无第三方运行时依赖,易于对接任意编程语言。", + "title": "WeChat Rest Api", + "contact": { + "name": "WeChatRest", + "url": "https://github.com/opentdp/wechat-rest" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "v0.8.0" + }, + "basePath": "/api", + "paths": { + "/accept_new_friend": { + "post": { + "produces": [ + "application/json" + ], + "summary": "接受好友请求", + "parameters": [ + { + "description": "接受好友参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.AcceptNewFriendRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/add_chatroom_members": { + "post": { + "produces": [ + "application/json" + ], + "summary": "添加群成员", + "parameters": [ + { + "description": "管理群成员参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ChatroomMembersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/alias_in_chatroom": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取群成员昵称", + "parameters": [ + { + "description": "获取群成员昵称参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetAliasInChatRoomRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/avatars": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取头像列表", + "parameters": [ + { + "description": "获取头像列表参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetAvatarsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.AvatarPayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/chatroom_members": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取群成员列表", + "parameters": [ + { + "description": "获取群成员列表参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetChatRoomMembersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.ContactPayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/chatrooms": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取群列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.ContactPayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/contacts": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取完整通讯录", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.ContactPayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/db_names": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取数据库列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/db_query_sql": { + "post": { + "produces": [ + "application/json" + ], + "summary": "执行数据库查询", + "parameters": [ + { + "description": "数据库查询参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.DbSqlQueryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/db_tables": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取数据库表列表", + "parameters": [ + { + "description": "获取数据库表列表参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetDbTablesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.DbTablePayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/del_chatroom_members": { + "post": { + "produces": [ + "application/json" + ], + "summary": "删除群成员", + "parameters": [ + { + "description": "管理群成员参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ChatroomMembersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/disable_receiver": { + "post": { + "produces": [ + "application/json" + ], + "summary": "关闭推送消息到URL", + "parameters": [ + { + "description": "推送消息到URL参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ReceiverRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/download_attach": { + "post": { + "produces": [ + "application/json" + ], + "summary": "下载附件", + "parameters": [ + { + "description": "下载附件参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.DownloadAttachRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/download_image": { + "post": { + "produces": [ + "application/json" + ], + "summary": "下载图片", + "parameters": [ + { + "description": "下载图片参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.DownloadImageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/enable_receiver": { + "post": { + "produces": [ + "application/json" + ], + "summary": "开启推送消息到URL", + "parameters": [ + { + "description": "推送消息到URL参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ReceiverRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/forward_msg": { + "post": { + "produces": [ + "application/json" + ], + "summary": "转发消息", + "parameters": [ + { + "description": "转发消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ForwardMsgRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/friends": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取好友列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/wcfrest.ContactPayload" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/get_audio_msg": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取语音消息", + "parameters": [ + { + "description": "获取语音消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetAudioMsgRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/get_ocr_result": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取OCR识别结果", + "parameters": [ + { + "description": "获取OCR识别结果参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetOcrRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/invite_chatroom_members": { + "post": { + "produces": [ + "application/json" + ], + "summary": "邀请群成员", + "parameters": [ + { + "description": "管理群成员参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ChatroomMembersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/is_login": { + "post": { + "produces": [ + "application/json" + ], + "summary": "检查登录状态", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/msg_types": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取所有消息类型", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/receive_transfer": { + "post": { + "produces": [ + "application/json" + ], + "summary": "接受转账", + "parameters": [ + { + "description": "接受转账参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.ReceiveTransferRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/refresh_pyq": { + "post": { + "produces": [ + "application/json" + ], + "summary": "刷新朋友圈", + "parameters": [ + { + "description": "刷新朋友圈参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.RefreshPyqRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/revoke_msg": { + "post": { + "produces": [ + "application/json" + ], + "summary": "撤回消息", + "parameters": [ + { + "description": "撤回消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.RevokeMsgRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/self_info": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取登录账号个人信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.UserInfoPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/self_wxid": { + "post": { + "produces": [ + "application/json" + ], + "summary": "获取登录账号wxid", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/send_file": { + "post": { + "produces": [ + "application/json" + ], + "summary": "发送文件消息", + "parameters": [ + { + "description": "发送文件消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.SendFileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/send_img": { + "post": { + "produces": [ + "application/json" + ], + "summary": "发送图片消息", + "parameters": [ + { + "description": "发送图片消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.SendImgRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/send_pat_msg": { + "post": { + "produces": [ + "application/json" + ], + "summary": "拍一拍群友", + "parameters": [ + { + "description": "拍一拍群友参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.SendPatMsgRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/send_rich_text": { + "post": { + "produces": [ + "application/json" + ], + "summary": "发送卡片消息", + "parameters": [ + { + "description": "发送卡片消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.SendRichTextRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/send_txt": { + "post": { + "produces": [ + "application/json" + ], + "summary": "发送文本消息", + "parameters": [ + { + "description": "发送文本消息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.SendTxtRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.CommonPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/socket_receiver": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "websocket" + ], + "summary": "推送消息到Socket", + "responses": { + "101": { + "description": "Switching Protocols 响应", + "schema": { + "type": "string" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + }, + "/user_info": { + "post": { + "produces": [ + "application/json" + ], + "summary": "根据wxid获取个人信息", + "parameters": [ + { + "description": "根据wxid获取个人信息参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wcfrest.GetInfoByWxidRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wcfrest.ContactPayload" + } + }, + "400": { + "description": "非法请求", + "schema": { + "type": "string" + } + }, + "500": { + "description": "内部服务器错误", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "wcfrest.AcceptNewFriendRequest": { + "type": "object", + "properties": { + "scene": { + "description": "添加方式:17 名片,30 扫码", + "type": "integer" + }, + "v3": { + "description": "加密的用户名", + "type": "string" + }, + "v4": { + "description": "验证信息 Ticket", + "type": "string" + } + } + }, + "wcfrest.AvatarPayload": { + "type": "object", + "properties": { + "big_head_img_url": { + "description": "大头像 url", + "type": "string" + }, + "small_head_img_url": { + "description": "小头像 url", + "type": "string" + }, + "usr_name": { + "description": "用户 id", + "type": "string" + } + } + }, + "wcfrest.ChatroomMembersRequest": { + "type": "object", + "properties": { + "roomid": { + "description": "群聊 id", + "type": "string" + }, + "wxids": { + "description": "用户 id 列表", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wcfrest.CommonPayload": { + "type": "object", + "properties": { + "error": { + "description": "错误信息" + }, + "result": { + "description": "返回结果", + "type": "string" + }, + "success": { + "description": "是否成功", + "type": "boolean" + } + } + }, + "wcfrest.ContactPayload": { + "type": "object", + "properties": { + "city": { + "description": "城市", + "type": "string" + }, + "code": { + "description": "微信号", + "type": "string" + }, + "country": { + "description": "国家", + "type": "string" + }, + "gender": { + "description": "性别", + "type": "integer" + }, + "name": { + "description": "昵称", + "type": "string" + }, + "province": { + "description": "省/州", + "type": "string" + }, + "remark": { + "description": "备注", + "type": "string" + }, + "wxid": { + "description": "用户 id", + "type": "string" + } + } + }, + "wcfrest.DbSqlQueryRequest": { + "type": "object", + "properties": { + "db": { + "description": "数据库名称", + "type": "string" + }, + "sql": { + "description": "待执行的 SQL", + "type": "string" + } + } + }, + "wcfrest.DbTablePayload": { + "type": "object", + "properties": { + "name": { + "description": "表名", + "type": "string" + }, + "sql": { + "description": "建表 SQL", + "type": "string" + } + } + }, + "wcfrest.DownloadAttachRequest": { + "type": "object", + "properties": { + "extra": { + "description": "消息中的 extra 字段", + "type": "string" + }, + "msgid": { + "description": "消息 id", + "type": "integer" + }, + "thumb": { + "description": "消息中的 thumb 字段", + "type": "string" + } + } + }, + "wcfrest.DownloadImageRequest": { + "type": "object", + "properties": { + "dir": { + "description": "存储路径", + "type": "string" + }, + "extra": { + "description": "消息中的 extra 字段", + "type": "string" + }, + "msgid": { + "description": "消息 id", + "type": "integer" + }, + "timeout": { + "description": "超时重试次数", + "type": "integer" + } + } + }, + "wcfrest.ForwardMsgRequest": { + "type": "object", + "properties": { + "id": { + "description": "待转发消息 id", + "type": "integer" + }, + "receiver": { + "description": "转发接收人或群的 id 列表", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wcfrest.GetAliasInChatRoomRequest": { + "type": "object", + "properties": { + "roomid": { + "description": "群聊 id", + "type": "string" + }, + "wxid": { + "description": "用户 id", + "type": "string" + } + } + }, + "wcfrest.GetAudioMsgRequest": { + "type": "object", + "properties": { + "msgid": { + "description": "消息 id", + "type": "integer" + }, + "path": { + "description": "存储路径", + "type": "string" + }, + "timeout": { + "description": "超时重试次数", + "type": "integer" + } + } + }, + "wcfrest.GetAvatarsRequest": { + "type": "object", + "properties": { + "wxids": { + "description": "用户 id 列表", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wcfrest.GetChatRoomMembersRequest": { + "type": "object", + "properties": { + "roomid": { + "description": "群聊 id", + "type": "string" + } + } + }, + "wcfrest.GetDbTablesRequest": { + "type": "object", + "properties": { + "db": { + "description": "数据库名称", + "type": "string" + } + } + }, + "wcfrest.GetInfoByWxidRequest": { + "type": "object", + "properties": { + "wxid": { + "description": "用户 id", + "type": "string" + } + } + }, + "wcfrest.GetOcrRequest": { + "type": "object", + "properties": { + "extra": { + "description": "消息中的 extra 字段", + "type": "string" + }, + "timeout": { + "description": "超时重试次数", + "type": "integer" + } + } + }, + "wcfrest.ReceiveTransferRequest": { + "type": "object", + "properties": { + "taid": { + "description": "Transaction id", + "type": "string" + }, + "tfid": { + "description": "转账id transferid", + "type": "string" + }, + "wxid": { + "description": "转账人", + "type": "string" + } + } + }, + "wcfrest.ReceiverRequest": { + "type": "object", + "properties": { + "url": { + "description": "接收推送消息的 url", + "type": "string" + } + } + }, + "wcfrest.RefreshPyqRequest": { + "type": "object", + "properties": { + "id": { + "description": "分页 id", + "type": "integer" + } + } + }, + "wcfrest.RevokeMsgRequest": { + "type": "object", + "properties": { + "msgid": { + "description": "消息 id", + "type": "integer" + } + } + }, + "wcfrest.SendFileRequest": { + "type": "object", + "properties": { + "path": { + "description": "文件路径", + "type": "string" + }, + "receiver": { + "description": "接收人或群的 id", + "type": "string" + } + } + }, + "wcfrest.SendImgRequest": { + "type": "object", + "properties": { + "path": { + "description": "图片路径", + "type": "string" + }, + "receiver": { + "description": "接收人或群的 id", + "type": "string" + } + } + }, + "wcfrest.SendPatMsgRequest": { + "type": "object", + "properties": { + "roomid": { + "description": "群 id", + "type": "string" + }, + "wxid": { + "description": "用户 id", + "type": "string" + } + } + }, + "wcfrest.SendRichTextRequest": { + "type": "object", + "properties": { + "account": { + "description": "填公众号 id 可以显示对应的头像(gh_ 开头的)", + "type": "string" + }, + "digest": { + "description": "摘要,三行", + "type": "string" + }, + "name": { + "description": "左下显示的名字", + "type": "string" + }, + "receiver": { + "description": "接收人或群的 id", + "type": "string" + }, + "thumburl": { + "description": "缩略图的链接", + "type": "string" + }, + "title": { + "description": "标题,最多两行", + "type": "string" + }, + "url": { + "description": "点击后跳转的链接", + "type": "string" + } + } + }, + "wcfrest.SendTxtRequest": { + "type": "object", + "properties": { + "aters": { + "description": "需要 At 的用户 id 列表", + "type": "array", + "items": { + "type": "string" + } + }, + "msg": { + "description": "消息内容", + "type": "string" + }, + "receiver": { + "description": "接收人或群的 id", + "type": "string" + } + } + }, + "wcfrest.UserInfoPayload": { + "type": "object", + "properties": { + "home": { + "description": "文件/图片等父路径", + "type": "string" + }, + "mobile": { + "description": "手机号", + "type": "string" + }, + "name": { + "description": "昵称", + "type": "string" + }, + "wxid": { + "description": "用户 id", + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/clients/gohttp/start.bat b/clients/gohttp/start-dev.bat similarity index 97% rename from clients/gohttp/start.bat rename to clients/gohttp/start-dev.bat index 7bbb50b..e0829e9 100644 --- a/clients/gohttp/start.bat +++ b/clients/gohttp/start-dev.bat @@ -18,3 +18,5 @@ if exist .local.yml ( ) else ( go run main.go ) + +cmd /k