Merge pull request #118 from rehiy/master

Impl websocket and change all http-get to http-post
This commit is contained in:
Changhua 2024-01-25 17:19:17 +08:00 committed by GitHub
commit 2ef366beb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 2449 additions and 1449 deletions

View File

@ -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)

View File

@ -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,
}

View File

@ -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)
}

View File

@ -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",
})

View File

@ -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 # 是否打印收到的消息

View File

@ -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

View File

@ -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=

View File

@ -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` 生成对应的客户端

View File

@ -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()

View File

@ -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)
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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)
}

View File

@ -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"`
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" width="160" height="160" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<path
d="m 133.19656,100.76048 c -4.65698,0.18313 -9.34964,0.70676 -13.95132,1.61149 -1.23893,0.23402 -2.5871,0.61836 -3.82856,1.0658 -0.64877,2.16647 -1.20763,4.36035 -1.79204,6.54542 1.58555,-0.30164 3.15751,-0.66922 4.73692,-1.00089 -0.61371,8.95742 -1.27989,17.91144 -1.99384,26.86171 -0.29442,3.62953 -0.52943,7.26341 -0.75616,10.89788 1.86774,-0.3579 3.55939,-1.29744 5.38281,-1.81763 1.27468,-0.42814 2.55455,-0.84488 3.80692,-1.33534 0.31406,-0.69306 0.1844,-1.52113 0.31831,-2.269 0.32993,-3.64635 0.65961,-7.29274 0.98978,-10.93908 2.7618,0.3073 5.57164,0.4131 8.31393,-0.12062 4.80385,-0.85929 9.45139,-3.20292 12.62313,-6.96543 2.55696,-3.1713 3.25843,-7.42864 3.05047,-11.39782 -0.10968,-3.07735 -1.47498,-6.16018 -3.95971,-8.04266 -3.08122,-2.33261 -7.0554,-2.8864 -10.80923,-3.07292 -0.71003,-0.0286 -1.42093,-0.0345 -2.13141,-0.0208 z m -0.40793,6.55367 c 2.41792,-0.009 5.14573,0.77883 6.45773,2.9691 1.10267,1.89282 0.79124,4.19106 0.49122,6.25738 -0.56573,3.40307 -3.15192,6.12911 -6.14793,7.64676 -1.519,0.73784 -3.24968,1.32799 -4.95463,1.0606 -1.2847,-0.20548 -1.93156,-1.60186 -1.77353,-2.7952 0.3007,-3.52247 0.57505,-7.04776 0.94908,-10.56363 0.12937,-0.83562 0.0553,-1.89107 0.36811,-2.75357 0.69887,-1.36058 2.39457,-1.67662 3.78561,-1.78204 0.27361,-0.0292 0.54966,-0.032 0.82434,-0.0393 z m -49.59709,-5.76679 c -1.177219,0.0152 -2.290464,0.0446 -3.406471,0.10975 -2.200556,0.0982 -4.570882,0.38716 -6.280955,1.91972 -2.294143,2.05844 -4.51393,4.20067 -6.779415,6.2914 2.032068,-0.34075 4.027356,-0.85809 6.035092,-1.31471 -0.29277,4.42 -0.583247,8.87329 -0.930953,13.24535 -0.247701,3.58438 -0.608065,7.18985 -0.865832,10.7856 -0.197329,2.45578 -0.299965,4.92062 -0.603051,7.36657 -1.182286,2.26098 -2.366388,4.521 -3.549373,6.78162 4.255462,0.0251 8.527429,0.0413 12.750612,-0.56007 6.773416,-0.89346 13.542359,-3.03099 19.074356,-7.13985 3.50579,-2.61935 6.21384,-6.26482 7.70233,-10.38067 1.32963,-3.69497 1.66725,-7.71313 1.26404,-11.60479 -0.38504,-3.63318 -2.20178,-7.07468 -4.99795,-9.42894 -2.3182,-2.05977 -4.964601,-3.82061 -7.963403,-4.70352 -3.686463,-1.19181 -7.599161,-1.43469 -11.449027,-1.36751 z m 2.274305,6.29932 c 3.134894,-0.0473 6.255769,1.4461 8.091177,4.00535 2.54448,3.501 3.754103,7.91422 3.431022,12.22534 -0.226322,4.14823 -1.236859,8.49082 -4.054824,11.67783 -2.576486,2.9221 -6.459033,4.43752 -10.304517,4.56919 -1.051815,0.0491 -2.105697,0.0482 -3.1583,0.0775 0.928832,-9.24255 1.484356,-18.51758 2.433733,-27.75794 0.121951,-1.23355 0.244303,-2.46709 0.365783,-3.7007 1.013568,-0.47883 2.038785,-1.10465 3.195926,-1.09668 z m -24.695317,-6.55434 c -6.616793,0.30247 -13.247083,0.59067 -19.822764,0.9827 -2.93934,0.16726 -5.878891,0.33118 -8.818307,0.49714 -0.939787,2.46811 -1.879559,4.93619 -2.819346,7.40428 3.678262,0.014 7.357493,-0.0451 11.034605,0.0727 -0.310173,4.62265 -0.461625,9.25797 -0.996882,13.86248 -0.577369,7.54214 -1.162189,15.08413 -1.672169,22.63031 3.114046,-1.09029 6.210004,-2.23218 9.316888,-3.3429 1.026749,-11.05353 2.082729,-22.10437 3.083613,-33.15981 3.507509,0.003 7.015208,-0.005 10.522592,0.003 0.834619,-1.78324 1.394282,-3.69399 2.119057,-5.52802 0.394904,-1.11279 0.836468,-2.39191 1.298175,-3.551 -1.081817,0.043 -2.16364,0.0859 -3.245462,0.12887 z m -39.28928,44.37272 C 10.397706,140.70341 6.3360532,121.72682 13.320607,107.53765 22.554039,88.779899 43.351122,78.588439 69.210903,80.148932 c 6.22913,0.375816 13.96527,0.694936 17.19144,0.708996 3.226156,0.01519 5.86543,0.0874 5.865084,0.162644 -0.005,0.953525 -10.29208,2.919739 -20.225093,3.865549 -27.253018,2.594993 -44.443892,11.736712 -52.420422,27.876019 -7.053434,14.27163 -1.767181,32.39927 9.462007,32.44701 2.179213,0.008 2.608039,0.21013 1.700552,0.79658 -1.719469,1.11122 -6.446537,0.93806 -9.303241,-0.34086 z M 78.514084,77.146886 C 65.119128,75.809604 63.292384,75.17957 70.828794,74.496338 105.03532,71.395227 126.34754,64.306892 134.8055,53.217996 c 2.77337,-3.636068 2.71149,-3.660615 -6.62342,-2.627369 -19.34773,2.141583 -35.197284,7.519706 -45.678459,15.499815 -5.65313,4.304141 -6.193784,4.444816 -3.369456,0.876576 12.153145,-15.354163 31.900745,-24.116341 58.894295,-26.131842 13.84256,-1.033551 12.81731,-1.410848 11.17819,4.113611 -6.98636,23.546803 -34.01928,35.859399 -70.692566,32.198099 z M 67.217444,69.080518 C 67.036566,51.005964 83.478129,29.343522 107.27263,16.306046 c 8.416,-4.611291 8.04996,-4.856276 8.04977,5.387472 -2e-4,9.674321 -1.76661,14.279227 -8.12002,21.168179 -4.00687,4.344647 -4.01654,4.293006 -0.68287,-3.640402 2.0729,-4.933034 3.6287,-14.561528 2.35291,-14.561528 -6.42277,0 -32.466361,27.807852 -38.917446,41.553832 -2.592261,5.523548 -2.709703,5.646553 -2.73753,2.866919 z" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -5,14 +5,14 @@
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="shortcut icon" type="image/svg" href="/assets/image/icon.svg" />
<link rel="shortcut icon" type="image/png" href="/assets/icon.png" />
<link rel="stylesheet" href="/assets/style.css" />
<title>WeChat Rest</title>
</head>
<body>
<ul>
<li><a href="/swag/">Api Document</a></li>
<li><a href="/swagger/">Api Document</a></li>
</ul>
</body>

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="shortcut icon" type="image/svg" href="/assets/image/icon.svg" />
<link rel="shortcut icon" type="image/png" href="/assets/icon.png" />
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.min.css" />
<link rel="stylesheet" href="/assets/style.css" />
<title>WeChat Rest Document</title>

File diff suppressed because it is too large Load Diff

View File

@ -18,3 +18,5 @@ if exist .local.yml (
) else (
go run main.go
)
cmd /k