├── .github ├── dependabot.yml └── workflows │ ├── del_runs.yml │ ├── updateDocsClient.yml │ ├── updateDocsServer.yml │ └── wechatBot Go Build.yml ├── .gitignore ├── .gitmodules ├── 4u ├── README.md └── wechatyGateway.sh ├── Server ├── General │ ├── DataProcessing.go │ ├── Gateway.go │ ├── Pretreatment.go │ └── enter.go ├── example.yaml ├── go.mod ├── go.sum ├── main.go └── test │ ├── test.go │ └── 祝福助手.go ├── docs ├── Architecture.md ├── Image │ └── bot.jpg ├── Log.md ├── README.md ├── Update.md └── submodule.md └── padlocal ├── Dockerfile ├── README.md ├── package.json └── wechatyGateway.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "npm" # See documentation for possible values 13 | directory: "/" # Location of package manifests 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/del_runs.yml: -------------------------------------------------------------------------------- 1 | name: Delete old workflow runs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * MON' 6 | workflow_dispatch: 7 | inputs: 8 | days: 9 | description: 'Number of days.' 10 | required: true 11 | default: "10" 12 | minimum_runs: 13 | description: 'The minimum runs to keep for each workflow.' 14 | required: true 15 | default: "6" 16 | 17 | jobs: 18 | del_runs: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Delete workflow runs 22 | uses: Mattraks/delete-workflow-runs@main 23 | with: 24 | token: ${{ secrets.TOKEN_GITHUB }} 25 | repository: ${{ github.repository }} 26 | retain_days: ${{ github.event.inputs.days }} 27 | keep_minimum_runs: 3 -------------------------------------------------------------------------------- /.github/workflows/updateDocsClient.yml: -------------------------------------------------------------------------------- 1 | name: updated docs client 2 | 3 | on: 4 | repository_dispatch: 5 | types: ["The beta version is updated"] 6 | 7 | jobs: 8 | Receive_Message: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Private Actions Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | path: Go-Wechaty-Bot 16 | - 17 | name: Private Actions Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | repository: ${{ github.repository_owner }}/gobot 21 | token: ${{ secrets.TOKEN_GITHUB }} 22 | path: gobot 23 | - 24 | name: Upgrade Docs 25 | run: | 26 | cd gobot 27 | event_type=$(git log --pretty=format:“%s” $(git rev-parse HEAD) -1 | awk '{print}') 28 | rm -rf .git 29 | cp -r ../Go-Wechaty-Bot/.git . 30 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 31 | git config --local user.name "github-actions[bot]" 32 | git add --all -- ':!Server' 33 | git commit -m ${event_type} -a 34 | git push -------------------------------------------------------------------------------- /.github/workflows/updateDocsServer.yml: -------------------------------------------------------------------------------- 1 | name: updated docs server 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: # 这里是用来指定哪个文件更改,才会触发的 7 | - 'docs/**' 8 | - '4u/**' 9 | - 'padlocal/**' 10 | - 'xp/**' 11 | 12 | jobs: 13 | Send_Message: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - 17 | name: Repository Dispatch 18 | uses: peter-evans/repository-dispatch@v2 19 | with: 20 | token: ${{ secrets.TOKEN_GITHUB }} 21 | repository: ${{ github.repository_owner }}/Go-Wechaty-Bot 22 | event-type: The beta version is updated 23 | client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' -------------------------------------------------------------------------------- /.github/workflows/wechatBot Go Build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseName: 7 | description: 'Set release name' 8 | required: true 9 | default: "Use Go & SDK Wechaty" 10 | tagName: 11 | description: 'Set tag name' 12 | required: false 13 | type: string 14 | # watch: 15 | # types: [started] 16 | 17 | jobs: 18 | Build: 19 | name: Build 20 | runs-on: ubuntu-latest 21 | steps: 22 | - 23 | name: Private Actions Checkout 24 | uses: actions/checkout@v4 25 | - 26 | name: Get Data 27 | id: time 28 | run: echo "dir=$(date +'%Y-%m-%d/%H:%M:%S')" >> $GITHUB_OUTPUT 29 | - 30 | name: Get commitId 31 | id: commitId 32 | run: | 33 | git submodule init 34 | git submodule update 35 | echo "dir=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 36 | - 37 | name: Build with xgo 38 | uses: crazy-max/ghaction-xgo@v3 39 | with: 40 | xgo_version: latest 41 | go_version: ${{ matrix.go_version }} 42 | dest: bin 43 | prefix: Go-Wechaty-Bot 44 | targets: android/386, android/amd64, android/arm, android/arm64, darwin/amd64, darwin/arm64, freebsd/386, freebsd/amd64, freebsd/arm, freebsd/arm64, linux/386, linux/amd64, linux/arm, linux/arm64, linux/mips, linux/mips64, linux/mips64le, linux/mipsle, linux/ppc64, linux/ppc64le, linux/riscv64, linux/s390x, netbsd/386, netbsd/amd64, netbsd/arm, netbsd/arm64, openbsd/386, openbsd/amd64, openbsd/arm, openbsd/arm64, openbsd/mips64, windows/386, windows/amd64, windows/arm, windows/arm64 45 | v: false 46 | x: false 47 | race: false 48 | ldflags: -s -w -X main.buildTime=${{ steps.time.outputs.dir }} -X main.version=${{ github.event.inputs.tagName }} -X main.commitId=${{ steps.commitId.outputs.dir }} -X main.author=XRSec 49 | buildmode: default 50 | working_dir: Server 51 | - 52 | name: Delete old Releases 53 | uses: dev-drprasad/delete-older-releases@v0.3.2 54 | with: 55 | repo: ${{ github.repository }} 56 | keep_latest: 3 57 | delete_tag_pattern: "" 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUB }} 60 | - 61 | name: Make release 62 | uses: softprops/action-gh-release@v1 63 | with: 64 | files: "Server/bin/**" 65 | body_path: docs/Update.md 66 | name: ${{ github.event.inputs.releaseName }} 67 | tag_name: ${{ github.event.inputs.tagName }} 68 | draft: false 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUB }} 71 | - name: Get Data 72 | id: getDingData 73 | run: | 74 | set -ex 75 | # 输出仓库名 76 | REPOSITORY="${{GITHUB.REPOSITORY}}" 77 | echo "REPOSITORY=${REPOSITORY#*/}" >> $GITHUB_OUTPUT 78 | 79 | # 获取用户仓库信息 80 | # RESPONSE="$(curl -sLm 10 https://api.github.com/repos/${{ GITHUB.REPOSITORY }})" 81 | # 建议填写自己的 TOKEN 82 | RESPONSE="$(curl -sLm 10 https://api.github.com/repos/${{ GITHUB.REPOSITORY }} -H "Authorization: token ${{ SECRETS.TOKEN_GITHUB }}")" 83 | 84 | # 获取 用户仓库 设置的 描述,如果为空,可能是没有使用 TOKEN 85 | DESCRIPTION="$(jq -r .description <(echo ${RESPONSE}))" 86 | echo "DESCRIPTION=${DESCRIPTION}" >> $GITHUB_OUTPUT 87 | 88 | # 获取 用户仓库 设置的 URL, 如果没有就输出 Github 地址 89 | URL="$(jq -r .homepage <(echo ${RESPONSE}))" 90 | if [[ "${URL}" == "null" || "${URL}" == "" ]]; then 91 | echo "URL=${{ GITHUB.SERVER_URL }}/${{ GITHUB.REPOSITORY }}" >> $GITHUB_OUTPUT 92 | else 93 | echo "URL=${URL}" >> $GITHUB_OUTPUT 94 | fi 95 | - 96 | name: Send dingding notify 97 | uses: zcong1993/actions-ding@master 98 | with: 99 | dingToken: ${{ SECRETS.DING_TOKEN }} 100 | secret: ${{ SECRETS.DING_SECRET }} 101 | body: | 102 | { 103 | "msgtype": "link", 104 | "link": { 105 | "text": "${{ steps.getDingData.outputs.DESCRIPTION }}", 106 | "title": "${{ steps.getDingData.outputs.REPOSITORY }} WorkFlow ${{ GITHUB.JOB }} Success!", 107 | "picUrl": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png", 108 | "messageUrl": "${{ steps.getDingData.outputs.URL }}" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | Server/test/test 3 | config.yaml 4 | tmp 5 | main.exe 6 | data.json 7 | test.sh 8 | .vscode 9 | logs 10 | *.memory-card.json 11 | node_modules 12 | package-lock.json 13 | yarn.lock 14 | screenlog.* 15 | friendExist.txt -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "xp"] 2 | path = xp 3 | url = git@github.com:XRSec/Go-Wechaty-Bot-XP.git 4 | [submodule "Server/Plug"] 5 | path = Server/Plug 6 | url = git@github.com:XRSec/Go-Wechaty-Bot-Plugin.git 7 | -------------------------------------------------------------------------------- /4u/README.md: -------------------------------------------------------------------------------- 1 | ## Go-Wechaty-Bot 4U PROTOCOL 2 | 3 | > 仅供学习使用,*请勿用于非法用途*! 4 | 5 | [1]: https://img.shields.io/badge/puppet-xp-blue 6 | [2]: https://img.shields.io/badge/puppet-padlocal-blue 7 | [3]: https://img.shields.io/badge/puppet-4u-blue 8 | [5]: https://github.com/XRSec/Go-Wechaty-Bot-XP 9 | [6]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/padlocal#go-wechaty-bot-padlcoal-protocol 10 | [7]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/4u#go-wechaty-bot-4u-protocol 11 | 12 | [![puppet-xp][1]][5] 〰️ [![puppet-padlocal][2]][6] 〰️ [![puppet-4u][3]][7] 「 Select Gateway 」 13 | 14 | ## Info 15 | 16 | ### Glossary 17 | 18 | ```ini 19 | Gateway : puppet-wechat4u 20 | Server: go-wechaty 21 | ``` 22 | 23 | ### Architecture 24 | 25 | ```mermaid 26 | flowchart LR 27 | Polyglot-->Python 28 | Polyglot-->Go 29 | Polyglot -->Rust 30 | Python-->Grpc 31 | Go-->Grpc 32 | Rust-->Grpc 33 | Grpc-->Gateway{Gateway}-->Wechat4U 34 | Wechat4U-->微信 35 | ``` 36 | 37 | ## ⇲ Use 38 | 39 | ### Init (depend main.General) 40 | 41 | 1. Checkout branch 42 | 43 | ```bash 44 | cd 4u 45 | ``` 46 | 47 | 2. Generate Token 48 | 49 | ```bash 50 | # Generate Token 51 | WECHATY_TOKEN:curl -s https://www.uuidgenerator.net/api/version4 52 | WECHATY_PUPPET_SERVICE_TOKEN:"insecure_" + WECHATY_TOKEN 53 | # WECHATY_TOKEN WECHATY_PUPPET_SERVICE_TOKEN 可同可不同 54 | ``` 55 | 56 | 3. Modifying a Configuration File 57 | 58 | ```bash 59 | # wechatyGateway.bat 60 | @set WECHATY_TOKEN=5f3029c0-0f46-4436-bdc6-02efcbad3309 61 | @set WECHATY_PUPPET_SERVICE_TOKEN=insecure_34bf8353-0874-4b29-851d-e8a2502fc747 62 | @set WECHATY_PUPPET_SERVER_PORT=25000 63 | ``` 64 | 65 | 72 | 73 | ### Start Server 74 | 75 | ```bash 76 | cd 4u && bash ./wechatyGateway.sh # Start puppet-wechat4u Gateway 77 | cd server && go run main.go # Start Server 78 | ``` 79 | -------------------------------------------------------------------------------- /4u/wechatyGateway.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # set -ex 3 | 4 | export WECHATY_PUPPET="wechaty-puppet-wechat" 5 | export WECHATY_PUPPET_PADLOCAL_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxx" 6 | # export WECHATY_PUPPET_PADLOCAL_TOKEN="$(curl -s https://www.uuidgenerator.net/api/version4)" 7 | 8 | export WECHATY_TOKEN="insecure_xxxxxxxxxxxxxxxxxxxxxxxxx" 9 | # export WECHATY_PUPPET_SERVICE_TOKEN="insecure_$(curl -s https://www.uuidgenerator.net/api/version4)" 10 | export WECHATY_PUPPET_SERVER_PORT="25000" 11 | export WECHATY_LOG="verbose" # silent | error | warn | info | verbose | silly 12 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true" 13 | 14 | if [ ! -n "$1" ]; then 15 | export tag=0.56 16 | else 17 | export tag=$1 18 | fi 19 | 20 | touch "$(pwd)/${WECHATY_TOKEN}.memory-card.json" 21 | 22 | docker run -ti --rm \ 23 | --name Go-Wechaty-Bot \ 24 | -e WECHATY_TOKEN \ 25 | -e TZ=Asia/Shanghai \ 26 | -e WECHATY_PUPPET_SERVICE_TOKEN \ 27 | -e WECHATY_LOG \ 28 | -e WECHATY_PUPPET \ 29 | -e WECHATY_PUPPET_SERVER_PORT \ 30 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \ 31 | -p "${WECHATY_PUPPET_SERVER_PORT}:${WECHATY_PUPPET_SERVER_PORT}" \ 32 | -v "$(pwd)/${WECHATY_TOKEN}.memory-card.json:/wechaty/${WECHATY_TOKEN}.memory-card.json" \ 33 | wechaty/wechaty:"${tag}" -------------------------------------------------------------------------------- /Server/General/DataProcessing.go: -------------------------------------------------------------------------------- 1 | package General 2 | 3 | import ( 4 | "fmt" 5 | "github.com/blinkbean/dingtalk" 6 | "github.com/wechaty/go-wechaty/wechaty" 7 | "gopkg.in/natefinch/lumberjack.v2" 8 | "io" 9 | "log" 10 | "os" 11 | "os/exec" 12 | "path" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | 17 | "github.com/sirupsen/logrus" 18 | "github.com/spf13/viper" 19 | ) 20 | 21 | func init() { 22 | // 出厂设置 23 | RootPath, _ := os.UserConfigDir() 24 | RootPath = RootPath + "/Go-Wechaty-Bot" 25 | viper.Set("RootPath", RootPath) 26 | viper.Set("LogPath", RootPath+"/logs") 27 | //logrus.Printf("Logs Path: [%v]", viper.GetString("LogPath")) 28 | // 初始化viper 29 | ViperInit() 30 | // 初始化日志 31 | logInit() 32 | } 33 | 34 | func NewGlobleService() *GlobleService { 35 | return globleServiceInstance 36 | } 37 | 38 | func (g *GlobleService) SetBot(bot *wechaty.Wechaty) { 39 | g.Bot = bot 40 | } 41 | 42 | func (g *GlobleService) GetBot() *wechaty.Wechaty { 43 | return g.Bot 44 | } 45 | 46 | /* 47 | 初始化日志 48 | */ 49 | func ViperInit() { 50 | // 初始化配置文件 51 | //fileInit(false, viper.GetString("RootPath")) 52 | //logrus.Printf("Viper Config Path: [%v]", viper.GetString("RootPath")+"/config.yaml") 53 | viper.SetConfigName("config") 54 | viper.SetConfigType("yaml") 55 | viper.AddConfigPath(viper.GetString("RootPath")) 56 | fileInit(true, viper.GetString("RootPath")) 57 | fileInit(false, viper.GetString("RootPath")+"/config.yaml") 58 | if err = viper.ReadInConfig(); err != nil { 59 | logrus.Errorf("[viper] 读取配置文件失败, Error: [%v] CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 60 | } else { 61 | logrus.Infof("[viper] 读取配置文件成功") 62 | } 63 | if viper.GetString("WECHATY.WECHATY_TOKEN") == "" && viper.GetString("BOT.ADMINID") == "" { 64 | var cmd *exec.Cmd 65 | if err = GetGatewayConfig("https://ghproxy.com/https://raw.githubusercontent.com/XRSec/Go-Wechaty-Bot/main/Server/example.yaml", viper.GetString("RootPath")+"/config.yaml"); err != nil { 66 | logrus.Errorf("[viper] 初始化配置文件失败, Error: [%v] CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 67 | return 68 | } 69 | if runtime.GOOS == "windows" { 70 | cmd = exec.Command("cmd", "/c", "start", viper.GetString("RootPath")+"/config.yaml") 71 | } else { 72 | cmd = exec.Command("open", viper.GetString("RootPath")+"/config.yaml") 73 | } 74 | if err = cmd.Run(); err != nil { 75 | return 76 | } 77 | 78 | if err = viper.ReadInConfig(); err != nil { 79 | logrus.Errorf("[viper] 读取配置文件失败, Error: [%v] CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 80 | } else { 81 | logrus.Infof("[viper] 读取配置文件成功") 82 | } 83 | logrus.Infof("请检查配置文件! (CTRL + Z 撤销覆盖)") 84 | os.Exit(1) 85 | } 86 | ViperWrite() 87 | } 88 | 89 | /* 90 | 初始化日志 91 | */ 92 | func logInit() { 93 | // 设置日志格式 94 | log.SetPrefix("\x1b[1;32m[Go-Wechaty-Bot] \x1b[0m") 95 | // \x1b[%dm%s\x1b[0m 96 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 97 | 98 | // 创建日志文件夹 99 | fileInit(true, viper.GetString("LogPath")) 100 | 101 | // TODO Debug Model 102 | //logrus.SetLevel(logrus.PanicLevel | logrus.FatalLevel | logrus.ErrorLevel | logrus.WarnLevel | logrus.DebugLevel) 103 | logrus.SetReportCaller(true) 104 | logrus.SetFormatter(&logrus.JSONFormatter{ 105 | TimestampFormat: "2006-01-02 15:04:05", 106 | CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) { 107 | fileName := fmt.Sprintf(" %v:%v ", path.Base(frame.File), strconv.Itoa(frame.Line)) 108 | function = strings.Replace(path.Ext(frame.Function), ".", "", -1) 109 | return function, fileName 110 | }, 111 | //PrettyPrint: true, 112 | }) 113 | 114 | //if viper.GetString("SYSLOG.STATUS") == "true" { 115 | // fmt.Printf("[syslog] 启动日志记录\n") 116 | // hook, err := lSyslog.NewSyslogHook(viper.GetString("SYSLOG.PROTOCOL"), viper.GetString("SYSLOG.REMOTE_ADDRESS"), syslog.LOG_INFO, "Go-Wechaty-Bot") 117 | // if err != nil { 118 | // fmt.Printf("SysLog Error: %v\n", err) 119 | // } 120 | // logrus.AddHook(hook) 121 | // fmt.Printf("[syslog] 日志记录器初始化完成\n") 122 | // return 123 | //} 124 | logrus.SetOutput(io.MultiWriter(os.Stdout, &lumberjack.Logger{ 125 | Filename: viper.GetString("LogPath") + "/Go-Wechaty-Bot.log", //日志文件位置 126 | MaxSize: 50, // 单文件最大容量,单位是MB 127 | MaxBackups: 1, // 最大保留过期文件个数 128 | MaxAge: 365, // 保留过期文件的最大时间间隔,单位是天 129 | Compress: true, // 是否需要压缩滚动日志, 使用的 gzip 压缩 130 | })) 131 | } 132 | 133 | /* 134 | 初始化文件/夹 135 | */ 136 | func fileInit(fileAttributes bool, fileName string) { 137 | if _, err = os.Stat(fileName); err != nil { 138 | if fileAttributes { 139 | if err = os.MkdirAll(fileName, os.ModePerm); err != nil { 140 | logrus.Errorf("创建[%v] 目录失败, Error: [%v] CoptRight: [%s]", fileName, err, Copyright(make([]uintptr, 1))) 141 | } else { 142 | logrus.Infof("创建[%v] 目录成功", fileName) 143 | } 144 | } else { 145 | var f *os.File 146 | if f, err = os.Create(fileName); err != nil { 147 | logrus.Errorf("创建[%v] 文件失败, Error: [%v] CoptRight: [%s]", fileName, err, Copyright(make([]uintptr, 1))) 148 | } else { 149 | logrus.Infof("创建[%v] 文件成功", fileName) 150 | } 151 | defer func(f *os.File) { 152 | if err = f.Close(); err != nil { 153 | logrus.Errorf("关闭[%v] 文件失败, Error: [%v] CoptRight: [%s]", fileName, err, Copyright(make([]uintptr, 1))) 154 | } 155 | }(f) 156 | } 157 | } 158 | } 159 | 160 | /* 161 | ViperWrite() 162 | 写入配置文件 163 | */ 164 | func ViperWrite() { 165 | if err = viper.WriteConfigAs(viper.ConfigFileUsed()); err != nil { 166 | logrus.Errorf("Viper Write file Error: %v CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 167 | } else { 168 | logrus.Infof("Viper Write file Success") 169 | } 170 | } 171 | 172 | /* 173 | DockerRestart() 重启容器 174 | */ 175 | func DockerRestart() { 176 | var cmd *exec.Cmd 177 | if runtime.GOOS == "windows" { 178 | cmd = exec.Command("cmd", "/c", "chcp 437 && docker restart "+viper.GetString("BOT.DOCKER")) 179 | } else { 180 | cmd = exec.Command("/bin/sh", "-c", "docker restart "+viper.GetString("BOT.DOCKER")) 181 | } 182 | if out, err := cmd.CombinedOutput(); err != nil { 183 | logrus.Errorf("Docker Restart Error: %v", err) 184 | } else { 185 | logrus.Infof("Docker Restart Success: %s", out) 186 | } 187 | } 188 | 189 | func DingSend(userID, msg string) { 190 | if NightMode(userID) { 191 | cli := dingtalk.InitDingTalkWithSecret(viper.GetString("Ding.TOKEN"), viper.GetString("Ding.SECRET")) 192 | if err = cli.SendMarkDownMessage(msg, msg); err != nil { 193 | logrus.Errorf("DingMessage Error: [%v] CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 194 | return 195 | } 196 | logrus.Infof("DingTalk 通知成功! Copyright: [%s]", Copyright(make([]uintptr, 1))) 197 | } else { 198 | logrus.Infof("现在处于夜间模式,请在白天使用 CoptRight: [%s]", Copyright(make([]uintptr, 1))) 199 | return 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Server/General/Gateway.go: -------------------------------------------------------------------------------- 1 | package General 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | log "github.com/sirupsen/logrus" 8 | "github.com/spf13/viper" 9 | "gopkg.in/natefinch/lumberjack.v2" 10 | "io" 11 | "io/ioutil" 12 | "net/http" 13 | "os" 14 | "os/exec" 15 | "syscall" 16 | ) 17 | 18 | func GatewayStart() error { 19 | var ( 20 | cmd *exec.Cmd 21 | stderr bytes.Buffer 22 | RootPath = viper.GetString("RootPath") 23 | ) 24 | 25 | if _, err = os.Stat(viper.GetString("RootPath") + "/package.json"); err != nil { 26 | if err = GetGatewayConfig("https://ghproxy.com/https://raw.githubusercontent.com/XRSec/Go-Wechaty-Bot/main/padlocal/package.json", viper.GetString("RootPath")+"/package.json"); err != nil { 27 | return err 28 | } 29 | } 30 | 31 | log.Infof("[gateway] 发现 Node Gateway 配置信息, 存在启动 Node Gateway") 32 | 33 | // 安装依赖 34 | if _, err = os.Stat(RootPath + "/node_modules/.bin/wechaty"); err != nil { 35 | cmd = exec.Command("npm", "install", "--registry=https://registry.npmmirror.com") 36 | cmd.Dir = RootPath 37 | if err = cmd.Run(); err != nil { 38 | return err 39 | } 40 | } 41 | log.Infof("[gateway] 安装依赖成功") 42 | 43 | // 启动 Node Gateway 44 | cmd = exec.Command("npm", "run", "serve") 45 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 46 | cmd.Dir = RootPath 47 | cmd.Env = os.Environ() 48 | // 设置环境变量 49 | cmd.Env = append(cmd.Env, "WECHATY_PUPPET=wechaty-puppet-padlocal", 50 | "WECHATY_TOKEN="+viper.GetString("WECHATY.WECHATY_TOKEN"), 51 | "WECHATY_PUPPET_PADLOCAL_TOKEN="+viper.GetString("WECHATY.WECHATY_PUPPET_PADLOCAL_TOKEN"), 52 | "WECHATY_PUPPET_SERVER_PORT="+viper.GetString("WECHATY.WECHATY_PUPPET_SERVER_PORT"), 53 | "WECHATY_LOG="+viper.GetString("WECHATY.WECHATY_LOG"), 54 | "WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT="+viper.GetString("WECHATY.WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT"), 55 | "WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="+viper.GetString("WECHATY.WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER"), 56 | ) 57 | _ = os.Setenv("WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER", viper.GetString("WECHATY.WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER")) 58 | _ = os.Setenv("WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT", viper.GetString("WECHATY.WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT")) 59 | //cmd.Stdout = io.MultiWriter(os.Stdout, &lumberjack.Logger{ 60 | cmd.Stdout = io.MultiWriter(&lumberjack.Logger{ 61 | Filename: viper.Get("LogPath").(string) + "gateway.log", //日志文件位置 62 | MaxSize: 50, // 单文件最大容量,单位是MB 63 | MaxBackups: 1, // 最大保留过期文件个数 64 | MaxAge: 365, // 保留过期文件的最大时间间隔,单位是天 65 | Compress: true, // 是否需要压缩滚动日志, 使用的 gzip 压缩 66 | }) 67 | 68 | cmd.Stderr = &stderr // 标准错误输出 69 | err = cmd.Start() 70 | 71 | log.Errorf(string(stderr.Bytes())) 72 | log.Infof("[gateway] 启动 Node Gateway 成功,ID: %v", cmd.Process.Pid) 73 | GatewayCmd = cmd 74 | go func() { 75 | if err = GatewayCmd.Wait(); err != nil { 76 | fmt.Printf("Child process %d exit with err: %v\n", GatewayCmd.Process.Pid, err) 77 | } 78 | }() 79 | return err 80 | } 81 | 82 | func GetGatewayConfig(configUrl string, file string) (err error) { 83 | var ( 84 | resp *http.Response 85 | data []byte 86 | ) 87 | 88 | if configUrl == "" { 89 | return errors.New("[gateway] 未配置 Node Gateway 的 NPM 下载地址") 90 | } 91 | 92 | if resp, err = http.Get(configUrl); err != nil { 93 | return err 94 | } 95 | defer func(Body io.ReadCloser) { 96 | if err := Body.Close(); err != nil { 97 | fmt.Println(err) 98 | } 99 | }(resp.Body) 100 | 101 | if data, err = ioutil.ReadAll(resp.Body); err != nil { 102 | return err 103 | } 104 | if err = ioutil.WriteFile(file, data, 0644); err != nil { 105 | return err 106 | } 107 | return err 108 | } 109 | 110 | func GatewayDemon() { 111 | // 这里就没有用for 循环,必须先正常连接一次 否则 server 循环10次连接不上直接退出 112 | if err = GatewayStart(); err != nil { 113 | log.Errorf("[gateway] Node Gateway 启动失败, Error: [%v]", err) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /Server/General/Pretreatment.go: -------------------------------------------------------------------------------- 1 | package General 2 | 3 | import ( 4 | "fmt" 5 | "github.com/XRSec/Go-Wechaty-Bot/Plug" 6 | "path" 7 | "runtime" 8 | "strings" 9 | "time" 10 | 11 | log "github.com/sirupsen/logrus" 12 | "github.com/spf13/viper" 13 | "github.com/wechaty/go-wechaty/wechaty" 14 | "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" 15 | "github.com/wechaty/go-wechaty/wechaty/user" 16 | ) 17 | 18 | func Pretreatment() *wechaty.Plugin { 19 | plug := wechaty.NewPlugin() 20 | plug.OnMessage(onMessage) 21 | return plug 22 | } 23 | 24 | func onMessage(context *wechaty.Context, message *user.Message) { 25 | log.Infof("[onMessage] Message: [%v]", message) 26 | var m Plug.MessageInfo 27 | m.ID = message.ID() 28 | m.UserName = message.Talker().Name() 29 | m.UserID = message.Talker().ID() 30 | m.Date = message.Date().Format("2006-01-02 15:04:05") 31 | m.Reply = false 32 | m.Pass = false 33 | m.Status = false 34 | m.AtMe = false 35 | // 公众号消息 36 | if message.Type() == schemas.MessageTypeRecalled { 37 | log.Infof("Type Pass, Type: [%v]:[%v] CoptRight: [%s]", message.Type().String(), message.Talker().Name(), Copyright(make([]uintptr, 1))) 38 | m.PassResult = "MessageTypeRecalled" 39 | m.Pass = true 40 | } 41 | // TODO 计划删除 微信团队 42 | if message.Type() == schemas.MessageTypeUnknown && message.Talker().Name() == "微信团队" { 43 | log.Infof("Type Pass, Type: [%v]:[%v] CoptRight: [%s]", message.Type().String(), message.Talker().Name(), Copyright(make([]uintptr, 1))) 44 | m.PassResult = "微信团队" 45 | m.Pass = true 46 | } 47 | // 公众号消息 48 | if message.Talker().Type().String() == "ContactTypeOfficial" { 49 | log.Infof("Official Pass, [%v] CoptRight: [%s]", message.Talker().Name(), Copyright(make([]uintptr, 1))) 50 | m.PassResult = "Official" 51 | m.Pass = true 52 | } 53 | // 所有人消息 54 | if message.MentionSelf() || message.Room() == nil { 55 | if strings.Contains(message.Text(), "所有人") { 56 | log.Infof("At All Pass, Type: [%v]:[%v] CoptRight: [%s]", message.Type().String(), message.Talker().Name(), Copyright(make([]uintptr, 1))) 57 | m.PassResult = "所有人" 58 | m.Pass = true 59 | } 60 | if strings.Contains(message.Text(), "群公告") { 61 | log.Infof("At Group Of Announcement Pass, Type: [%v]:[%v] CoptRight: [%s]", message.Type().String(), message.Talker().Name(), Copyright(make([]uintptr, 1))) 62 | m.PassResult = "群公告" 63 | m.Pass = true 64 | } 65 | m.AtMe = true 66 | } 67 | // 未知消息 68 | if message.Type() != schemas.MessageTypeText { 69 | m.Content = "[未知消息类型: " + message.Type().String() + "] " + message.Text() 70 | } else { 71 | m.Content = message.Text() 72 | } 73 | //群聊消息 74 | //m.AutoInfo = fmt.Sprintf("用户ID: [%v] 用户名称: [%v] 说: [%v]", m.UserID, m.UserName, strings.Replace(m.Content, "\u2005", " ", -1)) 75 | if message.Room() != nil { 76 | m.RoomName = message.Room().Topic() 77 | m.RoomID = message.Room().ID() 78 | m.Status = true 79 | } 80 | if !NightMode(m.UserID) { 81 | m.PassResult = "夜间模式" 82 | m.Pass = true 83 | } 84 | context.SetData("msgInfo", m) 85 | //chatTimeLimit(context) 86 | } 87 | 88 | /* 89 | ChatTimeLimit(message.Date().Format("2006-01-02 15:04:05")) 90 | : 判断消息是否在规定时间内 91 | : 如果是,则返回true,否则返回false 92 | */ 93 | func chatTimeLimit(context *wechaty.Context) { 94 | m, ok := (context.GetData("msgInfo")).(Plug.MessageInfo) 95 | if !ok { 96 | log.Errorf("Conversion Failed CoptRight: [%s]", Copyright(make([]uintptr, 1))) 97 | return 98 | } 99 | // 管理员取消限制 100 | if m.UserID == viper.GetString("Bot.AdminID") { 101 | log.Infof("[chatTimeLimit] 管理员 Copyright: [%s]", Copyright(make([]uintptr, 1))) 102 | return 103 | } 104 | //当前时间 105 | var ( 106 | now time.Time 107 | loc *time.Location 108 | lastDate time.Time 109 | date = viper.GetString(fmt.Sprintf("Chat.%v.Date", m.UserID)) 110 | ) 111 | if date == "" { 112 | log.Infof("Not Set Date, CoptRight: [%s]", Copyright(make([]uintptr, 1))) 113 | return 114 | } 115 | timeNow := time.Now().Format("2006-01-02 15:04:05") 116 | if loc, err = time.LoadLocation("Local"); err != nil { 117 | log.Errorf("[ChatTimeLimit] time.ParseInLocation, Error: [%v], Loc: [%v] CoptRight: [%s]", err, loc, Copyright(make([]uintptr, 1))) 118 | // [ChatTimeLimit] time.ParseInLocation, Error: [The system cannot find the path specified.], Loc: [UTC] 119 | return 120 | } 121 | if now, err = time.ParseInLocation("2006-01-02 15:04:05", timeNow, loc); err != nil { 122 | log.Errorf("[ChatTimeLimit] time.ParseInLocation, Error: [%v], Now: [%v] CoptRight: [%s]", err, now, Copyright(make([]uintptr, 1))) 123 | return 124 | } 125 | //当前时间转换为"年-月-日"的格式 126 | if lastDate, err = time.ParseInLocation("2006-01-02 15:04:05", date, loc); err != nil { 127 | log.Errorf("[ChatTimeLimit] time.ParseInLocation, Error: [%v], Lastdate: [%v] CoptRight: [%s]", err, lastDate, Copyright(make([]uintptr, 1))) 128 | return 129 | } 130 | //计算两个时间相差的秒数 131 | if second := int(now.Sub(lastDate).Seconds()); second < 30 { 132 | log.Infof("[ChatTimeLimit] 时间相差不足 开始时间: [%v], 结束时间: [%v], 相差秒数: [%d] CoptRight: [%s]", lastDate, now, second, Copyright(make([]uintptr, 1))) 133 | // Messages.Reply = fmt.Sprintf("[ChatTimeLimit] 时间相差不足 开始时间: [%v], 结束时间: [%v], 相差秒数: [%d]", lastDate, now, second) 134 | m.PassResult = "ChatTimeLimit" 135 | m.Pass = true 136 | context.SetData("msgInfo", m) 137 | return 138 | } 139 | log.Infof("[ChatTimeLimit] 时间相差超过 30 秒") 140 | } 141 | 142 | /* 143 | 如果有自定义消息内容则填写,没有则为空 144 | SayMessage(context, message, "hello word") 145 | SayMessage(context, message, "") 146 | 请确保你设置过了 ChatTimeLimit函数 147 | */ 148 | func SayMessage(context *wechaty.Context, message *user.Message, msg string) { 149 | m, ok := (context.GetData("msgInfo")).(Plug.MessageInfo) 150 | if !ok { 151 | log.Errorf("Conversion Failed CoptRight: [%s]", Copyright(make([]uintptr, 1))) 152 | return 153 | } 154 | if !NightMode(m.UserID) { // 夜间模式 155 | // 什么情况下会跳过 Pretreatment 不记录呢? 156 | m.PassResult = "NightMode" 157 | m.Pass = true 158 | context.SetData("msgInfo", m) 159 | return 160 | } 161 | 162 | //if m.Reply { 163 | // return 164 | //} 165 | //if m.Pass { 166 | // return 167 | //} 168 | 169 | if msg == "" { 170 | return 171 | } 172 | 173 | // TODO 0.79 私聊有问题 174 | if _, err = message.Say(msg); err != nil { 175 | log.Errorf("[SayMessage] [%v], error: %v CoptRight: [%s]", msg, err, Copyright(make([]uintptr, 1))) 176 | } 177 | 178 | m.ReplyResult = msg 179 | m.Reply = true 180 | context.SetData("msgInfo", m) 181 | viper.Set(fmt.Sprintf("Chat.%v.Date", m.UserID), m.Date) 182 | } 183 | 184 | /* 185 | func NightMode(message.Talker().ID()) 186 | : 管理员 返回 true 187 | : 凌晨返回 false 188 | */ 189 | func NightMode(userID string) bool { 190 | //当前时间 191 | startTimeStr := "00:00:00" 192 | endTimeStr := "06:00:00" 193 | now := time.Now() 194 | //当前时间转换为"年-月-日"的格式 195 | format := now.Format("2006-01-02") 196 | //转换为time类型需要的格式 197 | layout := "2006-01-02 15:04:05" 198 | //将开始时间拼接“年-月-日 ”转换为time类型 199 | timeStart, _ := time.ParseInLocation(layout, format+" "+startTimeStr, time.Local) 200 | //将结束时间拼接“年-月-日 ”转换为time类型 201 | timeEnd, _ := time.ParseInLocation(layout, format+" "+endTimeStr, time.Local) 202 | //使用time的Before和After方法,判断当前时间是否在参数的时间范围 203 | if userID == viper.GetString("Bot.AdminId") { 204 | log.Infof("[NightMode] 管理员 Copyright: [%s]", Copyright(make([]uintptr, 1))) 205 | return true 206 | } else { 207 | return !(now.Before(timeEnd) && now.After(timeStart)) 208 | } 209 | } 210 | 211 | /* 212 | use: Copyright(make([]uintptr, 1)) 213 | return: main.xxx.xxx.xxx 214 | */ 215 | func Copyright(pc []uintptr) string { 216 | s := "" 217 | for i := 1; i < 4; i++ { 218 | runtime.Callers(i, pc) 219 | if i == 3 { 220 | s += strings.Replace(path.Ext(runtime.FuncForPC(pc[0]).Name()), ".", "", -1) 221 | } else { 222 | s += strings.Replace(path.Ext(runtime.FuncForPC(pc[0]).Name()), ".", "", -1) + " 》" 223 | } 224 | } 225 | return s 226 | } 227 | -------------------------------------------------------------------------------- /Server/General/enter.go: -------------------------------------------------------------------------------- 1 | package General 2 | 3 | import ( 4 | "github.com/wechaty/go-wechaty/wechaty" 5 | "os/exec" 6 | ) 7 | 8 | var ( 9 | err error 10 | GatewayCmd *exec.Cmd 11 | globleServiceInstance = &GlobleService{} 12 | ) 13 | 14 | type GlobleService struct { 15 | Bot *wechaty.Wechaty 16 | } 17 | -------------------------------------------------------------------------------- /Server/example.yaml: -------------------------------------------------------------------------------- 1 | BOT: 2 | ADMINID: wxid_xxxxx 3 | NAME: xxxxxxxx 4 | GROUP: Debug 5 | Docker: Go-Wechaty-Bot 6 | #SYSLOG: 7 | # STATUS: false 8 | # PROTOCOL: udp 9 | # REMOTE_ADDRESS: localhost:514 10 | WECHATY: 11 | GATEWAY: false 12 | WECHATY_ENDPOINT: 127.0.0.1:25000 13 | WECHATY_LOG: verbose 14 | WECHATY_PUPPET_PADLOCAL_TOKEN: puppet_padlocal_xxxxxxxxxxxxxxxxxx 15 | WECHATY_PUPPET_SERVER_PORT: 25000 16 | WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT: true 17 | WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER: true 18 | WECHATY_TOKEN: insecure_xxxxxxxxxxxxxxxxxxxxxx 19 | 20 | GROUP: 21 | LISTS: 22 | xxxxx@chatroom: xxxxx 23 | PASS: 24 | xxxxx@chatroom: true 25 | 26 | DING: 27 | SECRET: xxxxxxxxxxxxxxxxxx 28 | TOKEN: xxxxxxxxxxxxxxxxxx 29 | WXOPENAI: 30 | ENV: online 31 | SIGNURL: https://openai.weixin.qq.com/openapi/sign/ 32 | URL: https://openai.weixin.qq.com/openapi/aibot/ 33 | TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 34 | TULING: 35 | TOKEN: xxxxxxxxxxxxxxxx&info= 36 | URL: http://www.tuling123.com/openapi/api?key= 37 | QingYunKe: 38 | AppID: 0 39 | Key: free 40 | URL: https://api.qingyunke.com/api.php?key=free&appid=0&msg= 41 | MYSQL: 42 | HOST: user:name@tcp(ip:port)/database_name?charset=utf8mb4&parseTime=True&loc=Local -------------------------------------------------------------------------------- /Server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/XRSec/Go-Wechaty-Bot 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/beevik/etree v1.1.0 7 | github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 8 | github.com/mdp/qrterminal/v3 v3.0.0 9 | github.com/robfig/cron/v3 v3.0.1 10 | github.com/sirupsen/logrus v1.9.0 11 | github.com/spf13/viper v1.15.0 12 | github.com/wechaty/go-wechaty v0.4.9 13 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 14 | gorm.io/driver/mysql v1.5.0 15 | gorm.io/gorm v1.25.0 16 | ) 17 | 18 | require ( 19 | github.com/fsnotify/fsnotify v1.6.0 // indirect 20 | github.com/go-sql-driver/mysql v1.7.0 // indirect 21 | github.com/golang/protobuf v1.5.3 // indirect 22 | github.com/google/uuid v1.3.0 // indirect 23 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect 24 | github.com/hashicorp/golang-lru v0.5.4 // indirect 25 | github.com/hashicorp/hcl v1.0.0 // indirect 26 | github.com/jinzhu/inflection v1.0.0 // indirect 27 | github.com/jinzhu/now v1.1.5 // indirect 28 | github.com/lucsky/cuid v1.2.1 // indirect 29 | github.com/magiconair/properties v1.8.7 // indirect 30 | github.com/maruel/rs v1.1.0 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 // indirect 32 | github.com/otiai10/opengraph v1.1.3 // indirect 33 | github.com/pelletier/go-toml/v2 v2.0.7 // indirect 34 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect 35 | github.com/spf13/afero v1.9.5 // indirect 36 | github.com/spf13/cast v1.5.0 // indirect 37 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 38 | github.com/spf13/pflag v1.0.5 // indirect 39 | github.com/subosito/gotenv v1.4.2 // indirect 40 | github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d // indirect 41 | github.com/wechaty/go-grpc v1.5.2 // indirect 42 | golang.org/x/net v0.17.0 // indirect 43 | golang.org/x/sys v0.13.0 // indirect 44 | golang.org/x/text v0.13.0 // indirect 45 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 46 | google.golang.org/grpc v1.54.0 // indirect 47 | google.golang.org/protobuf v1.30.0 // indirect 48 | gopkg.in/ini.v1 v1.67.0 // indirect 49 | gopkg.in/yaml.v3 v3.0.1 // indirect 50 | rsc.io/qr v0.2.0 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /Server/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= 42 | github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= 43 | github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= 44 | github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 45 | github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 h1:pamuM2sgLJLoMWfchc6y071z8ifalajU7btZmZNhoH4= 46 | github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto= 47 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 48 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 49 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 50 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 51 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 52 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 53 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 54 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 55 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 56 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 57 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 58 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 59 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 60 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 61 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 62 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 63 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 64 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 65 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 66 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 67 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 68 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 69 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 70 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 71 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 72 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 73 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 74 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 75 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 76 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 77 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 78 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 79 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 80 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 81 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 82 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 83 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 84 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 85 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 86 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 88 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 89 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 90 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 91 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 92 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 93 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 94 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 95 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 96 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 97 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 98 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 99 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 100 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 101 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 102 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 103 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 104 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 105 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 106 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 107 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 108 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 110 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 114 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 115 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 116 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 117 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 118 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 119 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 120 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 121 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 122 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 124 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 125 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 126 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 127 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 128 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 129 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 130 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 131 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 132 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 133 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 134 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE= 135 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08= 136 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 137 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 138 | github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= 139 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 140 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 141 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 142 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 143 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 144 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 145 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 146 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 147 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 148 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 149 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 150 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 151 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 152 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 153 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 154 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 155 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 156 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 157 | github.com/lucsky/cuid v1.2.1 h1:MtJrL2OFhvYufUIn48d35QGXyeTC8tn0upumW9WwTHg= 158 | github.com/lucsky/cuid v1.2.1/go.mod h1:QaaJqckboimOmhRSJXSx/+IT+VTfxfPGSo/6mfgUfmE= 159 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 160 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 161 | github.com/maruel/rs v1.1.0 h1:dh4OceAF5yD06EASOrb+DS358LI4g0B90YApSdjCP6U= 162 | github.com/maruel/rs v1.1.0/go.mod h1:vzwMjzSJJxLIXmU62qHj6O5QRn5kvCKxFrfaFCxBcUY= 163 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 164 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 165 | github.com/mdp/qrterminal v1.0.1/go.mod h1:Z33WhxQe9B6CdW37HaVqcRKzP+kByF3q/qLxOGe12xQ= 166 | github.com/mdp/qrterminal/v3 v3.0.0 h1:ywQqLRBXWTktytQNDKFjhAvoGkLVN3J2tAFZ0kMd9xQ= 167 | github.com/mdp/qrterminal/v3 v3.0.0/go.mod h1:NJpfAs7OAm77Dy8EkWrtE4aq+cE6McoLXlBqXQEwvE0= 168 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 169 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 170 | github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= 171 | github.com/otiai10/marmoset v0.4.0 h1:Hg59lQI7qQowBEdsAJ/+VDTEospTBzXX/A1Gsw4mlvA= 172 | github.com/otiai10/marmoset v0.4.0/go.mod h1:t2q6dXWZ9YcFdRREDApX4bCmfQnL3isJ2dgl8ychlXg= 173 | github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= 174 | github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= 175 | github.com/otiai10/opengraph v1.1.3 h1:4RoX4yckU/eaj34XxwoyNFvuPVrmjcUHMyAgjJL1Pwg= 176 | github.com/otiai10/opengraph v1.1.3/go.mod h1:ZMbPcfiSRSsg3+yrWZCXrgYL6kEK4KpH4GG1iyIvEXs= 177 | github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= 178 | github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 179 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 180 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 181 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 182 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 183 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 184 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 185 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 186 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 187 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 188 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 189 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 190 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 191 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 192 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= 193 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= 194 | github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= 195 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 196 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 197 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 198 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 199 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 200 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 201 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 202 | github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= 203 | github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= 204 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 205 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 206 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 207 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 208 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 209 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 210 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 211 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 212 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 213 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 214 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 215 | github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= 216 | github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 217 | github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d h1:4x1FeGJRB00cvxnKXnRJDT89fvG/Lzm2ecm0vlr/qDs= 218 | github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d/go.mod h1:uSELzeIcTceNCgzbKdJuJa0ouCqqtkyzL+6bnA3rM+M= 219 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 220 | github.com/wechaty/go-grpc v1.5.2 h1:bQ4txCuc2UROj6KfxcC7Dt7st0+1tKR3CPDJiR8oNAI= 221 | github.com/wechaty/go-grpc v1.5.2/go.mod h1:gtyrRa9Ts5KGzQh61CIytAYyE9HmVna6yFRamKN4udk= 222 | github.com/wechaty/go-wechaty v0.4.9 h1:TO66WcYtjQQT+tBuRF7VvJeVngg4sOFDY1D9MhwxhAM= 223 | github.com/wechaty/go-wechaty v0.4.9/go.mod h1:0599Rrvv5UKHUF6/EjSgCPoKELm+W9Sy749axYKxUoQ= 224 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 225 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 226 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 227 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 228 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 229 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 230 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 231 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 232 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 233 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 234 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 235 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 236 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 237 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 238 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 239 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 240 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 241 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 242 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 243 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 244 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 245 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 246 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 247 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 248 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 249 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 250 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 251 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 252 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 253 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 254 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 255 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 256 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 257 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 258 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 259 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 260 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 261 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 262 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 263 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 264 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 265 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 266 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 267 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 268 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 269 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 270 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 271 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 272 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 273 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 274 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 275 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 276 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 277 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 278 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 279 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 280 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 281 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 282 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 283 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 284 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 285 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 286 | golang.org/x/net v0.0.0-20190926025831-c00fd9afed17/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 287 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 288 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 289 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 290 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 291 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 292 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 293 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 294 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 295 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 296 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 297 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 298 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 299 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 300 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 301 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 302 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 303 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 304 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 305 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 306 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 307 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 308 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 309 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 310 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 311 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 312 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 313 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 314 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 315 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 316 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 317 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 318 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 319 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 320 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 321 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 322 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 323 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 324 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 325 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 326 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 327 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 328 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 329 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 330 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 331 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 332 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 349 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 359 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 360 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 362 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 363 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 364 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 365 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 366 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 367 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 368 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 369 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 370 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 371 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 372 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 373 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 374 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 375 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 376 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 377 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 378 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 379 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 380 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 381 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 382 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 383 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 384 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 385 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 386 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 387 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 388 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 389 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 390 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 391 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 392 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 393 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 394 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 395 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 396 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 397 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 398 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 399 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 400 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 401 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 402 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 403 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 404 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 405 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 406 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 407 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 408 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 409 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 410 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 411 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 412 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 413 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 414 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 415 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 416 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 417 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 418 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 419 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 420 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 421 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 422 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 423 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 424 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 425 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 426 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 427 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 428 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 429 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 430 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 431 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 432 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 433 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 434 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 435 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 436 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 437 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 438 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 439 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 440 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 441 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 442 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 443 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 444 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 445 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 446 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 447 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 448 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 449 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 450 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 451 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 452 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 453 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 454 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 455 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 456 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 457 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 458 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 459 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 460 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 461 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 462 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 463 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 464 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 465 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 466 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 467 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 468 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 469 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 470 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 471 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 472 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 473 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 474 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 475 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 476 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 477 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 478 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 479 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 480 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 481 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 482 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 483 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 484 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 485 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 486 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 487 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 488 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 489 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 490 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 491 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 492 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 493 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 494 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 495 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= 496 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= 497 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 498 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 499 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 500 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 501 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 502 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 503 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 504 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 505 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 506 | google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 507 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 508 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 509 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 510 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 511 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 512 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 513 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 514 | google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= 515 | google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 516 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 517 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 518 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 519 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 520 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 521 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 522 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 523 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 524 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 525 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 526 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 527 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 528 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 529 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 530 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 531 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 532 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 533 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 534 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 535 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 536 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 537 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 538 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 539 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 540 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 541 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 542 | gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM= 543 | gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo= 544 | gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 545 | gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU= 546 | gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 547 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 548 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 549 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 550 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 551 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 552 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 553 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 554 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 555 | rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= 556 | rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= 557 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 558 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 559 | -------------------------------------------------------------------------------- /Server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/XRSec/Go-Wechaty-Bot/General" 6 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Admin" 7 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Average" 8 | "github.com/XRSec/Go-Wechaty-Bot/Plug/CodeAuth" 9 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Cron" 10 | "github.com/XRSec/Go-Wechaty-Bot/Plug/DingMessage" 11 | ExportMessages "github.com/XRSec/Go-Wechaty-Bot/Plug/ExportMessage" 12 | "github.com/XRSec/Go-Wechaty-Bot/Plug/FileBox" 13 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Group" 14 | "github.com/XRSec/Go-Wechaty-Bot/Plug/GroupForward" 15 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Health" 16 | "github.com/XRSec/Go-Wechaty-Bot/Plug/Test" 17 | puppet "github.com/wechaty/go-wechaty/wechaty-puppet" 18 | "os" 19 | "os/signal" 20 | "syscall" 21 | "time" 22 | 23 | "github.com/mdp/qrterminal/v3" 24 | log "github.com/sirupsen/logrus" 25 | "github.com/spf13/viper" 26 | . "github.com/wechaty/go-wechaty/wechaty" 27 | "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" 28 | _interface "github.com/wechaty/go-wechaty/wechaty/interface" 29 | "github.com/wechaty/go-wechaty/wechaty/user" 30 | ) 31 | 32 | var ( 33 | err error 34 | ) 35 | 36 | func onScan(context *Context, qrCode string, status schemas.ScanStatus, data string) { 37 | fmt.Printf("\n\n") 38 | if status.String() == "ScanStatusWaiting" { 39 | qrterminal.GenerateWithConfig(qrCode, qrterminal.Config{ 40 | Level: qrterminal.L, 41 | Writer: os.Stdout, 42 | BlackChar: " \u2005", 43 | WhiteChar: "💵", 44 | QuietZone: 1, 45 | }) 46 | fmt.Printf("\n\n") 47 | log.Printf("[Scan] https://wechaty.js.org/qrcode/%v %v", qrCode, data) 48 | } else if status.String() == "ScanStatusScanned" { 49 | fmt.Printf("[Scan] Scanned: %v %v\n", status.String(), data) 50 | } else if status.String() == "ScanStatusCancel" { 51 | fmt.Printf("[Scan] Cancel: %v %v\n", status.String(), data) 52 | } else if status.String() == "ScanStatusTimeout" { 53 | fmt.Printf("[Scan] Timeout: %v %v\n", status.String(), data) 54 | } else if status.String() == "ScanStatusConfirmed" { 55 | fmt.Printf("[Scan] Confirmed: %v %v\n", status.String(), data) 56 | } else { 57 | fmt.Printf("[Scan] Unknow Status: %v %v\n", status.String(), data) 58 | } 59 | } 60 | 61 | /* 62 | @method onLogin 当机器人成功登陆后,会触发事件,并会在事件中传递当前登陆机器人的信息 63 | @param {*} user 64 | */ 65 | func onLogin(context *Context, user *user.ContactSelf) { 66 | fmt.Printf(`%v 67 | // 68 | \\ // 69 | \\ // 70 | ## DDDDDDDDDDDDDDDDDDDD ## 71 | ## DDDDDDDDDDDDDDDDDDDD ## 72 | ## hh hh ## ## ## ## ## ## ## ## ## ### ## #### ## 73 | ## hh // \\ hh ## ## ## ## ## ## ## ## ## 74 | ## hh // \\ hh ## ## ## ## ## ## ## ## ## 75 | ## hh hh ## ## ## ## ## ## ## ## ## ## 76 | ## hh wwww hh ## ## ## ## ## ## ## ## #### 77 | ## hh hh ## ## ## ## ## ## ## ## ## ## ## ### ## ## ### 78 | ## MMMMMMMMMMMMMMMMMMMM ## 79 | ##MMMMMMMMMMMMMMMMMMMMMM## 微信机器人: [%v] 已经登录成功了。 80 | %v`, "\n", user.Name(), "\n") 81 | viper.Set("bot.name", user.Name()) 82 | } 83 | 84 | /* 85 | * 86 | @method onLogout 当机器人检测到登出的时候,会触发事件,并会在事件中传递机器人的信息。 87 | @param {*} user 88 | */ 89 | func onLogout(context *Context, user *user.ContactSelf, reason string) { 90 | fmt.Println("========================onLogout👇========================") 91 | } 92 | 93 | /* 94 | @method onRoomInvite 当收到群邀请的时候,会触发这个事件。 95 | @param {*} user 96 | */ 97 | func onRoomInvite(context *Context, roomInvitation *user.RoomInvitation) { 98 | fmt.Println("========================onRoomInvite👇========================") 99 | // TODO 自动通过群聊申请有问题 待解决(官方的问题) 100 | var ( 101 | inviter _interface.IContact 102 | roomName string 103 | ) 104 | if err = roomInvitation.Accept(); err != nil { 105 | log.Errorf("[RoomInvite] Error: %v CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 106 | return 107 | } 108 | if inviter, err = roomInvitation.Inviter(); err != nil { 109 | log.Errorf("[RoomInvite] 获取用户属性失败, Error: %v CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 110 | return 111 | } 112 | if roomName, err = roomInvitation.Topic(); err != nil { 113 | log.Errorf("[RoomInvite] 获取群聊名称失败, Error: %v CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 114 | return 115 | } 116 | log.Infof("[RoomInvite] 通过群聊邀请, 群聊名称: [%v] 邀请人: [%v]", roomName, inviter.Name()) 117 | // 机器人进群自我介绍 onRoomJoin 已经实现 118 | } 119 | 120 | /* 121 | @method onRoomTopic 当有人修改群名称的时候会触发这个事件。 122 | @param {*} user 123 | */ 124 | func onRoomTopic(context *Context, room *user.Room, newTopic string, oldTopic string, changer _interface.IContact, date time.Time) { 125 | fmt.Println("========================onRoomTopic👇========================") 126 | } 127 | 128 | /* 129 | @method onRoomleave 当机器人把群里某个用户移出群聊的时候会触发这个时间。用户主动退群是无法检测到的。 130 | @param {*} user 131 | */ 132 | func onRoomleave(context *Context, room *user.Room, leaverList []_interface.IContact, remover _interface.IContact, date time.Time) { 133 | fmt.Println("========================onRoomleave👇========================") 134 | fmt.Printf("[onRoomleave] 群聊名称: [%v] 用户[%v] 被移出群聊", room.Topic(), leaverList[0].Name()) 135 | } 136 | 137 | func onFriendship(context *Context, friendship *user.Friendship) { 138 | fmt.Println("========================onFriendship👇========================") 139 | switch friendship.Type() { 140 | case 0: // FriendshipTypeUnknown 141 | 142 | /* 143 | 1. 新的好友请求 144 | */ 145 | case 1: // FriendshipTypeConfirm 146 | //log.Printf("friend ship confirmed with%v", friendship.Contact().Name()) 147 | 148 | /* 149 | 2. 通过好友申请 150 | */ 151 | case 2: // FriendshipTypeReceive 152 | if err = friendship.Accept(); err != nil { 153 | log.Errorf("[onFriendship] 添加好友失败, 好友名称: [%v], Error: [%v] CoptRight: [%s]", friendship.Contact().Name(), err, Copyright(make([]uintptr, 1))) 154 | return 155 | } 156 | log.Infof("[onFriendship] 添加好友成功, 好友名称:%v", friendship.Contact().Name()) 157 | case 3: // FriendshipTypeVerify 158 | if err = friendship.GetWechaty().Friendship().Add(friendship.Contact(), fmt.Sprintf("你好,我是%v,以后请多多关照!", viper.GetString("bot.name"))); err != nil { 159 | log.Errorf("[onFriendship] 添加好友失败, 好友名称: [%v], Error: [%v] CoptRight: [%s]", friendship.Contact().Name(), err, Copyright(make([]uintptr, 1))) 160 | return 161 | } 162 | log.Infof("[onFriendship] 添加好友成功, 好友名称:%v", friendship.Contact().Name()) 163 | 164 | default: 165 | // NONE 166 | } 167 | log.Infof("[onFriendship] %v好友关系是: %v Hello: %v ", friendship.Contact().Name(), friendship.Type(), friendship.Hello()) 168 | } 169 | 170 | func onError(context *Context, err error) { 171 | log.Errorf("[onError] Error: [%v] 消息来自函数: [%v]", err, Copyright(make([]uintptr, 1))) 172 | // Call with too few input arguments 173 | } 174 | 175 | func info() { 176 | fmt.Println("\n\n--------------------") 177 | fmt.Printf("LogPath: %v/github.com/XRSec/Go-Wechaty-Bot.log \n", viper.GetString("LogPath")) 178 | fmt.Printf("Config: %v/config.yaml \n", viper.GetString("RootPath")) 179 | fmt.Printf("Token: %v \n", viper.GetString("Wechaty.WECHATY_TOKEN")) 180 | fmt.Printf("Endpoint: %v \n", viper.GetString("Wechaty.WECHATY_ENDPOINT")) 181 | fmt.Printf("WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT: %v \n", viper.GetString("WECHATY.WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT")) 182 | fmt.Println("--------------------\n\n") 183 | } 184 | 185 | func wechatBotDaemon() { 186 | for i := 0; i <= 10; i++ { 187 | // 钉钉推送 188 | ViperInit() 189 | info() 190 | bot := NewWechaty(WithPuppetOption(puppet.Option{ 191 | Token: viper.GetString("WECHATY.WECHATY_TOKEN"), 192 | Endpoint: viper.GetString("WECHATY.WECHATY_ENDPOINT"), 193 | })) 194 | 195 | bot.OnScan(onScan). 196 | OnLogin(onLogin). 197 | OnLogout(onLogout). 198 | OnError(onError). 199 | OnRoomInvite(onRoomInvite). // 有问题,暂时不用,等待修复 200 | OnRoomTopic(onRoomTopic). 201 | //OnRoomJoin(onRoomJoin). 202 | OnRoomLeave(onRoomleave). 203 | OnFriendship(onFriendship). 204 | Use(Health.New()). 205 | Use(Pretreatment()). 206 | Use(Test.New()). 207 | Use(Cron.New()). 208 | Use(Group.New()). 209 | Use(GroupForward.New()). 210 | Use(Admin.New()). 211 | Use(Average.New()). 212 | Use(CodeAuth.New()). 213 | //Use(AutoReply.New()). 214 | Use(FileBox.New()). 215 | Use(ExportMessages.New()). 216 | Use(DingMessage.New()) 217 | //bot.DaemonStart() 218 | if err = bot.Start(); err != nil { 219 | // 重启Bot 220 | log.Infof("[main] Bot 错误: %v", err) 221 | if i > 10 { 222 | os.Exit(0) 223 | } 224 | log.Printf("正在重新启动程序, 当前重试次数: 第%v次", i) 225 | DingSend(viper.GetString("Bot.AdminID"), fmt.Sprintf("Bot 启动失败: 正在重新启动程序, 当前重试次数: 第%v次", i)) 226 | time.Sleep(10 * time.Second) 227 | } else { 228 | i = 0 229 | NewGlobleService().SetBot(bot) 230 | // Bot 守护程序 231 | var quitSig = make(chan os.Signal) 232 | signal.Notify(quitSig, os.Interrupt, os.Kill, syscall.SIGINT, syscall.SIGTERM) 233 | select { 234 | case <-quitSig: 235 | ViperWrite() 236 | log.Fatal("程序退出!") 237 | } 238 | } 239 | } 240 | } 241 | func main() { 242 | defer func() { 243 | if err := recover(); err != nil { 244 | log.Errorf("Error: %v CoptRight: [%s]", err, Copyright(make([]uintptr, 1))) 245 | } 246 | }() 247 | // 重试次数 10 248 | wechatBotDaemon() 249 | } 250 | -------------------------------------------------------------------------------- /Server/test/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/blinkbean/dingtalk" 7 | "github.com/robfig/cron/v3" 8 | log "github.com/sirupsen/logrus" 9 | "github.com/spf13/viper" 10 | . "github.com/wechaty/go-wechaty/wechaty" 11 | puppet "github.com/wechaty/go-wechaty/wechaty-puppet" 12 | "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" 13 | _interface "github.com/wechaty/go-wechaty/wechaty/interface" 14 | "github.com/wechaty/go-wechaty/wechaty/user" 15 | "gopkg.in/natefinch/lumberjack.v2" 16 | "io" 17 | "net/http" 18 | "os" 19 | "os/signal" 20 | "path" 21 | "runtime" 22 | "strconv" 23 | "strings" 24 | "syscall" 25 | "time" 26 | ) 27 | 28 | /* 29 | 30 | ***************************** 31 | TODO 32 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build test.go 33 | 34 | init().RootPath 35 | main().Endpoint 36 | main().Token 37 | heartbeat().roomID 38 | 39 | ***************************** 40 | 41 | */ 42 | 43 | func init() { 44 | RootPath, _ := os.UserConfigDir() 45 | RootPath = RootPath + "/Go-Wechaty-Bot" 46 | viper.Set("RootPath", RootPath) 47 | viper.Set("LogPath", RootPath+"/logs") 48 | log.SetFormatter(&log.JSONFormatter{ 49 | TimestampFormat: "2006-01-02 15:04:05", 50 | CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) { 51 | fileName := fmt.Sprintf(" %v:%v ", path.Base(frame.File), strconv.Itoa(frame.Line)) 52 | function = strings.Replace(path.Ext(frame.Function), ".", "", -1) 53 | return function, fileName 54 | }, 55 | //PrettyPrint: true, 56 | }) 57 | log.SetOutput(io.MultiWriter(os.Stdout, &lumberjack.Logger{ 58 | Filename: viper.GetString("LogPath") + "/Go-Wechaty-Bot.log", // 日志文件位置 59 | MaxSize: 50, // 单文件最大容量,单位是MB 60 | MaxBackups: 1, // 最大保留过期文件个数 61 | MaxAge: 365, // 保留过期文件的最大时间间隔,单位是天 62 | Compress: true, // 是否需要压缩滚动日志, 使用的 gzip 压缩 63 | })) 64 | } 65 | 66 | func main() { 67 | defer func() { 68 | if err := recover(); err != nil { 69 | log.Errorf("Error: %v", err) 70 | } 71 | }() 72 | Token, Endpoint := tokenGet() 73 | info(Token, Endpoint) 74 | for i := 0; i <= 10; i++ { 75 | var bot = NewWechaty(WithPuppetOption(puppet.Option{ 76 | Endpoint: Endpoint, 77 | Token: Token, 78 | })) 79 | botMain(bot) 80 | heartbeat(bot) 81 | daemonStart(bot, i) 82 | } 83 | } 84 | 85 | func botMain(bot *Wechaty) { 86 | bot.OnScan(func(context *Context, qrCode string, status schemas.ScanStatus, data string) { 87 | log.Infof("Scan QR Code: [ https://wechaty.js.org/qrcode/%s ] [%s] [%s]", qrCode, status, data) 88 | }).OnLogin(func(context *Context, user *user.ContactSelf) { 89 | log.Infof(user.Name() + " login") 90 | }).OnLogout(func(context *Context, user *user.ContactSelf, reason string) { 91 | log.Infof(user.Name() + " logout") 92 | }).OnError(func(context *Context, err error) { 93 | log.Error(err) 94 | }).OnMessage(func(context *Context, message *user.Message) { 95 | if message.Room() != nil { 96 | log.Infof("{User: %v, ID: %v}, {Group: %v, ID: %v}, Context: %v", message.Talker().Name(), message.Talker().ID(), message.Room().Topic(), message.Room().ID(), message.Text()) 97 | } else { 98 | log.Infof("{User: %v, ID: %v}, Context: %v", message.Talker().Name(), message.Talker().ID(), message.Text()) 99 | } 100 | 101 | if message.Type() != schemas.MessageTypeText || message.Age() > 2*60*time.Second || message.Text() != "ding" { 102 | return 103 | } 104 | if _, err := message.Say(result()); err != nil { 105 | log.Errorf("message.Say Error: [%v]", err) 106 | } 107 | }) 108 | } 109 | 110 | func heartbeat(bot *Wechaty) { 111 | nyc, _ := time.LoadLocation("Asia/Shanghai") 112 | c := cron.New(cron.WithLocation(nyc)) 113 | if _, err := c.AddFunc("0 23 * * *", func() { 114 | var roomID _interface.IRoom 115 | //if roomID = bot.Room().Find(&schemas.RoomQueryFilter{Id: "roomID@chatroom"}); roomID == nil { 116 | if roomID = bot.Room().Find("Debug"); roomID == nil { 117 | dingSend("RoomID Find Error") 118 | log.Infof("RoomID Find Error") 119 | return 120 | } 121 | 122 | if _, err := roomID.Say(result()); err != nil { 123 | dingSend("failed to send messages") 124 | log.Errorf("onHeartbeat Say Error: [%v]", err) 125 | return 126 | } 127 | log.Infof("Heartbeat Say Success") 128 | }); err != nil { 129 | dingSend("Heartbeat Cron Add Error: " + err.Error()) 130 | log.Errorf("Heartbeat Cron Add Error: [%v]", err) 131 | } 132 | c.Start() 133 | } 134 | 135 | func daemonStart(bot *Wechaty, i int) { 136 | if err := bot.Start(); err != nil { 137 | log.Infof("[main] Bot 错误: %v", err) 138 | if i > 10 { 139 | os.Exit(0) 140 | } 141 | log.Printf("正在重新启动程序, 当前重试次数: 第%v次", i) 142 | time.Sleep(10 * time.Second) 143 | } else { 144 | i = 0 145 | var quitSig = make(chan os.Signal) 146 | signal.Notify(quitSig, os.Interrupt, os.Kill, syscall.SIGINT, syscall.SIGTERM) 147 | select { 148 | case <-quitSig: 149 | log.Fatal("程序退出!") 150 | } 151 | } 152 | } 153 | 154 | /* SET YOUT TOKEN */ 155 | func tokenGet() (Token, Endpoint string) { 156 | /**************************/ 157 | 158 | Token = "insecure_xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 159 | Endpoint = "127.0.0.1:25000" 160 | 161 | /**************************/ 162 | 163 | if _, err := os.Stat(viper.GetString("RootPath") + "/config.yaml"); err == nil { 164 | log.Info("found config.yaml") 165 | viper.SetConfigName("config") 166 | viper.SetConfigType("yaml") 167 | viper.AddConfigPath(viper.GetString("RootPath")) 168 | if viper.ReadInConfig() == nil { 169 | token := viper.GetString("WECHATY.WECHATY_TOKEN") 170 | endpoint := viper.GetString("WECHATY.WECHATY_ENDPOINT") 171 | if token == "" || endpoint == "" { 172 | return Token, Endpoint 173 | } 174 | return token, endpoint 175 | } 176 | log.Infof(err.Error()) 177 | } 178 | return Token, Endpoint 179 | } 180 | 181 | func info(Token, Endpoint string) { 182 | fmt.Println("\n\n--------------------") 183 | fmt.Printf("LogPath: %v/Go-Wechaty-Bot.log \n", viper.GetString("LogPath")) 184 | fmt.Printf("Config: %v/config.yaml \n", viper.GetString("RootPath")) 185 | fmt.Printf("Token: %v \n", Token) 186 | fmt.Printf("Endpoint: %v \n", Endpoint) 187 | fmt.Println("--------------------\n\n") 188 | } 189 | 190 | func dingSend(msg string) { 191 | cli := dingtalk.InitDingTalkWithSecret(viper.GetString("Ding.TOKEN"), viper.GetString("Ding.SECRET")) 192 | if err := cli.SendMarkDownMessage(msg, msg); err != nil { 193 | log.Errorf("DingMessage Error: [%v]", err) 194 | return 195 | } 196 | log.Infof("DingTalk 通知成功!") 197 | } 198 | 199 | func result() string { 200 | type Jin struct { 201 | Content string 202 | origin string 203 | Author string 204 | Category string 205 | } 206 | 207 | var ( 208 | jin Jin 209 | resp *http.Response 210 | err error 211 | ) 212 | // 发起请求 213 | resp, err = http.Get("https://v1.jinrishici.com/shuqing") 214 | if err != nil { 215 | log.Errorf("今日诗词接口请求错误: [%v] ", err) 216 | } 217 | // 关闭请求 218 | defer func(Body io.ReadCloser) { 219 | if err = Body.Close(); err != nil { 220 | log.Errorf("今日诗词请求 Close Body Error: [%v]", err) 221 | } 222 | }(resp.Body) 223 | // 解析响应 224 | if err = json.NewDecoder(resp.Body).Decode(&jin); err != nil { 225 | log.Errorf("今日诗词接口解析错误: [%v]", err) 226 | } 227 | if jin.Content == "" { 228 | jin.Content = "情如之何,暮涂为客,忍堪送君。" 229 | } 230 | return jin.Content 231 | } 232 | -------------------------------------------------------------------------------- /Server/test/祝福助手.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | log "github.com/sirupsen/logrus" 7 | "github.com/spf13/viper" 8 | . "github.com/wechaty/go-wechaty/wechaty" 9 | puppet "github.com/wechaty/go-wechaty/wechaty-puppet" 10 | "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" 11 | _interface "github.com/wechaty/go-wechaty/wechaty/interface" 12 | "github.com/wechaty/go-wechaty/wechaty/user" 13 | "io/ioutil" 14 | "os" 15 | "path" 16 | "runtime" 17 | "strconv" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | /* 23 | 24 | ***************************** 25 | TODO 26 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build test.go 27 | 28 | init().RootPath 29 | main().Endpoint 30 | main().Token 31 | heartbeat().roomID 32 | 33 | ***************************** 34 | 35 | */ 36 | 37 | var ( 38 | lists []_interface.IContact 39 | err error 40 | ) 41 | 42 | func init() { 43 | RootPath, _ := os.UserConfigDir() 44 | RootPath = RootPath + "/Go-Wechaty-Bot" 45 | viper.Set("RootPath", RootPath) 46 | viper.Set("LogPath", RootPath+"/logs") 47 | viper.SetConfigName("config") 48 | viper.SetConfigType("yaml") 49 | viper.AddConfigPath(viper.GetString("RootPath")) 50 | err := viper.ReadInConfig() 51 | if err != nil { 52 | log.Errorf("viper.ReadInConfig() Error: [%v]", err) 53 | } 54 | log.SetFormatter(&log.JSONFormatter{ 55 | TimestampFormat: "2006-01-02 15:04:05", 56 | CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) { 57 | fileName := fmt.Sprintf(" %v:%v ", path.Base(frame.File), strconv.Itoa(frame.Line)) 58 | function = strings.Replace(path.Ext(frame.Function), ".", "", -1) 59 | return function, fileName 60 | }, 61 | //PrettyPrint: true, 62 | }) 63 | } 64 | 65 | func main() { 66 | var bot = NewWechaty(WithPuppetOption(puppet.Option{ 67 | Token: viper.GetString("WECHATY.WECHATY_TOKEN"), 68 | Endpoint: viper.GetString("WECHATY.WECHATY_ENDPOINT"), 69 | })) 70 | bot.OnScan(func(context *Context, qrCode string, status schemas.ScanStatus, data string) { 71 | log.Infof("Scan QR Code: [ https://wechaty.js.org/qrcode/%s ] [%s] [%s]", qrCode, status, data) 72 | }).OnLogin(func(context *Context, user *user.ContactSelf) { 73 | log.Infof(user.Name() + " login") 74 | }).OnLogout(func(context *Context, user *user.ContactSelf, reason string) { 75 | log.Infof(user.Name() + " logout") 76 | }).OnError(func(context *Context, err error) { 77 | log.Error(err) 78 | }).OnMessage(func(context *Context, message *user.Message) { 79 | if message.Self() || message.Talker().ID() == viper.GetString("BOT.ADMINID") { 80 | } else { 81 | return 82 | } 83 | if message.Type() != schemas.MessageTypeText { 84 | return 85 | } 86 | if !strings.Contains(message.Text(), "节日祝福") { 87 | return 88 | } 89 | if message.Text()[0:13] == "节日祝福 " { 90 | if _, err = os.Stat("friend.json"); err != nil { 91 | getAllToFile(message.GetWechaty().Contact()) 92 | SayMessage(message, fmt.Sprintf("群发开始,共计%v人", len(lists))) 93 | } else { 94 | readFromFile() 95 | SayMessage(message, fmt.Sprintf("群发继续, 剩余%v人", len(lists))) 96 | } 97 | //if msg == "" { 98 | // if message.Text()[0:7] == "群发 " { 99 | // msg = message.Text()[7:] 100 | // } 101 | // if message.Text()[0:8] == "forward " { 102 | // msg = message.Text()[8:] 103 | // } 104 | //} 105 | for i := 1; i < len(lists); i = 0 { 106 | if _, err = message.GetWechaty().Contact().Load(lists[i].ID()).Say(fmt.Sprintf("嗨,亲爱的%v, %v", lists[i].Name(), message.Text()[13:])); err != nil { 107 | _, _ = message.Say(fmt.Sprintf("群发失败, 剩余%v人未发送成功", len(lists))) 108 | writeToFile() 109 | return 110 | } 111 | lists = append(lists[:0], lists[(1):]...) 112 | writeToFile() 113 | time.Sleep(time.Second * 8) 114 | } 115 | if err := os.Remove("friend.json"); err != nil { 116 | log.Errorf("os.Remove Error: [%v]", err) 117 | return 118 | } 119 | } 120 | if message.Text()[0:19] == "节日祝福测试 " { 121 | SayMessage(message, fmt.Sprintf("嗨, 亲爱的%v, %v", message.Talker().Name(), message.Text()[19:])) 122 | } 123 | }) 124 | bot.DaemonStart() 125 | } 126 | 127 | func getAllToFile(c _interface.IContactFactory) { 128 | var lists2 []_interface.IContact 129 | lists = c.FindAll(nil) 130 | log.Infoln("ContactList: 加载成功") 131 | for _, v := range lists { 132 | if v.Type() != schemas.ContactTypePersonal { 133 | continue 134 | } 135 | if !v.Friend() { 136 | continue 137 | } 138 | lists2 = append(lists2, v) 139 | } 140 | lists = lists2 141 | writeToFile() 142 | } 143 | 144 | func readFromFile() { 145 | result, err := ioutil.ReadFile("friend.json") 146 | if err != nil { 147 | log.Errorf("ioutil.ReadFile Error: [%v]", err) 148 | return 149 | } 150 | err = json.Unmarshal(result, &lists) 151 | if err != nil { 152 | log.Errorf("json.Unmarshal Error: [%v]", err) 153 | return 154 | } 155 | } 156 | 157 | func writeToFile() { 158 | result, err := json.Marshal(lists) 159 | if err != nil { 160 | log.Errorf("json.Marshal Error: [%v]", err) 161 | return 162 | } 163 | _ = ioutil.WriteFile("friend.json", result, 0644) 164 | } 165 | 166 | func SayMessage(message *user.Message, msg string) { 167 | if _, err = message.Say(msg); err != nil { 168 | log.Errorf("[SayMessage] [%v], error: %v", msg, err) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /docs/Architecture.md: -------------------------------------------------------------------------------- 1 | # Go-Wechaty-Bot 2 | 3 | ```mermaid 4 | flowchart LR 5 | Go-Wechaty-Bot{Go-Wechaty-Bot}-->Gateway{Gateway}; 6 | Go-Wechaty-Bot{Go-Wechaty-Bot}-->Server{Server}; 7 | ``` 8 | 9 | ## Gateway 10 | 11 | ```mermaid 12 | flowchart LR 13 | Gateway{Gateway}-->Wechat{Wechat}-->Gateway{Wechaty}; 14 | ``` 15 | 16 | ## Server 17 | 18 | ```mermaid 19 | flowchart LR 20 | Server{Server}-->Gateway{Wechaty}-->Server{Server}; 21 | ``` 22 | 23 | ### Init 24 | ```mermaid 25 | flowchart LR 26 | main-->init-->viper-->config; 27 | config-->viper-->init-->main; 28 | main-->wechaty-->web[OnScan]; 29 | main-->wechaty-->onLogin; 30 | wechaty-->onLogout; 31 | wechaty-->onError; 32 | wechaty-->onMessage; 33 | wechaty-->onFriendShip 34 | ``` 35 | 36 | ### Main 37 | ```mermaid 38 | flowchart LR 39 | main-->init-->viper-->config; 40 | config-->viper-->init-->main; 41 | main-->wechaty-->web[OnScan]; 42 | main-->wechaty-->onlogin; 43 | wechaty-->onLogout; 44 | wechaty-->onError; 45 | wechaty-->onMessage; 46 | wechaty-->onFriendShip 47 | ``` 48 | 49 | #### onLogin 50 | 51 | ```mermaid 52 | flowchart LR 53 | onLogin-->viper-->config-->viper-->onLogin 54 | ``` 55 | 56 | #### onMessage 57 | 58 | ```mermaid 59 | flowchart LR 60 | onMessage-->Room 61 | onMessage-->Friend 62 | Friend-->keyword 63 | Room-->keyword 64 | keyword-->Admin 65 | keyword-->Custom 66 | keyword-->Tuling 67 | Tuling-->Viper-->Key 68 | Custom-->Viper-->Key 69 | Admin-->Viper-->Key 70 | onMessage-->消息订阅推送{Api}-->Room 71 | ``` 72 | 73 | #### onFriendShip 74 | 75 | ```mermaid 76 | flowchart LR 77 | onFriendShip-->isFriend 78 | onFriendShip-->notFriend 79 | ``` 80 | 81 | #### onLogout 82 | 83 | ```mermaid 84 | flowchart LR 85 | 账户退出{onLogout}-->DingBot-->exit 86 | ``` -------------------------------------------------------------------------------- /docs/Image/bot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRSec/Go-Wechaty-Bot/fe5c181d33e38aa0c67170d6f2f256a4f6119ab5/docs/Image/bot.jpg -------------------------------------------------------------------------------- /docs/Log.md: -------------------------------------------------------------------------------- 1 | ## 09/21 2 | 3 | - 修复定时任务异常问题 4 | 5 | ## 09/16 6 | 7 | - 拆分 插件库 文件 8 | 9 | ## 09/13 10 | 11 | - 拆分 Xp 库文件 12 | 13 | ## 09/08 14 | 15 | - Add 节日祝福助手 16 | - Disable AutoReply 17 | - mkdir test folder 18 | - 添加健康监测 19 | 20 | ## 07/07 21 | 22 | - Add CronTab 23 | 24 | ## 07/06 25 | - Add Gateway Docker Solution 26 | - Add Gateway Container 27 | - Add AutoRestart Docker Solution 28 | - Add AutoRestart Container 29 | - Fix Gateway Connection Error 30 | - Fix Gateway Reconnection Error 31 | - Add DataProcessimg Bot 32 | - Fix padlocal README.md 33 | - Add padlocal wechatyGateway.sh 34 | - 更新配置文件位置 请查看日志获取路径 35 | - 更新日志文件位置 请查看日志获取路径 36 | 37 | ## 05/15 38 | 39 | - Add sql plugin 40 | - del AutoInfo key 41 | - add ID key 42 | 43 | ## 05/11 44 | 45 | - Add GroupPass @Plig.Group 46 | 47 | ## 05/09 48 | 49 | - onMessage 函数 中注册的全局 struct 值容易错,是否启用线程锁? 50 | 51 | ## 05/05 52 | 53 | - 更新DingTalk 验证方式 54 | - 添加取消回复状态和原因 55 | - 添加文件识别功能 56 | 57 | ## 04/22 58 | 59 | - 新增时间限制 ChatTimeLimit 60 | - 修改夜间模式逻辑 NightMode 61 | - 修复聊天异常 bug 62 | 63 | ## 04/19 64 | 65 | - 修复 TLS 错误 66 | - 添加 TLS 选项验证 67 | - 更新 脚本 68 | - 修复夜间模式 bug 69 | 70 | ## 04/18 71 | 72 | - 完善 `加好友、踢人、邀请进群` 的功能 73 | - 添加`退群、倒计时` 的功能 74 | - 关闭 `OnRoomInvite` 的功能 75 | - 修改 `DingMessage` 的传参方式 76 | - 修复 `@ 异常` 的问题 77 | - 添加 `管理员 夜间模式不受限制` 78 | - 加强 `OnError` 的回溯功能 79 | - 移除关键字模式,改为 `微信对话开放平台` 80 | - 修复 自动更新文档 bug 81 | 82 | ## 04/10 83 | 84 | - 添加日志输出 json 格式 85 | 86 | - ```diff 87 | -- AutoInfo: "用户ID: [" + UserID + "] 用户名称: [" + UserName + "]" + message.Text() +"]", 88 | ++ AutoInfo: message.Text(), 89 | ``` 90 | 91 | - 添加 Tuling 图灵机器人 92 | - 添加微信对话平台机器人 93 | - 添加夜间模式 94 | - 调整 消息保存格式 95 | 96 | ## 04/09 97 | 98 | - 添加失败重试机制, 暂时没有奇怪的错误验证 99 | - 添加退出保存配置的功能 100 | - 添加自动编译功能 101 | 102 | ## 04/07 103 | 104 | - 添加 `Atme` 方法 用来替代官方的 `message.MentionSelf()` 方法,需要填写机器人 名称 bot: name: 随缘 105 | 106 | ## 04/05 107 | 108 | - 重构架构,所有功能在Plug文件夹,正在计划性删除 其他文件夹 109 | - Keyword 自定义内容回复 110 | - @机器人的事件只能捕捉到 `@机器人名字` 已经发起[issues](https://github.com/wechaty/puppet-xp/issues/97) 111 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Go-Wechaty-Bot 2 | 3 | > 仅供学习使用,*请勿用于非法用途*! 4 | 5 | [1]: https://img.shields.io/badge/puppet-xp-blue 6 | [2]: https://img.shields.io/badge/puppet-padlocal-blue 7 | [3]: https://img.shields.io/badge/puppet-4u-blue 8 | [5]: https://github.com/XRSec/Go-Wechaty-Bot-XP 9 | [6]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/padlocal#go-wechaty-bot-padlcoal-protocol 10 | [7]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/4u#go-wechaty-bot-4u-protocol 11 | 12 | [![puppet-xp][1]][5] 〰️ [![puppet-padlocal][2]][6] 〰️ [![puppet-4u][3]][7] 「 Select Gateway 」 13 | 14 | ## Architecture 15 | 16 | ```mermaid 17 | flowchart LR 18 | Polyglot-->Python 19 | Polyglot-->Go 20 | Polyglot -->Rust 21 | Python-->Grpc 22 | Go-->Grpc 23 | Rust-->Grpc 24 | Grpc-->Gateway{Gateway}-->Puppet{Puppet} 25 | Grpc-->Docker{Docker}-->Puppet{Puppet} 26 | Puppet{Puppet}-->xp-->微信 27 | Puppet{Puppet}-->padlcoal-->微信 28 | Puppet{Puppet}-->wechat4u-->微信 29 | ``` 30 | 31 | ## General 32 | 33 | 1. Clone Repo 34 | 35 | ```bash 36 | git clone https://github.com/XRSec/Go-Wechaty-Bot.git 37 | cd Go-Wechaty-Bot 38 | git submodule update --init --recursive 39 | ``` 40 | 41 | 2. Install the Packages ( [! Docker](https://github.com/XRSec/Go-Wechaty-Bot/blob/main/padlocal/wechatyGateway.sh)) 42 | 43 | ```bash 44 | # node-v16 45 | npm --registry http://registry.npmmirror.com install -g wechaty 46 | ``` 47 | 48 | 3. Edit `Server/config.yaml`. 49 | 50 | ```yaml 51 | bot: 52 | adminid: wxid_xxxxx 53 | name: xxxxxxxx 54 | ding: 55 | keyword: Wechaty 56 | token: xxxxxxxxxxxxxxxxxx 57 | url: https://oapi.dingtalk.com/robot/send?access_token= 58 | tuling: 59 | token: xxxxxxxxxxxxxxxx&info= 60 | url: http://www.tuling123.com/openapi/api?key= 61 | wechaty: 62 | wechaty_puppet_endpoint: 127.0.0.1:25001 63 | wechaty_puppet_service_token: insecure_xxxxxxxxxxxxxxxxxxxxxx 64 | wxopenai: 65 | env: online 66 | token: xxxxxxxxxxxxxxxxxxxxx 67 | ``` 68 | 69 | 4. Checking the Network Environment 70 | 71 | ```go 72 | if Gateway.IP = Server.IP { 73 | IP = NAT_IP // 192.168.0.10 74 | return // pass port 75 | } else { 76 | IP = InterNet_IP // curl cip.cc | https://ip.skk.moe/ 77 | } 78 | 79 | if PORT on Server is open { 80 | continue 81 | } else { 82 | os.exit(0) // port can't vist 83 | } 84 | // ncat ip port -vv // test 85 | ``` 86 | 87 | ## ⚓️ Re 88 | 89 | 1. [wechat-bot](https://github.com/cixingguangming55555/wechat-bot/blob/master/pic/doc.md) 90 | 91 | 2. [puppet-services](https://wechaty.js.org/docs/puppet-services/diy/#all-in-one-command) 92 | 93 | 3. [issues](https://github.com/wechaty/puppet-xp/issues/38) 94 | 95 | ## ⚠️ Debug 96 | 97 | 1. Network 98 | 99 | ```bash 100 | curl cip.cc 101 | curl -s https://api.chatie.io/v0/hosties/[WECHATY_TOKEN] 102 | ``` 103 | 104 | 2. Help me improve this project 105 | 106 | 3. Submit bugs and interesting features 107 | 108 | ## Update Repo 109 | 110 | 1. TODO && example.yaml 111 | 2. Update.md && Log.md 112 | 113 | ## Contact me 114 | 115 | Reply to group chat with us 116 | 117 | ![wxid: Tencent-IoT-Lab](Image/bot.jpg) 118 | -------------------------------------------------------------------------------- /docs/Update.md: -------------------------------------------------------------------------------- 1 | ## Add 2 | 3 | - 添加插件示例 4 | - 拆分 插件库 5 | 6 | ## TODO 7 | 8 | - 等待 [Wechaty-UI](https://github.com/wechaty/wechaty-ui) 正式上线 -------------------------------------------------------------------------------- /docs/submodule.md: -------------------------------------------------------------------------------- 1 | ## 初始化 2 | 3 | ```bash 4 | git clone git@github.com:XRSec/Go-Wechaty-Bot.git 5 | git submodule update --init --recursive 6 | ``` 7 | 8 | ## 更新本地代码 9 | 10 | ```bash 11 | git submodule update --recursive 12 | ``` 13 | 14 | ## 开发者 15 | 16 | ```bash 17 | cd Server/Plug 18 | git submodule update --recursive 19 | git checkout main 20 | git add . 21 | git commit -am "update" 22 | cd ../.. 23 | git add . 24 | git commit -am "update" 25 | git push --recurse-submodules=on-demand 26 | ``` -------------------------------------------------------------------------------- /padlocal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | LABEL maintainer="xrsec" 3 | LABEL mail="Jalapeno1868@outlook.com" 4 | LABEL Github="https://github.com/XRSec/Go-Wechaty-Bot" 5 | LABEL org.opencontainers.image.source="https://github.com/XRSec/Go-Wechaty-Bot" 6 | LABEL org.opencontainers.image.title="Go-Wechaty-Bot" 7 | 8 | WORKDIR /opt/go-wechaty-bot 9 | COPY . . 10 | 11 | RUN npm install --registry=https://registry.npmmirror.com 12 | 13 | WORKDIR /opt/go-wechaty-bot/node_modules/.bin/ 14 | 15 | EXPOSE 25000 16 | ENV NODE_ENV production 17 | ENV WECHATY_PUPPET wechaty-puppet-padlocal 18 | ENV WECHATY_PUPPET_SERVER_PORT 25000 19 | ENV WECHATY_TOKEN insecure_xxxxxxxxxxxxxxxxxxxxxxxxx 20 | ENV WECHATY_PUPPET_PADLOCAL_TOKEN puppet_padlocal_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 21 | ENV LANG 'zh_CN.UTF-8' 22 | STOPSIGNAL SIGQUIT 23 | 24 | CMD ["npm", "run", "serve"] -------------------------------------------------------------------------------- /padlocal/README.md: -------------------------------------------------------------------------------- 1 | ## Go-Wechaty-Bot PADLCOAL PROTOCOL 2 | 3 | > 仅供学习使用,*请勿用于非法用途*! 4 | 5 | [1]: https://img.shields.io/badge/puppet-xp-blue 6 | [2]: https://img.shields.io/badge/puppet-padlocal-blue 7 | [3]: https://img.shields.io/badge/puppet-4u-blue 8 | [5]: https://github.com/XRSec/Go-Wechaty-Bot-XP 9 | [6]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/padlocal#go-wechaty-bot-padlcoal-protocol 10 | [7]: https://github.com/XRSec/Go-Wechaty-Bot/tree/main/4u#go-wechaty-bot-4u-protocol 11 | 12 | [![puppet-xp][1]][5] 〰️ [![puppet-padlocal][2]][6] 〰️ [![puppet-4u][3]][7] 「 Select Gateway 」 13 | 14 | ## Info 15 | 16 | ### Glossary 17 | 18 | ```ini 19 | Gateway : puppet-padlocal 20 | Server: go-wechaty 21 | ``` 22 | 23 | ### Architecture 24 | 25 | ```mermaid 26 | flowchart LR 27 | Polyglot-->Python 28 | Polyglot-->Go 29 | Polyglot -->Rust 30 | Python-->Grpc 31 | Go-->Grpc 32 | Rust-->Grpc 33 | Grpc-->Docker{Docker}-->padlocal 34 | padlocal-->微信 35 | ``` 36 | 37 | ## ⇲ Use 38 | 39 | ### Init (depend main.General) 40 | 41 | 1. Checkout branch 42 | 43 | ```bash 44 | cd padlocal 45 | ``` 46 | 47 | 2. Get & Generate Token 48 | 49 | ```bash 50 | WECHATY_PUPPET_PADLOCAL_TOKEN: http://pad-local.com/#/tokens 51 | WECHATY_TOKEN:"insecure_" + curl -s https://www.uuidgenerator.net/api/version4 52 | WECHATY_PUPPET_SERVICE_TOKEN:WECHATY_TOKEN 53 | ``` 54 | 55 | 3. Modifying a Configuration File 56 | 57 | ```bash 58 | # wechatyGateway.sh 59 | export WECHATY_TOKEN="insecure_xxxxxxxx_xxxx_xxxx" 60 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_xxxxxxxxxxxxxxxx" 61 | export WECHATY_PUPPET_SERVER_PORT="25000" 62 | ``` 63 | 64 | 71 | 72 | ### Start Server 73 | 74 | ```bash 75 | cd padlocal && npm install && bash ./wechatyGateway.sh # Start puppet-padlocal Gateway 76 | cd server && go run main.go # Start Server 77 | ``` 78 | -------------------------------------------------------------------------------- /padlocal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-wechaty-bot", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/XRSec/Go-Wechaty-Bot.git", 6 | "url" : "https://xrsec.vercel.app/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/XRSec/Go-Wechaty-Bot.git" 10 | }, 11 | "dependencies": { 12 | "wechaty": "^1.20.2", 13 | "wechaty-puppet-padlocal": "^1.20.1" 14 | }, 15 | "scripts": { 16 | "serve": "node_modules/.bin/wechaty gateway --puppet ${WECHATY_PUPPET} --port ${WECHATY_PUPPET_SERVER_PORT} --token ${WECHATY_TOKEN} --puppet-token ${WECHATY_PUPPET_PADLOCAL_TOKEN}" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /padlocal/wechatyGateway.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # set -ex 3 | 4 | # http://pad-local.com/ 5 | export WECHATY_PUPPET="wechaty-puppet-padlocal" 6 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 7 | # http://pad-local.com/ 8 | 9 | export WECHATY_TOKEN="insecure_xxxxxxxxxxxxxxxxxxxxxxxxx" 10 | # export WECHATY_PUPPET_SERVICE_TOKEN="insecure_$(curl -s https://www.uuidgenerator.net/api/version4)" 11 | 12 | export WECHATY_PUPPET_SERVER_PORT="25000" 13 | export WECHATY_LOG="verbose" # silent | error | warn | info | verbose | silly 14 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true" 15 | 16 | echo "WECHATY_TOKEN = ${WECHATY_TOKEN}" 17 | 18 | # *********************PLAN 1*********************** 19 | if [ ! -n "$1" ]; then 20 | export tag="gateway" 21 | else 22 | export tag=$1 23 | fi 24 | 25 | #docker images wechaty/wechaty | grep gateway | wc -l == 0 26 | 27 | if [ "${tag}" == "gateway" ] && [ "$(docker images wechaty/wechaty | grep -c gateway)" == "0" ]; then 28 | docker build -t wechaty/wechaty:gateway . 29 | fi 30 | 31 | docker run -itd \ 32 | --name Go-Wechaty-Bot \ 33 | --restart=always \ 34 | -e WECHATY_TOKEN \ 35 | -e TZ=Asia/Shanghai \ 36 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \ 37 | -e WECHATY_LOG \ 38 | -e WECHATY_PUPPET \ 39 | -e WECHATY_PUPPET_SERVER_PORT \ 40 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \ 41 | -p "${WECHATY_PUPPET_SERVER_PORT}:${WECHATY_PUPPET_SERVER_PORT}" \ 42 | wechaty/wechaty:"${tag}" 43 | # *********************PLAN 1*********************** 44 | 45 | 46 | # *********************PLAN 2*********************** 47 | #npm run serve 48 | # *********************PLAN 2*********************** 49 | --------------------------------------------------------------------------------