[
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.go]\nindent_style = tab\nindent_size = 2\n\n[{Makefile, Dockerfile}]\nindent_style = tab\nindent_size = 4\n\n[*.{yml, yaml, json}]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v2\n      with:\n        go-version: ^1.16\n      id: go\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v2\n\n    - name: Get dependencies\n      run: |\n        make mod\n\n    - name: Test\n      run: make test\n\n    - name: codecoe\n      run: bash <(curl -s https://codecov.io/bash)\n"
  },
  {
    "path": ".gitignore",
    "content": "dingtalk-darwin-amd64.zip\ndingtalk-darwin-arm64.zip\ndingtalk-linux-amd64.zip\ndingtalk-windows-386.zip\ndingtalk-windows-amd64.zip\ndingtalk.exe\n.idea\ncoverage.txt\ncoverage.data\n.scannerwork\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\"btns\", \"dingtalk\", \"gomock\", \"mitchellh\", \"OAPI\", \"Unpatch\"]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\n## v1.5.0\n\n### Added\n\n- Support the environment variable prefix\n\n## v1.4.0\n\n### Added\n\n- Support environment variables\n\n### Refactor\n\n- update to golang 1.18.1\n\n## v1.2.0\n\n### Refactor\n\n- project layout\n\n### Added\n\n- support config.xml\n- validate function\n- .editconfig file\n\n## v1.1.1\n\n### Fixed\n\n- Fix release response body resource, see more at <https://github.com/CatchZeng/dingtalk/pull/1>.\n- Fix bug about timestamp, see more at <https://github.com/CatchZeng/dingtalk/pull/2>.\n\n## v1.1.0\n\n### Added\n\n- actionCard\n- feedCard\n\n## v1.0.0\n\n### Added\n\n- text\n- link\n- markdown\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2020 Catch Zeng\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "Makefile",
    "content": "SHELL := /bin/bash\nBASEDIR = $(shell pwd)\n\nAPP_NAME=dingtalk\nAPP_VERSION=1.5.0\nIMAGE_NAME=\"catchzeng/${APP_NAME}:${APP_VERSION}\"\nIMAGE_LATEST=\"catchzeng/${APP_NAME}:latest\"\n\nall: mod fmt imports lint test\nfirst:\n\tgo install golang.org/x/tools/cmd/goimports@latest\n\tgo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest\nfmt:\n\tgofmt -w .\nmod:\n\tgo mod tidy\nimports:\n\tgoimports -w .\nlint:\n\tgolangci-lint run\n.PHONY: test\ntest:\n\tsh scripts/test.sh\ntest-sonar:\n\tgo test -gcflags=-l -coverpkg=./... -coverprofile=coverage.data ./...\nmock:\n\tsh scripts/mock.sh\n.PHONY: build\nbuild:\n\trm -f dingtalk\n\tgo build -o dingtalk main.go\nbuild-darwin-amd64:\n\trm -f dingtalk dingtalk-darwin-amd64.zip\n\tCGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o dingtalk main.go\n\tzip dingtalk-darwin-amd64.zip dingtalk\nbuild-darwin-arm64:\n\trm -f dingtalk dingtalk-darwin-arm64.zip\n\tCGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o dingtalk main.go\n\tzip dingtalk-darwin-arm64.zip dingtalk\nbuild-linux:\n\trm -f dingtalk dingtalk-linux-amd64.zip\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o dingtalk main.go\n\tzip dingtalk-linux-amd64.zip dingtalk\nbuild-win:\n\trm -f dingtalk.exe dingtalk-windows-amd64.zip\n\tCGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o dingtalk.exe main.go\n\tzip dingtalk-windows-amd64.zip dingtalk.exe\nbuild-win32:\n\trm -f dingtalk.exe dingtalk-windows-386.zip\n\tCGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o dingtalk.exe main.go\n\tzip dingtalk-windows-386.zip dingtalk.exe\nbuild-release:\n\tmake build-darwin-amd64\n\tmake build-darwin-arm64\n\tmake build-linux\n\tmake build-win\n\tmake build-win32\n\trm -f dingtalk dingtalk.exe\nbuild-docker:\n\tsh build/package/build.sh ${IMAGE_NAME}\npush-docker:\n\tdocker tag ${IMAGE_NAME} ${IMAGE_LATEST};\n\tdocker push ${IMAGE_NAME};\n\tdocker push ${IMAGE_LATEST};\nhelp:\n\t@echo \"first - first time\"\n\t@echo \"fmt - go format\"\n\t@echo \"mod - go mod tidy\"\n\t@echo \"imports - go imports\"\n\t@echo \"lint - run golangci-lint\"\n\t@echo \"test - unit test\"\n\t@echo \"mock - mockgen\"\n\t@echo \"build - build binary\"\n\t@echo \"build-mac - build mac binary\"\n\t@echo \"build-linux - build linux amd64 binary\"\n\t@echo \"build-win - build win amd64 binary\"\n\t@echo \"build-win32 - build win 386 binary\"\n\t@echo \"build-docker - build docker image\"\n\t@echo \"push-docker - push docker image to docker hub\"\n"
  },
  {
    "path": "README.md",
    "content": "# dingtalk\n\n![Go](https://github.com/CatchZeng/dingtalk/workflows/Go/badge.svg)\n[![codecov](https://codecov.io/gh/CatchZeng/dingtalk/branch/master/graph/badge.svg)](https://codecov.io/gh/CatchZeng/dingtalk)\n[![Go Report Card](https://goreportcard.com/badge/github.com/CatchZeng/dingtalk)](https://goreportcard.com/report/github.com/CatchZeng/dingtalk)\n[![Release](https://img.shields.io/github/release/CatchZeng/dingtalk.svg)](https://github.com/CatchZeng/dingtalk/releases)\n[![GoDoc](https://godoc.org/github.com/CatchZeng/dingtalk?status.svg)](https://pkg.go.dev/github.com/CatchZeng/dingtalk?tab=doc)\n\n[English](https://github.com/CatchZeng/dingtalk/blob/master/READMEEN.md)\n\n> DingTalk(dingding) 是钉钉机器人的 go 实现。支持 **Docker、Jenkinsfile、命令行模式，module 模式**；支持**加签**安全设置，支持**链式语法**创建消息；支持**文本、链接、Markdown、ActionCard、FeedCard** 消息类型。\n\n> 注：使用飞书的小伙伴，可以使用[飞书（feishu）版](https://github.com/CatchZeng/feishu)。\n\n## 文档\n\n[钉钉文档](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq)\n\n## 特性\n\n- [x] 支持[Docker](https://github.com/CatchZeng/dingtalk#Docker)\n\n- [x] 支持[Jenkinsfile](https://github.com/CatchZeng/dingtalk#Jenkinsfile)\n\n- [x] 支持[module](https://github.com/CatchZeng/dingtalk#%E4%BD%9C%E4%B8%BA-module)\n\n- [x] 支持[命令行模式](https://github.com/CatchZeng/dingtalk#%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7)\n\n- [x] 支持[配置文件](https://github.com/CatchZeng/dingtalk#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)\n\n- [x] 支持[环境变量](https://github.com/CatchZeng/dingtalk#%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F)\n\n- [x] 支持加签\n\n<img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1572261283991-f8e35f4d-6997-4a02-9704-843ee8f97464.png\" width = 50% />\n\n- [x] Text 消息\n\n<img src=\"https://img.alicdn.com/tfs/TB1jFpqaRxRMKJjy0FdXXaifFXa-497-133.png\" width = 50% />\n\n- [x] Link 消息\n\n<img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1570679827267-6243216b-d1c3-48b7-9b1e-0f0b4211b50b.png\" width = 50% />\n\n- [x] Markdown 消息\n\n<img src=\"https://img.alicdn.com/tfs/TB1yL3taUgQMeJjy0FeXXXOEVXa-492-380.png\" width = 50% />\n\n- [x] ActionCard 消息\n\n    <img src=\"https://img.alicdn.com/tfs/TB1nhWCiBfH8KJjy1XbXXbLdXXa-547-379.png\" width = 50% />\n\n    <img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1570679939723-c1fb7861-5bcb-4c30-9e1b-033932f6b72f.png\" width = 50% />\n\n- [x] FeedCard 消息\n\n<img src=\"http://img01.taobaocdn.com/top/i1/LB1R2evQVXXXXXDapXXXXXXXXXX\" width = 50% />\n\n## 安装\n\n## Docker 安装\n\n```shell\ndocker pull catchzeng/dingtalk\n```\n\n### 二进制安装\n\n到 [releases](https://github.com/CatchZeng/dingtalk/releases/) 下载相应平台的二进制可执行文件，然后加入到 PATH 环境变量即可。\n\n### go install 安装\n\n```sh\n# Go 1.16+\ngo install github.com/CatchZeng/dingtalk@v1.5.0\n\n# Go version < 1.16\ngo get -u github.com/CatchZeng/dingtalk@v1.5.0\n```\n\n## 使用方法\n\n### 配置文件\n\n可以在 `$/HOME/.dingtalk` 下创建 `config.yaml` 填入 `access_token` 和 `secret` 默认值。\n\n```yaml\naccess_token: \"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\nsecret: \"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n```\n\n### 环境变量\n\n```sh\n$ export ACCESS_TOKEN=\"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\n$ export SECRET=\"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n$ dingtalk link -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a\n```\n\n你也可以为环境变量设置一个**前缀**\n\n```sh\n$ export DINGTALK_ENV_PREFIX=\"DINGTALK_\"\n$ export DINGTALK_ACCESS_TOKEN=\"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\n$ export DINGTALK_SECRET=\"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n$ dingtalk link -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a\n```\n\n### Docker\n\n```shell\ndocker run catchzeng/dingtalk dingtalk text -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68 -c \"docker test\"\n```\n\n### Jenkinsfile\n\n```shell\npipeline {\n    agent {\n        docker {\n            image 'catchzeng/dingtalk:latest'\n        }\n    }\n    environment {\n        DING_TOKEN = '1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f'\n        DING_SECRET = 'SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68'\n    }\n    stages {\n        stage('notify') {\n            steps {\n                sh 'dingtalk link -t ${DING_TOKEN} -s ${DING_SECRET} -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a'\n            }\n        }\n    }\n}\n```\n\n### 作为 module\n\n```sh\ngo get github.com/CatchZeng/dingtalk\n```\n\n```go\npackage main\n\nimport (\n    \"log\"\n\n    \"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n)\n\nfunc main() {\n\taccessToken := \"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\n    secret := \"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n    client := dingtalk.NewClient(accessToken, secret)\n\n    msg := dingtalk.NewTextMessage().SetContent(\"测试文本&at 某个人\").SetAt([]string{\"177010xxx60\"}, false)\n    client.Send(msg)\n}\n```\n\n### 命令行工具\n\n#### Demo\n\n```shell\n$ dingtalk text -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68 -c \"测试命令行 & at 某个人\" -m \"177010xxx60\",\"177010xxx61\"\n```\n\n```shell\n$ dingtalk markdown -D -i \"杭州天气\" -e '## 杭州天气 @150XXXXXXXX\n > 9度，西北风1级，空气良89，相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com)' -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\n\n{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"杭州天气\",\"text\":\"## 杭州天气 @150XXXXXXXX\\n \\u003e 9度，西北风1级，空气良89，相对温度73%\\n \\u003e ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\\n \\u003e ###### 10点20分发布 [天气](https://www.dingtalk.com)\"},\"at\":{\"atMobiles\":[],\"isAtAll\":false}}\n```\n\n> -D 参数：打印发送的消息内容\n\n#### Help\n\n```shell\n$ dingtalk -h\ndingtalk is a command line tool for DingTalk\n\nUsage:\n  dingtalk [command]\n\nAvailable Commands:\n  actionCard  send actionCard message with DingTalk robot\n  feedCard    send feedCard message with DingTalk robot\n  help        Help about any command\n  link        send link message with DingTalk robot\n  markdown    send markdown message with DingTalk robot\n  text        send text message with DingTalk robot\n  version     dingtalk version\n\nFlags:\n  -t, --access_token string   access_token\n  -m, --atMobiles strings     atMobiles\n  -D, --debug                 debug\n  -h, --help                  help for dingtalk\n  -a, --isAtAll               isAtAll\n  -s, --secret string         secret\n\nUse \"dingtalk [command] --help\" for more information about a command.\n```\n\n## Stargazers\n\n[![Stargazers over time](https://starchart.cc/CatchZeng/dingtalk.svg)](https://starchart.cc/CatchZeng/dingtalk)\n"
  },
  {
    "path": "READMEEN.md",
    "content": "# dingtalk\n\n![Go](https://github.com/CatchZeng/dingtalk/workflows/Go/badge.svg)\n[![codecov](https://codecov.io/gh/CatchZeng/dingtalk/branch/master/graph/badge.svg)](https://codecov.io/gh/CatchZeng/dingtalk)\n[![Go Report Card](https://goreportcard.com/badge/github.com/CatchZeng/dingtalk)](https://goreportcard.com/report/github.com/CatchZeng/dingtalk)\n[![Release](https://img.shields.io/github/release/CatchZeng/dingtalk.svg)](https://github.com/CatchZeng/dingtalk/releases)\n[![GoDoc](https://godoc.org/github.com/CatchZeng/dingtalk?status.svg)](https://pkg.go.dev/github.com/CatchZeng/dingtalk?tab=doc)\n\n[中文](https://github.com/CatchZeng/dingtalk/blob/master/README.md)\n\n> DingTalk (dingding) is the go implementation of the DingTalk robot. Support **Docker, Jenkinsfile,command line mode, module mode**, **signature security settings, chain syntax** to create messages, support **text, link, markdown、ActionCard、FeedCard** message types.\n\n## Doc\n\n[ding-doc](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq)\n\n## Feature\n\n- [x] Support [Docker](https://github.com/CatchZeng/dingtalk#Docker)\n\n- [x] Support [Jenkinsfile](https://github.com/CatchZeng/dingtalk#Jenkinsfile)\n\n- [x] Support [module](https://github.com/CatchZeng/dingtalk/blob/master/READMEEN.md#use-as-module)\n\n- [x] Support [Command Line Mode](https://github.com/CatchZeng/dingtalk/blob/master/READMEEN.md#use-as-command-line-tool)\n\n- [x] Support [config.yaml](https://github.com/CatchZeng/dingtalk/blob/master/READMEEN.md#config.yaml)\n\n- [x] Support [environment variables](https://github.com/CatchZeng/dingtalk#environment%20variables)\n\n- [x] Support sign\n\n<img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1572261283991-f8e35f4d-6997-4a02-9704-843ee8f97464.png\" width = 50% />\n\n- [x] Text message\n\n<img src=\"https://img.alicdn.com/tfs/TB1jFpqaRxRMKJjy0FdXXaifFXa-497-133.png\" width = 50% />\n\n- [x] Link message\n\n<img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1570679827267-6243216b-d1c3-48b7-9b1e-0f0b4211b50b.png\" width = 50% />\n\n- [x] Markdown message\n\n<img src=\"https://img.alicdn.com/tfs/TB1yL3taUgQMeJjy0FeXXXOEVXa-492-380.png\" width = 50% />\n\n- [x] ActionCard message\n\n  <img src=\"https://img.alicdn.com/tfs/TB1nhWCiBfH8KJjy1XbXXbLdXXa-547-379.png\" width = 50% />\n\n  <img src=\"https://dingtalkdoc.oss-cn-beijing.aliyuncs.com/images/0.0.210/1570679939723-c1fb7861-5bcb-4c30-9e1b-033932f6b72f.png\" width = 50% />\n\n- [x] FeedCard message\n\n<img src=\"http://img01.taobaocdn.com/top/i1/LB1R2evQVXXXXXDapXXXXXXXXXX\" width = 50% />\n\n## Install\n\n## with Docker\n\n```shell\ndocker pull catchzeng/dingtalk\n```\n\n### binary\n\nGo to [releases](https://github.com/CatchZeng/dingtalk/releases/) to download the binary executable file of the corresponding platform, and then add it to the PATH environment variable.\n\n### with go install\n\n```sh\n# Go 1.16+\ngo install github.com/CatchZeng/dingtalk@v1.5.0\n\n# Go version < 1.16\ngo get -u github.com/CatchZeng/dingtalk@v1.5.0\n```\n\n## Usage\n\n### config.yaml\n\nYou can create `config.yaml` under `$/HOME/.dingtalk` and fill in the default values of `access_token` and `secret`.\n\n```yaml\naccess_token: \"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\nsecret: \"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n```\n\n### environment variables\n\n```sh\n$ export ACCESS_TOKEN=1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\n$ export SECRET=SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\n$ dingtalk link -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a\n```\n\nYou can also set a **prefix** for environment variables.\n\n```sh\n$ export DINGTALK_ENV_PREFIX=\"DINGTALK_\"\n$ export DINGTALK_ACCESS_TOKEN=\"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\n$ export DINGTALK_SECRET=\"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n$ dingtalk link -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a\n```\n\n### Docker\n\n```shell\ndocker run catchzeng/dingtalk dingtalk text -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68 -c \"docker test\"\n```\n\n### Jenkinsfile\n\n```shell\npipeline {\n    agent {\n        docker {\n            image 'catchzeng/dingtalk:latest'\n        }\n    }\n    environment {\n        DING_TOKEN = '1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f'\n        DING_SECRET = 'SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68'\n    }\n    stages {\n        stage('notify') {\n            steps {\n                sh 'dingtalk link -t ${DING_TOKEN} -s ${DING_SECRET} -i \"标题\" -e \"信息\" -u \"https://makeoptim.com/\" -p \"https://makeoptim.com/assets/img/logo.png\" -a'\n            }\n        }\n    }\n}\n```\n\n### Use as module\n\n```sh\ngo get github.com/CatchZeng/dingtalk\n```\n\n```go\npackage main\n\nimport (\n    \"log\"\n\n    \"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n)\n\nfunc main() {\n\taccessToken := \"1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f\"\n    secret := \"SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\"\n    client := dingtalk.NewClient(accessToken, secret)\n\n    msg := dingtalk.NewTextMessage().SetContent(\"测试文本&at 某个人\").SetAt([]string{\"177010xxx60\"}, false)\n    client.Send(msg)\n}\n```\n\n### Use as command line tool\n\n#### Demo\n\n```shell\ndingtalk text -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68 -c \"测试命令行 & at 某个人\" -m \"177010xxx60\",\"177010xxx61\"\n```\n\n```shell\n$ dingtalk markdown -D -i \"杭州天气\" -e '## 杭州天气 @150XXXXXXXX\n > 9度，西北风1级，空气良89，相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com)' -t 1c53e149ba5de6597cxxxxxx0e901fdxxxxxx80b8ac141e4a75afdc44c85ca4f -s SECb90923e19e58b466481e9e7b7a5bxxxxxx4531axxxxxxad3967fb29f0eae5c68\n\n{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"杭州天气\",\"text\":\"## 杭州天气 @150XXXXXXXX\\n \\u003e 9度，西北风1级，空气良89，相对温度73%\\n \\u003e ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\\n \\u003e ###### 10点20分发布 [天气](https://www.dingtalk.com)\"},\"at\":{\"atMobiles\":[],\"isAtAll\":false}}\n```\n\n> -D: print the message content\n\n#### Help\n\n```shell\ndingtalk is a command line tool for DingTalk\n\nUsage:\n  dingtalk [command]\n\nAvailable Commands:\n  actionCard  send actionCard message with DingTalk robot\n  feedCard    send feedCard message with DingTalk robot\n  help        Help about any command\n  link        send link message with DingTalk robot\n  markdown    send markdown message with DingTalk robot\n  text        send text message with DingTalk robot\n  version     dingtalk version\n\nFlags:\n  -t, --access_token string   access_token\n  -m, --atMobiles strings     atMobiles\n  -D, --debug                 debug\n  -h, --help                  help for dingtalk\n  -a, --isAtAll               isAtAll\n  -s, --secret string         secret\n\nUse \"dingtalk [command] --help\" for more information about a command.\n```\n\n## Stargazers\n\n[![Stargazers over time](https://starchart.cc/CatchZeng/dingtalk.svg)](https://starchart.cc/CatchZeng/dingtalk)\n"
  },
  {
    "path": "build/package/Dockerfile",
    "content": "FROM golang:1.18.1-alpine as builder\nRUN mkdir /build\nADD . /build/\nWORKDIR /build\nRUN go mod tidy && go build -o dingtalk main.go\n\n\nFROM alpine:3.7\n\nCOPY --from=builder /build/dingtalk /usr/local/bin/dingtalk\nRUN chmod +x /usr/local/bin/dingtalk\n"
  },
  {
    "path": "build/package/build.sh",
    "content": "#!/bin/bash\nshell_dir=$(dirname $0)\ncd ${shell_dir}\n\n# check params\nif [[ ! $1 ]]; then\n    echo \"image tag is null\"; exit 1;\nelse\n    echo \"image tag: $1\"\nfi\n\ncd ../../\n\ndocker build -t $1 -f build/package/Dockerfile .\n"
  },
  {
    "path": "cmd/dingtalk/actionCard.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar actionCardCmd = &cobra.Command{\n\tUse:   \"actionCard\",\n\tShort: \"send actionCard message with DingTalk robot\",\n\tLong:  `send actionCard message with DingTalk robot`,\n\tArgs:  cobra.MinimumNArgs(0),\n\tRun:   runActionCardCmd,\n}\n\nfunc runActionCardCmd(_ *cobra.Command, args []string) {\n\tif len(actionCardVars.Title) < 1 {\n\t\tlog.Fatal(\"title can not be empty\")\n\t\treturn\n\t}\n\n\tif len(actionCardVars.Text) < 1 {\n\t\tlog.Fatal(\"text can not be empty\")\n\t\treturn\n\t}\n\n\tvar isOverallJump = false\n\tif len(actionCardVars.SingleTitle) < 1 {\n\t\tif len(btnTitles) < 1 {\n\t\t\tlog.Fatal(\"btns can not be empty when singleTitle is empty\")\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tisOverallJump = true\n\t\tif len(actionCardVars.SingleURL) < 1 {\n\t\t\tlog.Fatal(\"singleURL can not be empty\")\n\t\t\treturn\n\t\t}\n\t}\n\n\tclient, err := newClient()\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t\treturn\n\t}\n\n\tmsg := dingtalk.NewActionCardMessage()\n\tif isOverallJump {\n\t\tmsg.SetOverallJump(\n\t\t\tactionCardVars.Title,\n\t\t\tactionCardVars.Text,\n\t\t\tactionCardVars.SingleTitle,\n\t\t\tactionCardVars.SingleURL,\n\t\t\tactionCardVars.BtnOrientation,\n\t\t\tactionCardVars.HideAvatar)\n\t} else {\n\t\tif len(btnTitles) != len(btnActionURLs) {\n\t\t\tlog.Fatal(\"btnTitles & btnActionURLs count must be equal\")\n\t\t\treturn\n\t\t}\n\n\t\tfor i := 0; i < len(btnTitles); i++ {\n\t\t\tactionCardVars.Btns = append(actionCardVars.Btns, dingtalk.Btn{\n\t\t\t\tTitle:     btnTitles[i],\n\t\t\t\tActionURL: btnActionURLs[i],\n\t\t\t})\n\t\t}\n\t\tmsg.SetIndependentJump(\n\t\t\tactionCardVars.Title,\n\t\t\tactionCardVars.Text,\n\t\t\tactionCardVars.Btns,\n\t\t\tactionCardVars.BtnOrientation,\n\t\t\tactionCardVars.HideAvatar)\n\t}\n\treq, _, err := client.Send(msg)\n\tif debug {\n\t\tlog.Print(req)\n\t}\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t}\n}\n\nvar actionCardVars dingtalk.ActionCard\nvar btnTitles, btnActionURLs []string\n\nfunc init() {\n\trootCmd.AddCommand(actionCardCmd)\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.Title, \"title\", \"i\", \"\", \"title\")\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.Text, \"text\", \"e\", \"\", \"text\")\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.SingleTitle, \"singleTitle\", \"n\", \"\", \"singleTitle\")\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.SingleURL, \"singleURL\", \"u\", \"\", \"singleURL\")\n\tactionCardCmd.Flags().StringSliceVarP(&btnTitles, \"btnTitles\", \"b\", []string{}, \"btnTitles\")\n\tactionCardCmd.Flags().StringSliceVarP(&btnActionURLs, \"btnActionURLs\", \"c\", []string{}, \"btnActionURLs\")\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.BtnOrientation, \"btnOrientation\", \"o\", \"\", \"btnOrientation\")\n\tactionCardCmd.Flags().StringVarP(&actionCardVars.HideAvatar, \"hideAvatar\", \"d\", \"\", \"hideAvatar\")\n}\n"
  },
  {
    "path": "cmd/dingtalk/actionCard_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runActionCardCmd(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tt.Run(\"title is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"title can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"text is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tactionCardVars.Title = \"123\"\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"text can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"singleTitle is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tactionCardVars.Title = \"123\"\n\t\tactionCardVars.Text = \"123\"\n\t\tactionCardVars.SingleTitle = \"\"\n\t\tbtnTitles = []string{}\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"btns can not be empty when singleTitle is empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"singleTitle is not empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tactionCardVars.Title = \"123\"\n\t\tactionCardVars.Text = \"123\"\n\t\tactionCardVars.SingleTitle = \"123\"\n\t\tactionCardVars.SingleURL = \"\"\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"singleURL can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"new client error\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tactionCardVars.Title = \"123\"\n\t\tactionCardVars.Text = \"123\"\n\t\tactionCardVars.SingleTitle = \"123\"\n\t\tactionCardVars.SingleURL = \"123\"\n\t\tmsg := \"new client error\"\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn nil, errors.New(msg)\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"btnTitles & btnActionURLs different count \", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tactionCardVars.Title = \"123\"\n\t\tactionCardVars.Text = \"123\"\n\t\tactionCardVars.SingleTitle = \"\"\n\t\tactionCardVars.SingleURL = \"\"\n\t\tbtnTitles = []string{\"1\"}\n\t\tbtnActionURLs = []string{\"1\", \"2\"}\n\t\tmsg := \"btnTitles & btnActionURLs count must be equal\"\n\n\t\tclient := &dingtalk.Client{}\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runActionCardCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"client send\", func(t *testing.T) {\n\t\tactionCardVars.Title = \"123\"\n\t\tactionCardVars.Text = \"123\"\n\t\tactionCardVars.SingleTitle = \"123\"\n\t\tactionCardVars.SingleURL = \"123\"\n\t\tclient := &dingtalk.Client{}\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunActionCardCmd(&cobra.Command{}, []string{})\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/feedCard.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar feedCardCmd = &cobra.Command{\n\tUse:   \"feedCard\",\n\tShort: \"send feedCard message with DingTalk robot\",\n\tLong:  `send feedCard message with DingTalk robot`,\n\tArgs:  cobra.MinimumNArgs(0),\n\tRun:   runFeedCardCmd,\n}\n\nfunc runFeedCardCmd(_ *cobra.Command, args []string) {\n\tif len(feedCardVars.titles) < 1 || len(feedCardVars.picURLs) < 1 || len(feedCardVars.messageURLs) < 1 {\n\t\tlog.Fatal(\"titles & picURLs & messageURLs can not be empty\")\n\t\treturn\n\t}\n\n\tif len(feedCardVars.titles) == len(feedCardVars.picURLs) && len(feedCardVars.picURLs) == len(feedCardVars.messageURLs) {\n\t\tclient, err := newClient()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tmsg := dingtalk.NewFeedCardMessage()\n\t\tfor i := 0; i < len(feedCardVars.titles); i++ {\n\t\t\tmsg.AppendLink(feedCardVars.titles[i], feedCardVars.messageURLs[i], feedCardVars.picURLs[i])\n\t\t}\n\t\treq, _, err := client.Send(msg)\n\t\tif debug {\n\t\t\tlog.Print(req)\n\t\t}\n\t\tif err != nil {\n\t\t\tlog.Fatal(err.Error())\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tlog.Fatal(\"titles & picURLs & messageURLs count must be equal\")\n\t}\n}\n\n// FeedCardVars struct\ntype FeedCardVars struct {\n\ttitles      []string\n\tpicURLs     []string\n\tmessageURLs []string\n}\n\nvar feedCardVars FeedCardVars\n\nfunc init() {\n\trootCmd.AddCommand(feedCardCmd)\n\n\tfeedCardCmd.Flags().StringSliceVarP(&feedCardVars.titles, \"titles\", \"i\", []string{}, \"titles\")\n\tfeedCardCmd.Flags().StringSliceVarP(&feedCardVars.picURLs, \"picURLs\", \"p\", []string{}, \"picURLs\")\n\tfeedCardCmd.Flags().StringSliceVarP(&feedCardVars.messageURLs, \"messageURLs\", \"u\", []string{}, \"messageURLs\")\n}\n"
  },
  {
    "path": "cmd/dingtalk/feedCard_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runFeedCardCmd(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tconst emptyMsg = \"titles & picURLs & messageURLs can not be empty\"\n\tconst differentCountMsg = \"titles & picURLs & messageURLs count must be equal\"\n\n\tt.Run(\"titles is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\trunFeedCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := emptyMsg\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runFeedCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"titles & picURLs different count\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tfeedCardVars.titles = []string{\"1\", \"2\"}\n\t\tfeedCardVars.picURLs = []string{\"1\"}\n\t\tfeedCardVars.messageURLs = []string{\"1\"}\n\n\t\trunFeedCardCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := differentCountMsg\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runFeedCardCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"new client error\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tfeedCardVars.titles = []string{\"1\"}\n\t\tfeedCardVars.picURLs = []string{\"1\"}\n\t\tfeedCardVars.messageURLs = []string{\"1\"}\n\t\tmsg := \"new client error\"\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn nil, errors.New(msg)\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunFeedCardCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runFeedCardCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"client send\", func(t *testing.T) {\n\t\tfeedCardVars.titles = []string{\"1\"}\n\t\tfeedCardVars.picURLs = []string{\"1\"}\n\t\tfeedCardVars.messageURLs = []string{\"1\"}\n\t\tclient := &dingtalk.Client{}\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunFeedCardCmd(&cobra.Command{}, []string{})\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/link.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar linkCmd = &cobra.Command{\n\tUse:   \"link\",\n\tShort: \"send link message with DingTalk robot\",\n\tLong:  `send link message with DingTalk robot`,\n\tArgs:  cobra.MinimumNArgs(0),\n\tRun:   runLinkCmd,\n}\n\nfunc runLinkCmd(_ *cobra.Command, args []string) {\n\tif len(linkVars.title) < 1 {\n\t\tlog.Fatal(\"title can not be empty\")\n\t\treturn\n\t}\n\n\tif len(linkVars.text) < 1 {\n\t\tlog.Fatal(\"text can not be empty\")\n\t\treturn\n\t}\n\n\tif len(linkVars.messageURL) < 1 {\n\t\tlog.Fatal(\"messageURL can not be empty\")\n\t\treturn\n\t}\n\n\tclient, err := newClient()\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t\treturn\n\t}\n\n\tmsg := dingtalk.NewLinkMessage().\n\t\tSetLink(linkVars.title, linkVars.text, linkVars.picURL, linkVars.messageURL)\n\treq, _, err := client.Send(msg)\n\tif debug {\n\t\tlog.Print(req)\n\t}\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t}\n}\n\n// LinkVars struct\ntype LinkVars struct {\n\ttitle      string\n\ttext       string\n\tpicURL     string\n\tmessageURL string\n}\n\nvar linkVars LinkVars\n\nfunc init() {\n\trootCmd.AddCommand(linkCmd)\n\tlinkCmd.Flags().StringVarP(&linkVars.title, \"title\", \"i\", \"\", \"title\")\n\tlinkCmd.Flags().StringVarP(&linkVars.text, \"text\", \"e\", \"\", \"text\")\n\tlinkCmd.Flags().StringVarP(&linkVars.picURL, \"picURL\", \"p\", \"\", \"picURL\")\n\tlinkCmd.Flags().StringVarP(&linkVars.messageURL, \"messageURL\", \"u\", \"\", \"messageURL\")\n}\n"
  },
  {
    "path": "cmd/dingtalk/link_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runLinkCmd(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tt.Run(\"title is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\trunLinkCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"title can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runLinkCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"text is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tlinkVars.title = \"123\"\n\n\t\trunLinkCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"text can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runLinkCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"messageURL is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tlinkVars.title = \"123\"\n\t\tlinkVars.text = \"123\"\n\n\t\trunLinkCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"messageURL can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runLinkCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"new client error\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tlinkVars.title = \"123\"\n\t\tlinkVars.text = \"123\"\n\t\tlinkVars.messageURL = \"123\"\n\t\tmsg := \"new client error\"\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn nil, errors.New(msg)\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunLinkCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runLinkCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"client send\", func(t *testing.T) {\n\t\tlinkVars.title = \"123\"\n\t\tlinkVars.text = \"123\"\n\t\tlinkVars.messageURL = \"123\"\n\t\tclient := &dingtalk.Client{}\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunLinkCmd(&cobra.Command{}, []string{})\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/markdown.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar markdownCmd = &cobra.Command{\n\tUse:   \"markdown\",\n\tShort: \"send markdown message with DingTalk robot\",\n\tLong:  `send markdown message with DingTalk robot`,\n\tArgs:  cobra.MinimumNArgs(0),\n\tRun:   runMarkdownCmd,\n}\n\nfunc runMarkdownCmd(_ *cobra.Command, args []string) {\n\tif len(markdownVars.title) < 1 {\n\t\tlog.Fatal(\"title can not be empty\")\n\t\treturn\n\t}\n\n\tif len(markdownVars.text) < 1 {\n\t\tlog.Fatal(\"text can not be empty\")\n\t\treturn\n\t}\n\n\tclient, err := newClient()\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t\treturn\n\t}\n\n\tmsg := dingtalk.NewMarkdownMessage().\n\t\tSetMarkdown(markdownVars.title, markdownVars.text).\n\t\tSetAt(atMobiles, isAtAll)\n\treq, _, err := client.Send(msg)\n\tif debug {\n\t\tlog.Print(req)\n\t}\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t}\n}\n\n// MarkdownVars struct\ntype MarkdownVars struct {\n\ttitle string\n\ttext  string\n}\n\nvar markdownVars MarkdownVars\n\nfunc init() {\n\trootCmd.AddCommand(markdownCmd)\n\tmarkdownCmd.Flags().StringVarP(&markdownVars.title, \"title\", \"i\", \"\", \"title\")\n\tmarkdownCmd.Flags().StringVarP(&markdownVars.text, \"text\", \"e\", \"\", \"text\")\n}\n"
  },
  {
    "path": "cmd/dingtalk/markdown_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runMarkdownCmd(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tt.Run(\"title is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\trunMarkdownCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"title can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runMarkdownCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"text is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tmarkdownVars.title = \"123\"\n\n\t\trunMarkdownCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"text can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runMarkdownCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"new client error\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\tmarkdownVars.title = \"123\"\n\t\tmarkdownVars.text = \"123\"\n\t\tmsg := \"new client error\"\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn nil, errors.New(msg)\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunMarkdownCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runMarkdownCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"client send\", func(t *testing.T) {\n\t\tmarkdownVars.title = \"123\"\n\t\tmarkdownVars.text = \"123\"\n\t\tclient := &dingtalk.Client{}\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunMarkdownCmd(&cobra.Command{}, []string{})\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/root.go",
    "content": "package dingtalk\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/CatchZeng/dingtalk/configs\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar rootCmd = &cobra.Command{\n\tUse:   \"dingtalk\",\n\tShort: \"dingtalk is a command line tool for DingTalk\",\n\tLong:  \"dingtalk is a command line tool for DingTalk\",\n}\n\n// Execute adds all child commands to the root command and sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute() {\n\tif err := rootCmd.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc newClient() (*dingtalk.Client, error) {\n\ttoken := getAccessToken()\n\tsecret := getSecret()\n\n\tif len(token) < 1 {\n\t\treturn nil, errors.New(\"access_token can not be empty\")\n\t}\n\tclient := dingtalk.NewClient(token, secret)\n\treturn client, nil\n}\n\nfunc getAccessToken() string {\n\tif len(accessToken) > 0 {\n\t\treturn accessToken\n\t}\n\n\tvalue, err := configs.GetConfig(configs.AccessToken)\n\tif err == nil {\n\t\treturn value\n\t}\n\treturn \"\"\n}\n\nfunc getSecret() string {\n\tif len(secret) > 0 {\n\t\treturn secret\n\t}\n\n\tvalue, err := configs.GetConfig(configs.Secret)\n\tif err == nil {\n\t\treturn value\n\t}\n\treturn \"\"\n}\n\nvar accessToken, secret string\nvar isAtAll bool\nvar atMobiles []string\nvar debug bool\n\nfunc init() {\n\tcobra.OnInitialize(configs.InitConfig)\n\n\trootCmd.PersistentFlags().StringVarP(&accessToken, configs.AccessToken, \"t\", \"\", configs.AccessToken)\n\trootCmd.PersistentFlags().StringVarP(&secret, configs.Secret, \"s\", \"\", configs.Secret)\n\trootCmd.PersistentFlags().BoolVarP(&isAtAll, \"isAtAll\", \"a\", false, \"isAtAll\")\n\trootCmd.PersistentFlags().StringSliceVarP(&atMobiles, \"atMobiles\", \"m\", []string{}, \"atMobiles\")\n\trootCmd.PersistentFlags().BoolVarP(&debug, \"debug\", \"D\", false, \"debug\")\n\n\tif err := viper.BindPFlag(configs.AccessToken, rootCmd.PersistentFlags().Lookup(configs.AccessToken)); err != nil {\n\t\tlog.Print(err)\n\t}\n\tif err := viper.BindPFlag(configs.Secret, rootCmd.PersistentFlags().Lookup(configs.Secret)); err != nil {\n\t\tlog.Print(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/dingtalk/root_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/configs\"\n)\n\nfunc Test_newClient(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tt.Run(\"getAccessToken return empty\", func(t *testing.T) {\n\t\taccessToken = \"\"\n\t\t_, err := newClient()\n\t\tif err == nil {\n\t\t\tt.Error(\"newClient() error\")\n\t\t}\n\t})\n\n\tt.Run(\"getAccessToken return token\", func(t *testing.T) {\n\t\taccessToken = \"123\"\n\t\tclient, err := newClient()\n\t\tif err != nil || client == nil {\n\t\t\tt.Error(\"newClient() error\")\n\t\t}\n\t})\n}\n\nfunc Test_getAccessToken(t *testing.T) {\n\tt.Run(\"get from accessToken\", func(t *testing.T) {\n\t\taccessToken = \"123\"\n\t\tgot := getAccessToken()\n\t\tif got != accessToken {\n\t\t\tt.Errorf(\"getAccessToken() = %v, want %v\", got, accessToken)\n\t\t}\n\t})\n\n\tt.Run(\"GetConfig error\", func(t *testing.T) {\n\t\taccessToken = \"\"\n\n\t\tmonkey.Patch(configs.GetConfig, func(key string) (string, error) {\n\t\t\treturn \"\", errors.New(\"GetConfig error\")\n\t\t})\n\t\tdefer monkey.Unpatch(configs.GetConfig)\n\n\t\tgot := getAccessToken()\n\t\tif got != \"\" {\n\t\t\tt.Errorf(\"getAccessToken() = %v, want %v\", got, \"\")\n\t\t}\n\t})\n\n\tt.Run(\"get from config\", func(t *testing.T) {\n\t\taccessToken = \"\"\n\n\t\twant := \"123\"\n\t\tmonkey.Patch(configs.GetConfig, func(key string) (string, error) {\n\t\t\treturn want, nil\n\t\t})\n\t\tdefer monkey.Unpatch(configs.GetConfig)\n\n\t\tgot := getAccessToken()\n\t\tif got != want {\n\t\t\tt.Errorf(\"getAccessToken() = %v, want %v\", got, want)\n\t\t}\n\t})\n}\n\nfunc Test_getSecret(t *testing.T) {\n\tt.Run(\"get from secret\", func(t *testing.T) {\n\t\tsecret = \"123\"\n\t\tgot := getSecret()\n\t\tif got != secret {\n\t\t\tt.Errorf(\"getSecret() = %v, want %v\", got, secret)\n\t\t}\n\t})\n\n\tt.Run(\"GetConfig error\", func(t *testing.T) {\n\t\tsecret = \"\"\n\n\t\tmonkey.Patch(configs.GetConfig, func(key string) (string, error) {\n\t\t\treturn \"\", errors.New(\"GetConfig error\")\n\t\t})\n\t\tdefer monkey.Unpatch(configs.GetConfig)\n\n\t\tgot := getSecret()\n\t\tif got != \"\" {\n\t\t\tt.Errorf(\"getSecret() = %v, want %v\", got, \"\")\n\t\t}\n\t})\n\n\tt.Run(\"get from config\", func(t *testing.T) {\n\t\tsecret = \"\"\n\n\t\twant := \"123\"\n\t\tmonkey.Patch(configs.GetConfig, func(key string) (string, error) {\n\t\t\treturn want, nil\n\t\t})\n\t\tdefer monkey.Unpatch(configs.GetConfig)\n\n\t\tgot := getSecret()\n\t\tif got != want {\n\t\t\tt.Errorf(\"getSecret() = %v, want %v\", got, want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/text.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar textCmd = &cobra.Command{\n\tUse:   \"text\",\n\tShort: \"send text message with DingTalk robot\",\n\tLong:  `send text message with DingTalk robot`,\n\tArgs:  cobra.MinimumNArgs(0),\n\tRun:   runTextCmd,\n}\n\nfunc runTextCmd(_ *cobra.Command, _ []string) {\n\tif len(textVars.content) < 1 {\n\t\tlog.Fatal(\"content can not be empty\")\n\t\treturn\n\t}\n\n\tclient, err := newClient()\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t\treturn\n\t}\n\n\tmsg := dingtalk.NewTextMessage().\n\t\tSetContent(textVars.content).\n\t\tSetAt(atMobiles, isAtAll)\n\treq, _, err := client.Send(msg)\n\tif debug {\n\t\tlog.Print(req)\n\t}\n\tif err != nil {\n\t\tlog.Fatal(err.Error())\n\t}\n}\n\n// TextVars struct\ntype TextVars struct {\n\tcontent string\n}\n\nvar textVars TextVars\n\nfunc init() {\n\trootCmd.AddCommand(textCmd)\n\ttextCmd.Flags().StringVarP(&textVars.content, \"content\", \"c\", \"\", \"content\")\n}\n"
  },
  {
    "path": "cmd/dingtalk/text_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/pkg/dingtalk\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runTextCmd(t *testing.T) {\n\tfakeExit := func(int) {\n\t\tlog.Print(\"fake exit\")\n\t}\n\tpatch := monkey.Patch(os.Exit, fakeExit)\n\tdefer patch.Unpatch()\n\n\tt.Run(\"content is empty\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\trunTextCmd(&cobra.Command{}, []string{})\n\t\tgot := buf.String()\n\n\t\twant := \"content can not be empty\"\n\n\t\tif !strings.Contains(got, want) {\n\t\t\tt.Errorf(\"runTextCmd() = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"new client error\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tlog.SetOutput(&buf)\n\t\tdefer func() {\n\t\t\tlog.SetOutput(os.Stderr)\n\t\t}()\n\n\t\ttextVars.content = \"123\"\n\t\tmsg := \"new client error\"\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn nil, errors.New(msg)\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunTextCmd(&cobra.Command{}, []string{})\n\n\t\tgot := buf.String()\n\n\t\tif !strings.Contains(got, msg) {\n\t\t\tt.Errorf(\"runTextCmd() = %v, want %v\", got, msg)\n\t\t}\n\t})\n\n\tt.Run(\"client send\", func(t *testing.T) {\n\t\ttextVars.content = \"123\"\n\t\tclient := &dingtalk.Client{}\n\n\t\tmonkey.Patch(newClient, func() (*dingtalk.Client, error) {\n\t\t\treturn client, nil\n\t\t})\n\t\tdefer monkey.Unpatch(newClient)\n\n\t\trunTextCmd(&cobra.Command{}, []string{})\n\t})\n}\n"
  },
  {
    "path": "cmd/dingtalk/version.go",
    "content": "package dingtalk\n\nimport (\n\t\"log\"\n\n\tv \"github.com/CatchZeng/gutils/version\"\n\t\"github.com/spf13/cobra\"\n)\n\nconst (\n\tversion   = \"1.5.0\"\n\tbuildTime = \"2022/04/20\"\n)\n\n// versionCmd represents the version command\nvar versionCmd = &cobra.Command{\n\tUse:   \"version\",\n\tShort: \"dingtalk version\",\n\tLong:  `dingtalk version`,\n\tRun:   runVersionCmd,\n}\n\nfunc runVersionCmd(_ *cobra.Command, _ []string) {\n\tv := v.Stringify(version, buildTime)\n\tlog.Println(v)\n}\n\nfunc init() {\n\trootCmd.AddCommand(versionCmd)\n}\n"
  },
  {
    "path": "cmd/dingtalk/version_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\tv \"github.com/CatchZeng/gutils/version\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nfunc Test_runVersionCmd(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlog.SetOutput(&buf)\n\tdefer func() {\n\t\tlog.SetOutput(os.Stderr)\n\t}()\n\trunVersionCmd(&cobra.Command{}, []string{})\n\tgot := buf.String()\n\n\twant := v.Stringify(version, buildTime)\n\n\tif !strings.Contains(got, want) {\n\t\tt.Errorf(\"runVersionCmd() = %v, want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "configs/app.go",
    "content": "package configs\n\nconst (\n\t// AccessToken access token key\n\tAccessToken string = \"access_token\"\n\t// Secret secret key\n\tSecret string = \"secret\"\n)\n"
  },
  {
    "path": "configs/configs.go",
    "content": "package configs\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/mitchellh/go-homedir\"\n\t\"github.com/spf13/viper\"\n)\n\n// InitConfig reads in configs file and ENV variables if set.\nfunc InitConfig() {\n\t// Find home directory.\n\thome, err := homedir.Dir()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Search configs in home directory with name \".dingtalk\" (without extension).\n\tconfigPath := path.Join(home, \".dingtalk\")\n\tviper.AddConfigPath(configPath)\n\tviper.SetConfigName(\"config\")\n\n\tenvPrefix := os.Getenv(\"DINGTALK_ENV_PREFIX\")\n\tviper.SetEnvPrefix(envPrefix)\n\tviper.AutomaticEnv() // read in environment variables that match\n\n\t// If a configs file is found, read it in.\n\tif err := viper.ReadInConfig(); err != nil {\n\t\treturn\n\t}\n\tlog.Println(\"using configs file:\", viper.ConfigFileUsed())\n}\n\n// GetConfig get configs with key\nfunc GetConfig(key string) (string, error) {\n\t// Check the environment variable\n\tenvPrefix := os.Getenv(\"DINGTALK_ENV_PREFIX\")\n\tenvKey := envPrefix + strings.ToUpper(key)\n\tresult := os.Getenv(envKey)\n\n\tif result != \"\" {\n\t\treturn result, nil\n\t}\n\n\t// If a configs file is found, read it in.\n\terr := viper.ReadInConfig()\n\tif err == nil {\n\t\treturn viper.GetString(key), nil\n\t}\n\treturn \"\", err\n}\n"
  },
  {
    "path": "configs/configs_test.go",
    "content": "package configs\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/mitchellh/go-homedir\"\n\t\"github.com/spf13/viper\"\n)\n\nfunc TestInitConfig(t *testing.T) {\n\tt.Run(\"homedir.Dir() return error\", func(t *testing.T) {\n\t\tmonkey.Patch(homedir.Dir, func() (string, error) {\n\t\t\treturn \"\", errors.New(\"homedir error\")\n\t\t})\n\t\tshouldPanic(t, InitConfig)\n\t})\n\n\tt.Run(\"viper.ReadInConfig() return error\", func(t *testing.T) {\n\t\tmonkey.Patch(homedir.Dir, func() (string, error) {\n\t\t\treturn \"/catchzeng\", nil\n\t\t})\n\n\t\tmonkey.Patch(viper.ReadInConfig, func() error {\n\t\t\treturn errors.New(\"ReadInConfig error\")\n\t\t})\n\n\t\tInitConfig()\n\t})\n\n\tt.Run(\"viper.ReadInConfig() return nil\", func(t *testing.T) {\n\t\tmonkey.Patch(homedir.Dir, func() (string, error) {\n\t\t\treturn \"/catchzeng\", nil\n\t\t})\n\n\t\tmonkey.Patch(viper.ReadInConfig, func() error {\n\t\t\treturn nil\n\t\t})\n\n\t\tInitConfig()\n\t})\n}\n\nfunc shouldPanic(t *testing.T, f func()) {\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Errorf(\"should have panicked\")\n\t\t}\n\t}()\n\tf()\n}\n\nfunc TestGetConfig(t *testing.T) {\n\tt.Run(\"viper.ReadInConfig() return error\", func(t *testing.T) {\n\t\tmonkey.Patch(viper.ReadInConfig, func() error {\n\t\t\treturn errors.New(\"ReadInConfig error\")\n\t\t})\n\n\t\tkey := \"123\"\n\t\tif _, err := GetConfig(key); err == nil {\n\t\t\tt.Error(\"GetConfig error\")\n\t\t}\n\t})\n\n\tt.Run(\"viper.ReadInConfig() return nil\", func(t *testing.T) {\n\t\tmonkey.Patch(viper.ReadInConfig, func() error {\n\t\t\treturn nil\n\t\t})\n\n\t\tmonkey.Patch(viper.GetString, func(key string) string {\n\t\t\treturn key\n\t\t})\n\n\t\tkey := \"456\"\n\t\tif value, err := GetConfig(key); err != nil || value != key {\n\t\t\tt.Error(\"GetConfig error\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/CatchZeng/dingtalk\n\ngo 1.18\n\nrequire (\n\tbou.ke/monkey v1.0.2\n\tgithub.com/CatchZeng/gutils v0.1.4\n\tgithub.com/golang/mock v1.4.4\n\tgithub.com/mitchellh/go-homedir v1.1.0\n\tgithub.com/spf13/cobra v1.4.0\n\tgithub.com/spf13/viper v1.11.0\n)\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.5.1 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/magiconair/properties v1.8.6 // indirect\n\tgithub.com/mitchellh/mapstructure v1.4.3 // indirect\n\tgithub.com/pelletier/go-toml v1.9.4 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect\n\tgithub.com/spf13/afero v1.8.2 // indirect\n\tgithub.com/spf13/cast v1.4.1 // indirect\n\tgithub.com/spf13/jwalterweatherman v1.1.0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/subosito/gotenv v1.2.0 // indirect\n\tgolang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgopkg.in/ini.v1 v1.66.4 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI=\nbou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA=\ncloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/CatchZeng/gutils v0.1.4 h1:Xj1oqB0Rg3F6rymxW1d+ajPl0wHtw2KvDlcquhnCNAc=\ngithub.com/CatchZeng/gutils v0.1.4/go.mod h1:Uz8tJTZDM9XWGTFQt3oIYs+zY3/2fOy0TVQaQTgPtWg=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=\ngithub.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=\ngithub.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0=\ngithub.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=\ngithub.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=\ngithub.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=\ngithub.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=\ngithub.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=\ngithub.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44=\ngithub.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=\ngopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "internal/security/security.go",
    "content": "package security\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// https://oapi.dingtalk.com/robot/send?access_token=xxx\nconst dingTalkOAPI = \"oapi.dingtalk.com\"\n\nvar dingTalkURL url.URL = url.URL{\n\tScheme: \"https\",\n\tHost:   dingTalkOAPI,\n\tPath:   \"robot/send\",\n}\n\n// URL get DingTalk URL with accessToken & secret\n// If no signature is set, the secret is set to \"\"\n// 如果没有加签，secret 设置为 \"\" 即可\nfunc URL(accessToken string, secret string) (string, error) {\n\ttimestamp := strconv.FormatInt(time.Now().Unix()*1000, 10)\n\treturn URLWithTimestamp(timestamp, accessToken, secret)\n}\n\n// URLWithTimestamp get DingTalk URL with timestamp & accessToken & secret\nfunc URLWithTimestamp(timestamp string, accessToken string, secret string) (string, error) {\n\tdtu := dingTalkURL\n\tvalue := url.Values{}\n\tvalue.Set(\"access_token\", accessToken)\n\n\tif secret == \"\" {\n\t\tdtu.RawQuery = value.Encode()\n\t\treturn dtu.String(), nil\n\t}\n\n\tsign, err := sign(timestamp, secret)\n\tif err != nil {\n\t\tdtu.RawQuery = value.Encode()\n\t\treturn dtu.String(), err\n\t}\n\n\tvalue.Set(\"timestamp\", timestamp)\n\tvalue.Set(\"sign\", sign)\n\tdtu.RawQuery = value.Encode()\n\treturn dtu.String(), nil\n}\n\n// Validate validate\n// https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p\nfunc Validate(signStr, timestamp, secret string) (bool, error) {\n\tt, err := strconv.ParseInt(timestamp, 10, 64)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\ttimeGap := time.Since(time.Unix(t, 0))\n\tif math.Abs(timeGap.Hours()) > 1 {\n\t\treturn false, fmt.Errorf(\"specified timestamp is expired\")\n\t}\n\n\tourSign, err := sign(timestamp, secret)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn ourSign == signStr, nil\n}\n\nfunc sign(timestamp string, secret string) (string, error) {\n\tstringToSign := fmt.Sprintf(\"%s\\n%s\", timestamp, secret)\n\th := hmac.New(sha256.New, []byte(secret))\n\tif _, err := io.WriteString(h, stringToSign); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(h.Sum(nil)), nil\n}\n"
  },
  {
    "path": "internal/security/security_test.go",
    "content": "package security\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"bou.ke/monkey\"\n)\n\nconst (\n\ttimestamp   = \"1582163555000\"\n\taccessToken = \"1c53e149ba5de6597ca2442f0e901fd86156780b8ac141e4a75afdc44c85ca4f\"\n\tsecret      = \"SECb90923e19e58b466481e9e7b7a5b4f108a4531abde590ad3967fb29f0eae5c68\"\n\tsigned      = \"BQKsG%2BQOCl%2BbYJOLc6pxDHxjVquzlZPWgvRzeN2J5zY%3D\"\n)\n\nfunc TestURL(t *testing.T) {\n\tmonkey.Patch(strconv.FormatInt, func(i int64, base int) string {\n\t\treturn timestamp\n\t})\n\n\tdefer monkey.Unpatch(strconv.FormatInt)\n\n\tgot, err := URL(accessToken, secret)\n\tif err != nil {\n\t\tt.Errorf(\"URL() error = %v\", err)\n\t}\n\n\twant := fmt.Sprintf(\"https://oapi.dingtalk.com/robot/send?access_token=%v&sign=%v&timestamp=%v\", accessToken, signed, timestamp)\n\tif got != want {\n\t\tt.Errorf(\"URL() = %v, want %v\", got, want)\n\t}\n}\n\nfunc TestURLWithTimestamp(t *testing.T) {\n\ttype args struct {\n\t\taccessToken string\n\t\tsecret      string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"without sign\",\n\t\t\targs: args{\n\t\t\t\taccessToken: accessToken,\n\t\t\t},\n\t\t\twant:    fmt.Sprintf(\"https://oapi.dingtalk.com/robot/send?access_token=%v\", accessToken),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"with sign\",\n\t\t\targs: args{\n\t\t\t\taccessToken: accessToken,\n\t\t\t\tsecret:      secret,\n\t\t\t},\n\t\t\twant:    fmt.Sprintf(\"https://oapi.dingtalk.com/robot/send?access_token=%v&sign=%v&timestamp=%v\", accessToken, signed, timestamp),\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := URLWithTimestamp(timestamp, tt.args.accessToken, tt.args.secret)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"URLWithTimestamp() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"URLWithTimestamp() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestValidate(t *testing.T) {\n\tvalidateTimestamp := strconv.FormatInt(time.Now().Add(60*time.Second).Unix(), 10)\n\n\tresult, err := sign(validateTimestamp, secret)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\t_, err = Validate(result, strconv.FormatInt(time.Now().Add(-3601*time.Second).Unix(), 10), secret)\n\tif err == nil {\n\t\tt.Error(\"this should be err, but not\")\n\t}\n\n\t_, err = Validate(result, strconv.FormatInt(time.Now().Add(3601*time.Second).Unix(), 10), secret)\n\tif err == nil {\n\t\tt.Error(\"this should be err, but not\")\n\t}\n\n\tb, err := Validate(result, validateTimestamp, secret)\n\tif err != nil {\n\t\tt.Error(err)\n\t} else {\n\t\tif !b {\n\t\t\tt.Error(\"token is not the same\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/CatchZeng/dingtalk/cmd/dingtalk\"\n)\n\nfunc main() {\n\tlog.SetOutput(os.Stdout)\n\tlog.SetFlags(0)\n\tdingtalk.Execute()\n}\n"
  },
  {
    "path": "pkg/dingtalk/actionCard.go",
    "content": "package dingtalk\n\nimport \"encoding/json\"\n\n// ActionCardMessage struct\ntype ActionCardMessage struct {\n\tMsgType    MsgType    `json:\"msgtype\"`\n\tActionCard ActionCard `json:\"actionCard\"`\n}\n\n// ActionCard actionCard struct\ntype ActionCard struct {\n\tTitle          string `json:\"title\"`\n\tText           string `json:\"text\"`\n\tSingleTitle    string `json:\"singleTitle\"`\n\tSingleURL      string `json:\"singleURL\"`\n\tBtns           []Btn  `json:\"btns\"`\n\tBtnOrientation string `json:\"btnOrientation\"`\n\tHideAvatar     string `json:\"hideAvatar\"`\n}\n\n// Btn struct\ntype Btn struct {\n\tTitle     string `json:\"title\"`\n\tActionURL string `json:\"actionURL\"`\n}\n\n// ToByte to byte\nfunc (m *ActionCardMessage) ToByte() ([]byte, error) {\n\tm.MsgType = MsgTypeActionCard\n\tjsonByte, err := json.Marshal(m)\n\treturn jsonByte, err\n}\n\n// NewActionCardMessage new message\nfunc NewActionCardMessage() *ActionCardMessage {\n\tmsg := ActionCardMessage{}\n\treturn &msg\n}\n\n// SetOverallJump set overall jump actionCard\nfunc (m *ActionCardMessage) SetOverallJump(\n\ttitle string,\n\ttext string,\n\tsingleTitle string,\n\tsingleURL string,\n\tbtnOrientation string,\n\thideAvatar string) *ActionCardMessage {\n\tm.ActionCard = ActionCard{\n\t\tTitle:          title,\n\t\tText:           text,\n\t\tSingleTitle:    singleTitle,\n\t\tSingleURL:      singleURL,\n\t\tBtnOrientation: btnOrientation,\n\t\tHideAvatar:     hideAvatar,\n\t}\n\treturn m\n}\n\n// SetIndependentJump set independent jump actionCard\nfunc (m *ActionCardMessage) SetIndependentJump(\n\ttitle string,\n\ttext string,\n\tbtns []Btn,\n\tbtnOrientation string,\n\thideAvatar string) *ActionCardMessage {\n\tm.ActionCard = ActionCard{\n\t\tTitle:          title,\n\t\tText:           text,\n\t\tBtns:           btns,\n\t\tBtnOrientation: btnOrientation,\n\t\tHideAvatar:     hideAvatar,\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "pkg/dingtalk/actionCard_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestActionCardMessage_ToByte(t *testing.T) {\n\tmsg := NewActionCardMessage()\n\t_, _ = msg.ToByte()\n\tif msg.MsgType != MsgTypeActionCard {\n\t\tt.Errorf(\"ActionCardMessage.ToByte() type error\")\n\t}\n}\n\nfunc TestNewActionCardMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant *ActionCardMessage\n\t}{\n\t\t{\n\t\t\tname: \"Should return a ActionCardMessage instance\",\n\t\t\twant: &ActionCardMessage{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewActionCardMessage(); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewActionCardMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestActionCardMessage_SetOverallJump(t *testing.T) {\n\tgot := NewActionCardMessage()\n\tgot.SetOverallJump(\"title\", \"text\", \"singleTitle\", \"singleURL\", \"btnOrientation\", \"hideAvatar\")\n\n\tcard := ActionCard{\n\t\tTitle:          \"title\",\n\t\tText:           \"text\",\n\t\tSingleTitle:    \"singleTitle\",\n\t\tSingleURL:      \"singleURL\",\n\t\tBtnOrientation: \"btnOrientation\",\n\t\tHideAvatar:     \"hideAvatar\",\n\t}\n\twant := NewActionCardMessage()\n\twant.ActionCard = card\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetOverallJump() = %v, want %v\", got, want)\n\t}\n}\n\nfunc TestActionCardMessage_SetIndependentJump(t *testing.T) {\n\tgot := NewActionCardMessage()\n\tgot.SetIndependentJump(\"title\", \"text\", []Btn{{\n\t\tTitle:     \"title\",\n\t\tActionURL: \"actionURL\",\n\t}}, \"btnOrientation\", \"hideAvatar\")\n\n\tcard := ActionCard{\n\t\tTitle: \"title\",\n\t\tText:  \"text\",\n\t\tBtns: []Btn{{\n\t\t\tTitle:     \"title\",\n\t\t\tActionURL: \"actionURL\",\n\t\t}},\n\t\tBtnOrientation: \"btnOrientation\",\n\t\tHideAvatar:     \"hideAvatar\",\n\t}\n\twant := NewActionCardMessage()\n\twant.ActionCard = card\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetIndependentJump() = %v, want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pkg/dingtalk/client.go",
    "content": "package dingtalk\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/CatchZeng/dingtalk/internal/security\"\n)\n\n// Client dingtalk client\ntype Client struct {\n\tAccessToken string\n\tSecret      string\n}\n\n// NewClient new dingtalk client\nfunc NewClient(accessToken, secret string) *Client {\n\treturn &Client{\n\t\tAccessToken: accessToken,\n\t\tSecret:      secret,\n\t}\n}\n\n// Response response struct\ntype Response struct {\n\tErrMsg  string `json:\"errmsg\"`\n\tErrCode int64  `json:\"errcode\"`\n}\n\nconst httpTimoutSecond = time.Duration(30) * time.Second\n\n// Send message\nfunc (d *Client) Send(message Message) (string, *Response, error) {\n\tres := &Response{}\n\n\treqBytes, err := message.ToByte()\n\tif err != nil {\n\t\treturn \"\", res, err\n\t}\n\treqString := string(reqBytes)\n\n\tpushURL, err := security.URL(d.AccessToken, d.Secret)\n\tif err != nil {\n\t\treturn reqString, res, err\n\t}\n\n\treq, err := http.NewRequest(http.MethodPost, pushURL, bytes.NewReader(reqBytes))\n\tif err != nil {\n\t\treturn reqString, res, err\n\t}\n\treq.Header.Add(\"Accept-Charset\", \"utf8\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tclient := new(http.Client)\n\tclient.Timeout = httpTimoutSecond\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn reqString, res, err\n\t}\n\tdefer resp.Body.Close()\n\n\tresultByte, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn reqString, res, err\n\t}\n\n\terr = json.Unmarshal(resultByte, &res)\n\tif err != nil {\n\t\treturn reqString, res, fmt.Errorf(\"unmarshal http response body from json error = %v\", err)\n\t}\n\n\tif res.ErrCode != 0 {\n\t\treturn reqString, res, fmt.Errorf(\"send message to dingtalk error = %s\", res.ErrMsg)\n\t}\n\n\treturn reqString, res, nil\n}\n"
  },
  {
    "path": "pkg/dingtalk/client_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"bou.ke/monkey\"\n\t\"github.com/CatchZeng/dingtalk/internal/security\"\n\n\tmock_message \"github.com/CatchZeng/dingtalk/test/mocks/message\"\n\t\"github.com/golang/mock/gomock\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\ttype args struct {\n\t\taccessToken string\n\t\tsecret      string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *Client\n\t}{\n\t\t{\n\t\t\tname: \"\",\n\t\t\targs: args{\n\t\t\t\taccessToken: \"123456\",\n\t\t\t\tsecret:      \"111111\",\n\t\t\t},\n\t\t\twant: &Client{\n\t\t\t\tAccessToken: \"123456\",\n\t\t\t\tSecret:      \"111111\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewClient(tt.args.accessToken, tt.args.secret); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewClient() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClient_Send(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmessage := mock_message.NewMockMessage(ctrl)\n\n\tt.Run(\"message return error\", func(t *testing.T) {\n\t\tc := &Client{}\n\n\t\tmessage.EXPECT().ToByte().Return([]byte{}, errors.New(\"test\"))\n\n\t\tif _, _, err := c.Send(message); err == nil {\n\t\t\tt.Error(\"send error\")\n\t\t}\n\t})\n\n\tt.Run(\"security.URL return error\", func(t *testing.T) {\n\t\tc := &Client{\n\t\t\tAccessToken: \"test-access-token\",\n\t\t\tSecret:      \"test-secret\",\n\t\t}\n\n\t\tmessage.EXPECT().ToByte().Return([]byte{}, nil)\n\t\tmonkey.Patch(security.URL, func(accessToken string, secret string) (string, error) {\n\t\t\treturn \"\", errors.New(\"URL error\")\n\t\t})\n\n\t\tif _, _, err := c.Send(message); err == nil {\n\t\t\tt.Error(\"send error\")\n\t\t}\n\t})\n\n\tt.Run(\"http.NewRequest return error\", func(t *testing.T) {\n\t\tc := &Client{\n\t\t\tAccessToken: \"test-access-token\",\n\t\t\tSecret:      \"test-secret\",\n\t\t}\n\n\t\tmessage.EXPECT().ToByte().Return([]byte{}, nil)\n\t\tmonkey.Patch(security.URL, func(accessToken string, secret string) (string, error) {\n\t\t\treturn \"https://oapi.dingtalk.com/robot/send?access_token=ewfewfwfwefwafew\", nil\n\t\t})\n\n\t\tmonkey.Patch(http.NewRequest, func(method, url string, body io.Reader) (*http.Request, error) {\n\t\t\treturn nil, errors.New(\"NewRequest error\")\n\t\t})\n\n\t\tif _, _, err := c.Send(message); err == nil {\n\t\t\tt.Error(\"send error\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pkg/dingtalk/feedCard.go",
    "content": "package dingtalk\n\nimport \"encoding/json\"\n\n// FeedCardMessage feed message struct\ntype FeedCardMessage struct {\n\tMsgType  MsgType  `json:\"msgtype\"`\n\tFeedCard FeedCard `json:\"feedCard\"`\n}\n\n// FeedCard feedCard struct\ntype FeedCard struct {\n\tLinks []FeedCardLink `json:\"links\"`\n}\n\n// FeedCardLink struct\ntype FeedCardLink struct {\n\tTitle      string `json:\"title\"`\n\tPicURL     string `json:\"picURL\"`\n\tMessageURL string `json:\"messageURL\"`\n}\n\n// ToByte to byte\nfunc (m *FeedCardMessage) ToByte() ([]byte, error) {\n\tm.MsgType = MsgTypeFeedCard\n\tjsonByte, err := json.Marshal(m)\n\treturn jsonByte, err\n}\n\n// NewFeedCardMessage new message\nfunc NewFeedCardMessage() *FeedCardMessage {\n\tmsg := FeedCardMessage{}\n\treturn &msg\n}\n\n// AppendLink append link\nfunc (m *FeedCardMessage) AppendLink(\n\ttitle string,\n\tmessageURL string,\n\tpicURL string) *FeedCardMessage {\n\tvar link = FeedCardLink{\n\t\tTitle:      title,\n\t\tMessageURL: messageURL,\n\t\tPicURL:     picURL,\n\t}\n\tm.FeedCard.Links = append(m.FeedCard.Links, link)\n\treturn m\n}\n"
  },
  {
    "path": "pkg/dingtalk/feedCard_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestFeedCardMessage_ToByte(t *testing.T) {\n\tmsg := NewFeedCardMessage()\n\t_, _ = msg.ToByte()\n\tif msg.MsgType != MsgTypeFeedCard {\n\t\tt.Errorf(\"FeedCardMessage.ToByte() type error\")\n\t}\n}\n\nfunc TestNewFeedCardMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant *FeedCardMessage\n\t}{\n\t\t{\n\t\t\tname: \"Should return a FeedCardMessage instance\",\n\t\t\twant: &FeedCardMessage{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewFeedCardMessage(); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewFeedCardMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFeedCardMessage_AppendLink(t *testing.T) {\n\tmsg := NewFeedCardMessage()\n\tmsg.AppendLink(\"title\", \"messageURL\", \"picURL\")\n\tif len(msg.FeedCard.Links) != 1 {\n\t\tt.Errorf(\"The number of links after AppendLink should be 1\")\n\t}\n\n\tmsg.AppendLink(\"title2\", \"messageURL2\", \"picURL2\")\n\tif len(msg.FeedCard.Links) != 2 {\n\t\tt.Errorf(\"The number of links after AppendLink should be 2\")\n\t}\n}\n"
  },
  {
    "path": "pkg/dingtalk/link.go",
    "content": "package dingtalk\n\nimport \"encoding/json\"\n\n// LinkMessage link message struct\ntype LinkMessage struct {\n\tMsgType MsgType `json:\"msgtype\"`\n\tLink    Link    `json:\"link\"`\n}\n\n// Link link struct\ntype Link struct {\n\tTitle      string `json:\"title\"`\n\tText       string `json:\"text\"`\n\tPicURL     string `json:\"picUrl\"`\n\tMessageURL string `json:\"messageUrl\"`\n}\n\n// ToByte to byte\nfunc (m *LinkMessage) ToByte() ([]byte, error) {\n\tm.MsgType = MsgTypeLink\n\tjsonByte, err := json.Marshal(m)\n\treturn jsonByte, err\n}\n\n// NewLinkMessage new message\nfunc NewLinkMessage() *LinkMessage {\n\tmsg := LinkMessage{}\n\treturn &msg\n}\n\n// SetLink set link\nfunc (m *LinkMessage) SetLink(\n\ttitle string,\n\ttext string,\n\tpicURL string,\n\tmessageURL string) *LinkMessage {\n\tm.Link = Link{\n\t\tTitle:      title,\n\t\tText:       text,\n\t\tPicURL:     picURL,\n\t\tMessageURL: messageURL,\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "pkg/dingtalk/link_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestLinkMessage_ToByte(t *testing.T) {\n\tmsg := NewLinkMessage()\n\t_, _ = msg.ToByte()\n\tif msg.MsgType != MsgTypeLink {\n\t\tt.Errorf(\"LinkMessage.ToByte() type error\")\n\t}\n}\n\nfunc TestNewLinkMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant *LinkMessage\n\t}{\n\t\t{\n\t\t\tname: \"Should return a LinkMessage instance\",\n\t\t\twant: &LinkMessage{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewLinkMessage(); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewLinkMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLinkMessage_SetLink(t *testing.T) {\n\tgot := NewLinkMessage()\n\tgot.SetLink(\"title\", \"text\", \"picURL\", \"messageURL\")\n\n\twant := NewLinkMessage()\n\twant.Link = Link{\n\t\tTitle:      \"title\",\n\t\tText:       \"text\",\n\t\tPicURL:     \"picURL\",\n\t\tMessageURL: \"messageURL\",\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetLink() = %v, want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pkg/dingtalk/markdown.go",
    "content": "package dingtalk\n\nimport \"encoding/json\"\n\n// MarkdownMessage markdown message struct\ntype MarkdownMessage struct {\n\tMsgType  MsgType  `json:\"msgtype\"`\n\tMarkdown Markdown `json:\"markdown\"`\n\tAt       At       `json:\"at\"`\n}\n\n// Markdown markdown struct\ntype Markdown struct {\n\tTitle string `json:\"title\"`\n\tText  string `json:\"text\"`\n}\n\n// ToByte to byte\nfunc (m *MarkdownMessage) ToByte() ([]byte, error) {\n\tm.MsgType = MsgTypeMarkdown\n\tjsonByte, err := json.Marshal(m)\n\treturn jsonByte, err\n}\n\n// NewMarkdownMessage new message\nfunc NewMarkdownMessage() *MarkdownMessage {\n\tmsg := MarkdownMessage{}\n\treturn &msg\n}\n\n// SetMarkdown set markdown\nfunc (m *MarkdownMessage) SetMarkdown(title string, text string) *MarkdownMessage {\n\tm.Markdown = Markdown{\n\t\tTitle: title,\n\t\tText:  text,\n\t}\n\treturn m\n}\n\n// SetAt set at\nfunc (m *MarkdownMessage) SetAt(atMobiles []string, isAtAll bool) *MarkdownMessage {\n\tm.At = At{\n\t\tAtMobiles: atMobiles,\n\t\tIsAtAll:   isAtAll,\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "pkg/dingtalk/markdown_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestMarkdownMessage_ToByte(t *testing.T) {\n\tmsg := NewMarkdownMessage()\n\t_, _ = msg.ToByte()\n\tif msg.MsgType != MsgTypeMarkdown {\n\t\tt.Errorf(\"MarkdownMessage.ToByte() type error\")\n\t}\n}\n\nfunc TestNewMarkdownMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant *MarkdownMessage\n\t}{\n\t\t{\n\t\t\tname: \"Should return a MarkdownMessage instance\",\n\t\t\twant: &MarkdownMessage{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewMarkdownMessage(); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewMarkdownMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarkdownMessage_SetMarkdown(t *testing.T) {\n\tgot := NewMarkdownMessage()\n\tgot.SetMarkdown(\"title\", \"text\")\n\n\twant := NewMarkdownMessage()\n\twant.Markdown = Markdown{\n\t\tTitle: \"title\",\n\t\tText:  \"text\",\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetMarkdown() = %v, want %v\", got, want)\n\t}\n}\n\nfunc TestMarkdownMessage_SetAt(t *testing.T) {\n\tgot := NewMarkdownMessage()\n\tgot.SetAt([]string{\"atMobiles\"}, false)\n\n\twant := NewMarkdownMessage()\n\twant.At = At{\n\t\tAtMobiles: []string{\"atMobiles\"},\n\t\tIsAtAll:   false,\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetMarkdown() = %v, want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pkg/dingtalk/message.go",
    "content": "package dingtalk\n\n// MsgType message type enum\ntype MsgType string\n\nconst (\n\t// MsgTypeText text\n\tMsgTypeText MsgType = \"text\"\n\t// MsgTypeMarkdown markdown\n\tMsgTypeMarkdown MsgType = \"markdown\"\n\t// MsgTypeLink link\n\tMsgTypeLink MsgType = \"link\"\n\t// MsgTypeActionCard actionCard\n\tMsgTypeActionCard MsgType = \"actionCard\"\n\t// MsgTypeFeedCard feedCard\n\tMsgTypeFeedCard MsgType = \"feedCard\"\n)\n\n// Message interface\ntype Message interface {\n\tToByte() ([]byte, error)\n}\n\n// At at struct\ntype At struct {\n\tAtMobiles []string `json:\"atMobiles\"`\n\tIsAtAll   bool     `json:\"isAtAll\"`\n}\n"
  },
  {
    "path": "pkg/dingtalk/text.go",
    "content": "package dingtalk\n\nimport \"encoding/json\"\n\n// TextMessage text message struct\ntype TextMessage struct {\n\tMsgType MsgType `json:\"msgtype\"`\n\tText    Text    `json:\"text\"`\n\tAt      At      `json:\"at\"`\n}\n\n// Text text struct\ntype Text struct {\n\tContent string `json:\"content\"`\n}\n\n// ToByte to byte\nfunc (m *TextMessage) ToByte() ([]byte, error) {\n\tm.MsgType = MsgTypeText\n\tjsonByte, err := json.Marshal(m)\n\treturn jsonByte, err\n}\n\n// NewTextMessage new message\nfunc NewTextMessage() *TextMessage {\n\tmsg := TextMessage{}\n\treturn &msg\n}\n\n// SetContent set content\nfunc (m *TextMessage) SetContent(content string) *TextMessage {\n\tm.Text = Text{\n\t\tContent: content,\n\t}\n\treturn m\n}\n\n// SetAt set at\nfunc (m *TextMessage) SetAt(atMobiles []string, isAtAll bool) *TextMessage {\n\tm.At = At{\n\t\tAtMobiles: atMobiles,\n\t\tIsAtAll:   isAtAll,\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "pkg/dingtalk/text_test.go",
    "content": "package dingtalk\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestTextMessage_ToByte(t *testing.T) {\n\tmsg := NewTextMessage()\n\t_, _ = msg.ToByte()\n\tif msg.MsgType != MsgTypeText {\n\t\tt.Errorf(\"TextMessage.ToByte() type error\")\n\t}\n}\n\nfunc TestNewTextMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant *TextMessage\n\t}{\n\t\t{\n\t\t\tname: \"Should return a TextMessage instance\",\n\t\t\twant: &TextMessage{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewTextMessage(); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewTextMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTextMessage_SetContent(t *testing.T) {\n\tgot := NewTextMessage()\n\tgot.SetContent(\"content\")\n\n\twant := NewTextMessage()\n\twant.Text = Text{\n\t\tContent: \"content\",\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetContent() = %v, want %v\", got, want)\n\t}\n}\n\nfunc TestTextMessage_SetAt(t *testing.T) {\n\tgot := NewTextMessage()\n\tgot.SetAt([]string{\"atMobiles\"}, false)\n\n\twant := NewTextMessage()\n\twant.At = At{\n\t\tAtMobiles: []string{\"atMobiles\"},\n\t\tIsAtAll:   false,\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"SetMarkdown() = %v, want %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "scripts/mock.sh",
    "content": "#!/bin/bash\nshell_dir=$(dirname $0)\ncd ${shell_dir}\n\ncd ..\n\nrm -rf test/mocks\nmkdir -p test/mocks/message\n\n# open with vscode\nif which mockgen >/dev/null; then\n  echo \"mockgen has installed in PATH\"\nelse\n  echo \"warning: 'mockgen' command has not installed in PATH\"\n  GO111MODULE=on go get github.com/golang/mock/mockgen@v1.4.3\nfi\n\nmockgen -package=mock_message -source=message.go > test/mocks/message/message.go"
  },
  {
    "path": "scripts/test.sh",
    "content": "#!/bin/bash\nshell_dir=$(dirname $0)\ncd ${shell_dir}\n\ncd ..\n\nset -e\necho \"\" > coverage.txt\n\nfor d in $(go list ./... | grep -v vendor); do\n    go test -gcflags=-l -race -coverprofile=profile.out -covermode=atomic $d\n    if [ -f profile.out ]; then\n        cat profile.out >> coverage.txt\n        rm profile.out\n    fi\ndone\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "# must be unique in a given SonarQube instance\nsonar.projectKey=dingtalk\n\n# --- optional properties ---\n\n# defaults to project key\n#sonar.projectName=My project\n# defaults to 'not provided'\nsonar.projectVersion=1.0\n\n# Path is relative to the sonar-project.properties file. Defaults to .\nsonar.sources=.\n\n# Encoding of the source code. Default is default system encoding\nsonar.sourceEncoding=UTF-8\n\nsonar.exclusions=**/proto/**\nsonar.language=go\nsonar.tests=.\nsonar.test.inclusions=**/*_test.go\nsonar.test.exclusions=**/vendor/**,**/proto/**\nsonar.go.coverage.reportPaths=coverage.data\nDsonar.coverage.dtdVerification=false\n"
  },
  {
    "path": "test/mocks/message/message.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: message.go\n\n// Package mock_message is a generated GoMock package.\npackage mock_message\n\nimport (\n\treflect \"reflect\"\n\n\tgomock \"github.com/golang/mock/gomock\"\n)\n\n// MockMessage is a mock of Message interface\ntype MockMessage struct {\n\tctrl     *gomock.Controller\n\trecorder *MockMessageMockRecorder\n}\n\n// MockMessageMockRecorder is the mock recorder for MockMessage\ntype MockMessageMockRecorder struct {\n\tmock *MockMessage\n}\n\n// NewMockMessage creates a new mock instance\nfunc NewMockMessage(ctrl *gomock.Controller) *MockMessage {\n\tmock := &MockMessage{ctrl: ctrl}\n\tmock.recorder = &MockMessageMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use\nfunc (m *MockMessage) EXPECT() *MockMessageMockRecorder {\n\treturn m.recorder\n}\n\n// ToByte mocks base method\nfunc (m *MockMessage) ToByte() ([]byte, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ToByte\")\n\tret0, _ := ret[0].([]byte)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// ToByte indicates an expected call of ToByte\nfunc (mr *MockMessageMockRecorder) ToByte() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ToByte\", reflect.TypeOf((*MockMessage)(nil).ToByte))\n}\n"
  }
]