├── .hadolint.yaml ├── tests ├── vars.json ├── audio.mp3 ├── github.png ├── video.mp4 ├── voice.ogg ├── github-logo.png ├── gophercolor.png ├── message.txt ├── message_template.txt └── message_html.txt ├── images ├── logo.png └── logo.svg ├── .github ├── dependabot.yml ├── FUNDING.yml └── workflows │ ├── goreleaser.yml │ ├── lint.yml │ ├── codeql.yml │ └── docker.yml ├── .gitignore ├── docker └── Dockerfile ├── .golangci.yml ├── LICENSE ├── go.mod ├── .goreleaser.yaml ├── README.md ├── Makefile ├── go.sum ├── DOCS.md ├── main.go ├── plugin.go └── plugin_test.go /.hadolint.yaml: -------------------------------------------------------------------------------- 1 | ignored: 2 | - DL3018 3 | - DL3008 4 | -------------------------------------------------------------------------------- /tests/vars.json: -------------------------------------------------------------------------------- 1 | {"env":"testing","version":"1.2.0-SNAPSHOT"} 2 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/images/logo.png -------------------------------------------------------------------------------- /tests/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/audio.mp3 -------------------------------------------------------------------------------- /tests/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/github.png -------------------------------------------------------------------------------- /tests/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/video.mp4 -------------------------------------------------------------------------------- /tests/voice.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/voice.ogg -------------------------------------------------------------------------------- /tests/github-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/github-logo.png -------------------------------------------------------------------------------- /tests/gophercolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-telegram/HEAD/tests/gophercolor.png -------------------------------------------------------------------------------- /tests/message.txt: -------------------------------------------------------------------------------- 1 | Sample message loaded from file. 2 | 3 | Commit msg: {{commit.message}} 4 | 5 | duration: {{duration build.started build.finished}} 6 | -------------------------------------------------------------------------------- /tests/message_template.txt: -------------------------------------------------------------------------------- 1 | Sample message template loaded from file. 2 | 3 | *Environ:* {{tpl.env}} 4 | *Version:* {{tpl.version}} 5 | 6 | Commit msg: {{commit.message}} 7 | 8 | duration: {{duration build.started build.finished}} 9 | -------------------------------------------------------------------------------- /tests/message_html.txt: -------------------------------------------------------------------------------- 1 | Test HTML Format from file 2 | Google .com 1 3 | Google .com 2 4 | Google .com 3 5 | Google .com 4 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: gomod 8 | directory: / 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | drone-telegram 26 | coverage.txt 27 | .env 28 | release 29 | bin 30 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.17 2 | 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | 6 | LABEL maintainer="Bo-Yi Wu " \ 7 | org.label-schema.name="Telegram Plugin" \ 8 | org.label-schema.vendor="Bo-Yi Wu" \ 9 | org.label-schema.schema-version="1.0" 10 | 11 | LABEL org.opencontainers.image.source=https://github.com/appleboy/drone-telegram 12 | LABEL org.opencontainers.image.description="plugin for sending telegram notifications" 13 | LABEL org.opencontainers.image.licenses=MIT 14 | 15 | RUN apk add --no-cache ca-certificates && \ 16 | rm -rf /var/cache/apk/* 17 | 18 | COPY release/${TARGETOS}/${TARGETARCH}/drone-telegram /bin/ 19 | 20 | ENTRYPOINT ["/bin/drone-telegram"] 21 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - bodyclose 5 | - dogsled 6 | - errcheck 7 | - exportloopref 8 | - exhaustive 9 | - gochecknoinits 10 | - goconst 11 | - gocritic 12 | - gofmt 13 | - goimports 14 | - goprintffuncname 15 | - gosec 16 | - gosimple 17 | - govet 18 | - ineffassign 19 | - misspell 20 | - nakedret 21 | - noctx 22 | - nolintlint 23 | - staticcheck 24 | - stylecheck 25 | - typecheck 26 | - unconvert 27 | - unparam 28 | - unused 29 | - whitespace 30 | - gofumpt 31 | 32 | issues: 33 | exclude-rules: 34 | # Exclude `lll` issues for long lines with `go:generate`. 35 | - linters: 36 | - lll 37 | source: "^//go:generate " 38 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://www.paypal.me/appleboy46'] 14 | -------------------------------------------------------------------------------- /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: Goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Setup go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: go.mod 23 | check-latest: true 24 | 25 | - name: Run GoReleaser 26 | uses: goreleaser/goreleaser-action@v6 27 | with: 28 | # either 'goreleaser' (default) or 'goreleaser-pro' 29 | distribution: goreleaser 30 | version: latest 31 | args: release --clean 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Bo-Yi Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/appleboy/drone-telegram 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/appleboy/drone-template-lib v1.3.0 7 | github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible 8 | github.com/joho/godotenv v1.5.1 9 | github.com/stretchr/testify v1.9.0 10 | github.com/urfave/cli v1.22.15 11 | ) 12 | 13 | require ( 14 | dario.cat/mergo v1.0.1 // indirect 15 | github.com/Masterminds/goutils v1.1.1 // indirect 16 | github.com/Masterminds/semver/v3 v3.3.0 // indirect 17 | github.com/Masterminds/sprig/v3 v3.3.0 // indirect 18 | github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/google/uuid v1.6.0 // indirect 21 | github.com/huandu/xstrings v1.5.0 // indirect 22 | github.com/mailgun/raymond/v2 v2.0.48 // indirect 23 | github.com/mitchellh/copystructure v1.2.0 // indirect 24 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 27 | github.com/shopspring/decimal v1.4.0 // indirect 28 | github.com/sirupsen/logrus v1.9.3 // indirect 29 | github.com/spf13/cast v1.7.0 // indirect 30 | github.com/technoweenie/multipartstreamer v1.0.1 // indirect 31 | golang.org/x/crypto v0.27.0 // indirect 32 | golang.org/x/sys v0.25.0 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint and Testing 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Setup go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version-file: go.mod 18 | check-latest: true 19 | 20 | - name: Setup golangci-lint 21 | uses: golangci/golangci-lint-action@v6 22 | with: 23 | version: latest 24 | args: --verbose 25 | 26 | - uses: hadolint/hadolint-action@v3.1.0 27 | name: hadolint for Dockerfile 28 | with: 29 | dockerfile: docker/Dockerfile 30 | 31 | test: 32 | strategy: 33 | matrix: 34 | os: [ubuntu-latest] 35 | go: [1.22, 1.23] 36 | include: 37 | - os: ubuntu-latest 38 | go-build: ~/.cache/go-build 39 | name: ${{ matrix.os }} @ Go ${{ matrix.go }} 40 | runs-on: ${{ matrix.os }} 41 | env: 42 | GO111MODULE: on 43 | GOPROXY: https://proxy.golang.org 44 | steps: 45 | - name: Set up Go ${{ matrix.go }} 46 | uses: actions/setup-go@v5 47 | with: 48 | go-version: ${{ matrix.go }} 49 | 50 | - name: Checkout Code 51 | uses: actions/checkout@v4 52 | with: 53 | ref: ${{ github.ref }} 54 | 55 | - uses: actions/cache@v4 56 | with: 57 | path: | 58 | ${{ matrix.go-build }} 59 | ~/go/pkg/mod 60 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 61 | restore-keys: | 62 | ${{ runner.os }}-go- 63 | - name: Run Tests 64 | run: | 65 | make test 66 | - name: Upload coverage to Codecov 67 | uses: codecov/codecov-action@v4 68 | with: 69 | flags: ${{ matrix.os }},go-${{ matrix.go }} 70 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [master] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [master] 20 | schedule: 21 | - cron: "41 23 * * 6" 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: ["go"] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v3 55 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "v*" 9 | pull_request: 10 | branches: 11 | - "master" 12 | 13 | jobs: 14 | build-docker: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Setup go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: "^1" 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Build binary 27 | run: | 28 | make build_linux_amd64 29 | make build_linux_arm64 30 | 31 | - name: Set up QEMU 32 | uses: docker/setup-qemu-action@v3 33 | 34 | - name: Set up Docker Buildx 35 | uses: docker/setup-buildx-action@v3 36 | 37 | - name: Login to Docker Hub 38 | uses: docker/login-action@v3 39 | with: 40 | username: ${{ secrets.DOCKERHUB_USERNAME }} 41 | password: ${{ secrets.DOCKERHUB_TOKEN }} 42 | 43 | - name: Login to GitHub Container Registry 44 | uses: docker/login-action@v3 45 | with: 46 | registry: ghcr.io 47 | username: ${{ github.repository_owner }} 48 | password: ${{ secrets.GITHUB_TOKEN }} 49 | 50 | - name: Docker meta 51 | id: docker-meta 52 | uses: docker/metadata-action@v5 53 | with: 54 | images: | 55 | ${{ github.repository }} 56 | ghcr.io/${{ github.repository }} 57 | tags: | 58 | type=raw,value=latest,enable={{is_default_branch}} 59 | type=semver,pattern={{version}} 60 | type=semver,pattern={{major}}.{{minor}} 61 | type=semver,pattern={{major}} 62 | 63 | - name: Build and push 64 | uses: docker/build-push-action@v6 65 | with: 66 | context: . 67 | platforms: linux/amd64,linux/arm64 68 | file: docker/Dockerfile 69 | push: ${{ github.event_name != 'pull_request' }} 70 | tags: ${{ steps.docker-meta.outputs.tags }} 71 | labels: ${{ steps.docker-meta.outputs.labels }} 72 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | 5 | builds: 6 | - env: 7 | - CGO_ENABLED=0 8 | goos: 9 | - darwin 10 | - linux 11 | - windows 12 | - freebsd 13 | goarch: 14 | - amd64 15 | - arm 16 | - arm64 17 | goarm: 18 | - "5" 19 | - "6" 20 | - "7" 21 | ignore: 22 | - goos: darwin 23 | goarch: arm 24 | - goos: darwin 25 | goarch: ppc64le 26 | - goos: darwin 27 | goarch: s390x 28 | - goos: windows 29 | goarch: ppc64le 30 | - goos: windows 31 | goarch: s390x 32 | - goos: windows 33 | goarch: arm 34 | goarm: "5" 35 | - goos: windows 36 | goarch: arm 37 | goarm: "6" 38 | - goos: windows 39 | goarch: arm 40 | goarm: "7" 41 | - goos: windows 42 | goarch: arm64 43 | - goos: freebsd 44 | goarch: ppc64le 45 | - goos: freebsd 46 | goarch: s390x 47 | - goos: freebsd 48 | goarch: arm 49 | goarm: "5" 50 | - goos: freebsd 51 | goarch: arm 52 | goarm: "6" 53 | - goos: freebsd 54 | goarch: arm 55 | goarm: "7" 56 | - goos: freebsd 57 | goarch: arm64 58 | flags: 59 | - -trimpath 60 | ldflags: 61 | - -s -w 62 | - -X main.Version={{.Version}} 63 | binary: >- 64 | {{ .ProjectName }}- 65 | {{- if .IsSnapshot }}{{ .Branch }}- 66 | {{- else }}{{- .Version }}-{{ end }} 67 | {{- .Os }}- 68 | {{- if eq .Arch "amd64" }}amd64 69 | {{- else if eq .Arch "amd64_v1" }}amd64 70 | {{- else if eq .Arch "386" }}386 71 | {{- else }}{{ .Arch }}{{ end }} 72 | {{- if .Arm }}-{{ .Arm }}{{ end }} 73 | no_unique_dist_dir: true 74 | hooks: 75 | post: 76 | - cmd: xz -k -9 {{ .Path }} 77 | dir: ./dist/ 78 | 79 | archives: 80 | - format: binary 81 | name_template: "{{ .Binary }}" 82 | allow_different_binary_count: true 83 | 84 | checksum: 85 | name_template: "checksums.txt" 86 | extra_files: 87 | - glob: ./**.xz 88 | 89 | snapshot: 90 | name_template: "{{ incpatch .Version }}" 91 | 92 | release: 93 | # You can add extra pre-existing files to the release. 94 | # The filename on the release will be the last part of the path (base). 95 | # If another file with the same name exists, the last one found will be used. 96 | # 97 | # Templates: allowed 98 | extra_files: 99 | - glob: ./**.xz 100 | 101 | changelog: 102 | use: github 103 | groups: 104 | - title: Features 105 | regexp: "^.*feat[(\\w)]*:+.*$" 106 | order: 0 107 | - title: "Bug fixes" 108 | regexp: "^.*fix[(\\w)]*:+.*$" 109 | order: 1 110 | - title: "Enhancements" 111 | regexp: "^.*chore[(\\w)]*:+.*$" 112 | order: 2 113 | - title: "Refactor" 114 | regexp: "^.*refactor[(\\w)]*:+.*$" 115 | order: 3 116 | - title: "Build process updates" 117 | regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ 118 | order: 4 119 | - title: "Documentation updates" 120 | regexp: ^.*?docs?(\(.+\))??!?:.+$ 121 | order: 4 122 | - title: Others 123 | order: 999 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drone-telegram 2 | 3 | ![logo](./images/logo.png) 4 | 5 | [![GoDoc](https://godoc.org/github.com/appleboy/drone-telegram?status.svg)](https://godoc.org/github.com/appleboy/drone-telegram) 6 | [![codecov](https://codecov.io/gh/appleboy/drone-telegram/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/drone-telegram) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/drone-telegram)](https://goreportcard.com/report/github.com/appleboy/drone-telegram) 8 | 9 | [Drone](https://github.com/drone/drone) plugin for sending telegram notifications. For the usage 10 | information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/appleboy/drone-telegram/). 11 | 12 | ## Feature 13 | 14 | * [x] Send with Text Message. (`markdown` or `html` format) 15 | * [x] Send with New Photo. 16 | * [x] Send with New Document. 17 | * [x] Send with New Audio. 18 | * [x] Send with New Voice. 19 | * [x] Send with New Location. 20 | * [x] Send with New Venue. 21 | * [x] Send with New Video. 22 | * [x] Send with New Sticker. 23 | 24 | ## Build or Download a binary 25 | 26 | The pre-compiled binaries can be downloaded from [release page](https://github.com/appleboy/drone-telegram/releases). Support the following OS type. 27 | 28 | * Windows amd64/386 29 | * Linux arm/amd64/386 30 | * Darwin amd64/386 31 | 32 | With `Go` installed 33 | 34 | ```sh 35 | go get -u -v github.com/appleboy/drone-telegram 36 | ``` 37 | 38 | or build the binary with the following command: 39 | 40 | ```sh 41 | export GOOS=linux 42 | export GOARCH=amd64 43 | export CGO_ENABLED=0 44 | export GO111MODULE=on 45 | 46 | go test -cover ./... 47 | 48 | go build -v -a -tags netgo -o release/linux/amd64/drone-telegram . 49 | ``` 50 | 51 | ## Testing 52 | 53 | Test the package with the following command: 54 | 55 | ```sh 56 | make test 57 | ``` 58 | 59 | ## Usage 60 | 61 | Execute from the working directory: 62 | 63 | ```sh 64 | docker run --rm \ 65 | -e PLUGIN_TOKEN=xxxxxxx \ 66 | -e PLUGIN_TO=xxxxxxx \ 67 | -e PLUGIN_MESSAGE=test \ 68 | -e PLUGIN_MESSAGE_FILE=testmessage.md \ 69 | -e PLUGIN_PHOTO=tests/github.png \ 70 | -e PLUGIN_DOCUMENT=tests/gophercolor.png \ 71 | -e PLUGIN_STICKER=tests/github-logo.png \ 72 | -e PLUGIN_AUDIO=tests/audio.mp3 \ 73 | -e PLUGIN_VOICE=tests/voice.ogg \ 74 | -e PLUGIN_LOCATION="24.9163213 121.1424972" \ 75 | -e PLUGIN_VENUE="24.9163213 121.1424972 title address" \ 76 | -e PLUGIN_VIDEO=tests/video.mp4 \ 77 | -e PLUGIN_DEBUG=true \ 78 | -e PLUGIN_ONLY_MATCH_EMAIL=false \ 79 | -e PLUGIN_FORMAT=markdown \ 80 | -e DRONE_REPO_OWNER=appleboy \ 81 | -e DRONE_REPO_NAME=go-hello \ 82 | -e DRONE_COMMIT_SHA=e5e82b5eb3737205c25955dcc3dcacc839b7be52 \ 83 | -e DRONE_COMMIT_BRANCH=master \ 84 | -e DRONE_COMMIT_LINK=https://github.com/appleboy/go-hello/compare/master... \ 85 | -e DRONE_COMMIT_AUTHOR=appleboy \ 86 | -e DRONE_COMMIT_AUTHOR_EMAIL=appleboy@gmail.com \ 87 | -e DRONE_BUILD_NUMBER=1 \ 88 | -e DRONE_BUILD_STATUS=success \ 89 | -e DRONE_BUILD_LINK=http://github.com/appleboy/go-hello \ 90 | -e DRONE_TAG=1.0.0 \ 91 | -e DRONE_JOB_STARTED=1477550550 \ 92 | -e DRONE_JOB_FINISHED=1477550750 \ 93 | -v $(pwd):$(pwd) \ 94 | -w $(pwd) \ 95 | appleboy/drone-telegram 96 | ``` 97 | 98 | Load all environments from file. 99 | 100 | ```bash 101 | docker run --rm \ 102 | -e PLUGIN_ENV_FILE=your_env_file_path \ 103 | -v $(pwd):$(pwd) \ 104 | -w $(pwd) \ 105 | appleboy/drone-telegram 106 | ``` 107 | -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIST := dist 2 | EXECUTABLE := drone-telegram 3 | GOFMT ?= gofumpt -l 4 | DIST := dist 5 | DIST_DIRS := $(DIST)/binaries $(DIST)/release 6 | GO ?= go 7 | SHASUM ?= shasum -a 256 8 | GOFILES := $(shell find . -name "*.go" -type f) 9 | HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" ) 10 | XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest 11 | XGO_VERSION := go-1.19.x 12 | GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 13 | 14 | LINUX_ARCHS ?= linux/amd64,linux/arm64 15 | DARWIN_ARCHS ?= darwin-10.12/amd64,darwin-10.12/arm64 16 | WINDOWS_ARCHS ?= windows/* 17 | 18 | ifneq ($(shell uname), Darwin) 19 | EXTLDFLAGS = -extldflags "-static" $(null) 20 | else 21 | EXTLDFLAGS = 22 | endif 23 | 24 | ifeq ($(HAS_GO), GO) 25 | GOPATH ?= $(shell $(GO) env GOPATH) 26 | export PATH := $(GOPATH)/bin:$(PATH) 27 | 28 | CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766 29 | CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) 30 | endif 31 | 32 | ifeq ($(OS), Windows_NT) 33 | GOFLAGS := -v -buildmode=exe 34 | EXECUTABLE ?= $(EXECUTABLE).exe 35 | else ifeq ($(OS), Windows) 36 | GOFLAGS := -v -buildmode=exe 37 | EXECUTABLE ?= $(EXECUTABLE).exe 38 | else 39 | GOFLAGS := -v 40 | EXECUTABLE ?= $(EXECUTABLE) 41 | endif 42 | 43 | ifneq ($(DRONE_TAG),) 44 | VERSION ?= $(DRONE_TAG) 45 | else 46 | VERSION ?= $(shell git describe --tags --always || git rev-parse --short HEAD) 47 | endif 48 | 49 | TAGS ?= 50 | LDFLAGS ?= -X 'main.Version=$(VERSION)' 51 | 52 | all: build 53 | 54 | fmt: 55 | @hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 56 | $(GO) install mvdan.cc/gofumpt; \ 57 | fi 58 | $(GOFMT) -w $(GOFILES) 59 | 60 | vet: 61 | $(GO) vet ./... 62 | 63 | .PHONY: fmt-check 64 | fmt-check: 65 | @hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 66 | $(GO) install mvdan.cc/gofumpt; \ 67 | fi 68 | @diff=$$($(GOFMT) -d $(GOFILES)); \ 69 | if [ -n "$$diff" ]; then \ 70 | echo "Please run 'make fmt' and commit the result:"; \ 71 | echo "$${diff}"; \ 72 | exit 1; \ 73 | fi; 74 | 75 | test: 76 | @$(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1 77 | 78 | install: $(GOFILES) 79 | $(GO) install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' 80 | 81 | build: $(EXECUTABLE) 82 | 83 | $(EXECUTABLE): $(GOFILES) 84 | $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o bin/$@ 85 | 86 | build_linux_amd64: 87 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/amd64/$(DEPLOY_IMAGE) 88 | 89 | build_linux_i386: 90 | CGO_ENABLED=0 GOOS=linux GOARCH=386 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/i386/$(DEPLOY_IMAGE) 91 | 92 | build_linux_arm64: 93 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/arm64/$(DEPLOY_IMAGE) 94 | 95 | build_linux_arm: 96 | CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/arm/$(DEPLOY_IMAGE) 97 | 98 | ssh-server: 99 | adduser -h /home/drone-scp -s /bin/sh -D -S drone-scp 100 | echo drone-scp:1234 | chpasswd 101 | mkdir -p /home/drone-scp/.ssh 102 | chmod 700 /home/drone-scp/.ssh 103 | cat tests/.ssh/id_rsa.pub >> /home/drone-scp/.ssh/authorized_keys 104 | cat tests/.ssh/test.pub >> /home/drone-scp/.ssh/authorized_keys 105 | chmod 600 /home/drone-scp/.ssh/authorized_keys 106 | chown -R drone-scp /home/drone-scp/.ssh 107 | apk add --update openssh openrc 108 | rm -rf /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_dsa_key 109 | sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config 110 | sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config 111 | ./tests/entrypoint.sh /usr/sbin/sshd -D & 112 | 113 | coverage: 114 | sed -i '/main.go/d' coverage.txt 115 | 116 | .PHONY: deps-backend 117 | deps-backend: 118 | $(GO) mod download 119 | $(GO) install $(GXZ_PAGAGE) 120 | $(GO) install $(XGO_PACKAGE) 121 | 122 | .PHONY: release 123 | release: release-linux release-darwin release-windows release-copy release-compress release-check 124 | 125 | $(DIST_DIRS): 126 | mkdir -p $(DIST_DIRS) 127 | 128 | .PHONY: release-windows 129 | release-windows: | $(DIST_DIRS) 130 | CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(WINDOWS_ARCHS)' -out $(EXECUTABLE)-$(VERSION) . 131 | ifeq ($(CI),true) 132 | cp -r /build/* $(DIST)/binaries/ 133 | endif 134 | 135 | .PHONY: release-linux 136 | release-linux: | $(DIST_DIRS) 137 | CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out $(EXECUTABLE)-$(VERSION) . 138 | ifeq ($(CI),true) 139 | cp -r /build/* $(DIST)/binaries/ 140 | endif 141 | 142 | .PHONY: release-darwin 143 | release-darwin: | $(DIST_DIRS) 144 | CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets '$(DARWIN_ARCHS)' -out $(EXECUTABLE)-$(VERSION) . 145 | ifeq ($(CI),true) 146 | cp -r /build/* $(DIST)/binaries/ 147 | endif 148 | 149 | .PHONY: release-copy 150 | release-copy: | $(DIST_DIRS) 151 | cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done; 152 | 153 | .PHONY: release-check 154 | release-check: | $(DIST_DIRS) 155 | cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done; 156 | 157 | .PHONY: release-compress 158 | release-compress: | $(DIST_DIRS) 159 | cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done; 160 | 161 | clean: 162 | $(GO) clean -x -i ./... 163 | rm -rf coverage.txt $(EXECUTABLE) $(DIST) 164 | 165 | version: 166 | @echo $(VERSION) 167 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= 2 | dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 4 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 5 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 6 | github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= 7 | github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 8 | github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= 9 | github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= 10 | github.com/appleboy/drone-template-lib v1.3.0 h1:aX36/1za3v8JsEyBeMY1Bp/VNRtZa8qPYkfkjBszW+A= 11 | github.com/appleboy/drone-template-lib v1.3.0/go.mod h1:edlmXkFMKYAVypff8r2oN7aFlHfOZE5sLyPEnRHONeA= 12 | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= 13 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 14 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 16 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 17 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 18 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 19 | github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= 20 | github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= 21 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 22 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 23 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 24 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 25 | github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= 26 | github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 27 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 28 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 29 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 30 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 31 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 32 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 33 | github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= 34 | github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= 35 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 36 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 37 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 38 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 39 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 40 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 41 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 42 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 43 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 44 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 45 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 46 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 47 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 48 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 49 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 50 | github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= 51 | github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 52 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 53 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 54 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 55 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 56 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 57 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 58 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 59 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 60 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 61 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 62 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 63 | github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= 64 | github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= 65 | github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM= 66 | github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= 67 | golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 68 | golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 69 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 70 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 72 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 74 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 75 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 76 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 77 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 78 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 79 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 80 | -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2019-10-19T00:00:00+00:00 3 | author: appleboy 4 | containerImage: docker.io/appleboy/drone-telegram 5 | containerImageUrl: https://hub.docker.com/r/appleboy/drone-telegram 6 | description: Plugin for sending Telegram notifications 7 | icon: https://raw.githubusercontent.com/appleboy/drone-telegram/refs/heads/master/images/logo.svg 8 | image: appleboy/drone-telegram 9 | logo: telegram.svg 10 | name: Telegram 11 | repo: appleboy/drone-telegram 12 | tags: [ notifications, chat ] 13 | title: Telegram 14 | url: https://github.com/appleboy/drone-telegram 15 | --- 16 | 17 | The Telegram plugin posts build status messages to your account. The below pipeline configuration demonstrates simple usage: 18 | 19 | ```yaml 20 | - name: send telegram notification 21 | image: appleboy/drone-telegram 22 | settings: 23 | token: xxxxxxxxxx 24 | to: telegram_user_id 25 | ``` 26 | 27 | Example configuration with photo message: 28 | 29 | ```diff 30 | - name: send telegram notification 31 | image: appleboy/drone-telegram 32 | settings: 33 | token: xxxxxxxxxx 34 | to: telegram_user_id 35 | + photo: 36 | + - tests/1.png 37 | + - tests/2.png 38 | ``` 39 | 40 | Example configuration with document message: 41 | 42 | ```diff 43 | - name: send telegram notification 44 | image: appleboy/drone-telegram 45 | settings: 46 | token: xxxxxxxxxx 47 | to: telegram_user_id 48 | + document: 49 | + - tests/1.pdf 50 | + - tests/2.pdf 51 | ``` 52 | 53 | Example configuration with sticker message: 54 | 55 | ```diff 56 | - name: send telegram notification 57 | image: appleboy/drone-telegram 58 | settings: 59 | token: xxxxxxxxxx 60 | to: telegram_user_id 61 | + sticker: 62 | + - tests/3.png 63 | + - tests/4.png 64 | ``` 65 | 66 | Example configuration with audio message: 67 | 68 | ```diff 69 | - name: send telegram notification 70 | image: appleboy/drone-telegram 71 | settings: 72 | token: xxxxxxxxxx 73 | to: telegram_user_id 74 | + audio: 75 | + - tests/audio1.mp3 76 | + - tests/audio2.mp3 77 | ``` 78 | 79 | Example configuration with voice message: 80 | 81 | ```diff 82 | - name: send telegram notification 83 | image: appleboy/drone-telegram 84 | settings: 85 | token: xxxxxxxxxx 86 | to: telegram_user_id 87 | + voice: 88 | + - tests/voice1.ogg 89 | + - tests/voice2.ogg 90 | ``` 91 | 92 | Example configuration with location message: 93 | 94 | ```diff 95 | - name: send telegram notification 96 | image: appleboy/drone-telegram 97 | settings: 98 | token: xxxxxxxxxx 99 | to: telegram_user_id 100 | + location: 101 | + - 24.9163213,121.1424972 102 | + - 24.9263213,121.1224972 103 | ``` 104 | 105 | Example configuration with venue message: 106 | 107 | ```diff 108 | - name: send telegram notification 109 | image: appleboy/drone-telegram 110 | settings: 111 | token: xxxxxxxxxx 112 | to: telegram_user_id 113 | + venue: 114 | + - 24.9163213,121.1424972,title,address 115 | + - 24.3163213,121.1824972,title,address 116 | ``` 117 | 118 | Example configuration with video message: 119 | 120 | ```diff 121 | - name: send telegram notification 122 | image: appleboy/drone-telegram 123 | settings: 124 | token: xxxxxxxxxx 125 | to: telegram_user_id 126 | + video: 127 | + - tests/video1.mp4 128 | + - tests/video2.mp4 129 | ``` 130 | 131 | Example configuration with message format (`Markdown` or `HTML`), default as `Markdown`: 132 | 133 | ```diff 134 | - name: send telegram notification 135 | image: appleboy/drone-telegram 136 | settings: 137 | token: xxxxxxxxxx 138 | to: telegram_user_id 139 | + format: Markdown 140 | ``` 141 | 142 | Example configuration with a custom message template: 143 | 144 | ```diff 145 | - name: send telegram notification 146 | image: appleboy/drone-telegram 147 | settings: 148 | token: xxxxxxxxxx 149 | to: telegram_user_id 150 | + message: > 151 | + {{#success build.status}} 152 | + build {{build.number}} succeeded. Good job. 153 | + {{else}} 154 | + build {{build.number}} failed. Fix me please. 155 | + {{/success}} 156 | ``` 157 | 158 | Example configuration with a custom message template loaded from file: 159 | 160 | ```diff 161 | - name: send telegram notification 162 | image: appleboy/drone-telegram 163 | settings: 164 | token: xxxxxxxxxx 165 | to: telegram_user_id 166 | + message_file: message_file.tpl 167 | ``` 168 | 169 | Example configuration with a generic message template loaded from file, with additional extra vars: 170 | 171 | ```diff 172 | - name: send telegram notification 173 | image: appleboy/drone-telegram 174 | settings: 175 | token: xxxxxxxxxx 176 | to: telegram_user_id 177 | + message_file: message_file.tpl 178 | + template_vars: 179 | + env: testing 180 | + app: MyApp 181 | ``` 182 | 183 | Where `message_file.tpl` is: 184 | 185 | ```bash 186 | Build finished for *{{tpl.app}}* - *{{tpl.env}}* 187 | 188 | {{#success build.status}} 189 | build {{build.number}} succeeded. Good job. 190 | {{else}} 191 | build {{build.number}} failed. Fix me please. 192 | {{/success}} 193 | ``` 194 | 195 | Example configuration with a custom message template, with extra vars loaded from file (e.g. from previous build steps): 196 | 197 | ```diff 198 | - name: send telegram notification 199 | image: appleboy/drone-telegram 200 | settings: 201 | token: xxxxxxxxxx 202 | to: telegram_user_id 203 | + template_vars_file: build_report.json 204 | + message: > 205 | + {{#success build.status}} 206 | + build {{build.number}} succeeded, artefact version = {{tpl.artefact_version}}. 207 | + {{else}} 208 | + build {{build.number}} failed. Fix me please. 209 | + {{/success}} 210 | ``` 211 | 212 | Where `build_report.json` is: 213 | 214 | ``` 215 | { 216 | ... 217 | "artefact_version": "0.2.3452" 218 | ... 219 | } 220 | ``` 221 | 222 | Example configuration with a custom socks5 URL: 223 | 224 | ```diff 225 | - name: send telegram notification 226 | image: appleboy/drone-telegram 227 | settings: 228 | token: xxxxxxxxxx 229 | to: telegram_user_id 230 | message: send message using custom socks5 URL 231 | + socks5: socks5://67.204.21.1:64312 232 | ``` 233 | 234 | Disables link previews for links in this message 235 | 236 | ```diff 237 | - name: send telegram notification 238 | image: appleboy/drone-telegram 239 | settings: 240 | token: xxxxxxxxxx 241 | to: telegram_user_id 242 | message: send message without a link preview 243 | + disable_web_page_preview: true 244 | ``` 245 | 246 | Disables notifications for this message 247 | 248 | ```diff 249 | - name: send telegram notification 250 | image: appleboy/drone-telegram 251 | settings: 252 | token: xxxxxxxxxx 253 | to: telegram_user_id 254 | message: send message message silently 255 | + disable_notification: true 256 | ``` 257 | 258 | ## Parameter Reference 259 | 260 | token 261 | : telegram token from [telegram developer center](https://core.telegram.org/bots/api) 262 | 263 | to 264 | : telegram user id (can be requested from the @userinfobot inside Telegram) 265 | 266 | message 267 | : overwrite the default message template 268 | 269 | message_file 270 | : overwrite the default message template with the contents of the specified file 271 | 272 | template_vars 273 | : define additional template vars. Example: `var1: hello` can be used within the template as `tpl.var1` 274 | 275 | template_vars_file 276 | : load additional template vars from json file. Example: given file content `{"var1":"hello"}`, variable can be used within the template as `tpl.var1` 277 | 278 | photo 279 | : local file path 280 | 281 | document 282 | : local file path 283 | 284 | sticker 285 | : local file path 286 | 287 | audio 288 | : local file path 289 | 290 | voice 291 | : local file path 292 | 293 | location 294 | : local file path 295 | 296 | video 297 | : local file path 298 | 299 | venue 300 | : local file path 301 | 302 | format 303 | : `markdown` or `html` format 304 | 305 | ## Template Reference 306 | 307 | repo.owner 308 | : repository owner 309 | 310 | repo.name 311 | : repository name 312 | 313 | commit.sha 314 | : git sha for current commit 315 | 316 | commit.branch 317 | : git branch for current commit 318 | 319 | commit.link 320 | : git commit link in remote 321 | 322 | commit.author 323 | : git author for current commit 324 | 325 | commit.email 326 | : git author email for current commit 327 | 328 | commit.message 329 | : git current commit message 330 | 331 | build.status 332 | : build status type enumeration, either `success` or `failure` 333 | 334 | build.event 335 | : build event type enumeration, one of `push`, `pull_request`, `tag`, `deployment` 336 | 337 | build.number 338 | : build number 339 | 340 | build.tag 341 | : git tag for current commit 342 | 343 | build.link 344 | : link the the build results in drone 345 | 346 | build.started 347 | : unix timestamp for build started 348 | 349 | build.finished 350 | : unix timestamp for build finished 351 | 352 | ## Template Function Reference 353 | 354 | uppercasefirst 355 | : converts the first letter of a string to uppercase 356 | 357 | uppercase 358 | : converts a string to uppercase 359 | 360 | lowercase 361 | : converts a string to lowercase. Example `{{lowercase build.author}}` 362 | 363 | datetime 364 | : converts a unix timestamp to a date time string. Example `{{datetime build.started}}` 365 | 366 | success 367 | : returns true if the build is successful 368 | 369 | failure 370 | : returns true if the build is failed 371 | 372 | truncate 373 | : returns a truncated string to n characters. Example `{{truncate build.sha 8}}` 374 | 375 | urlencode 376 | : returns a url encoded string 377 | 378 | since 379 | : returns a duration string between now and the given timestamp. Example `{{since build.started}}` 380 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/joho/godotenv" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | // Version set at compile-time 12 | var ( 13 | Version string 14 | ) 15 | 16 | func main() { 17 | // Load env-file if it exists first 18 | if filename, found := os.LookupEnv("PLUGIN_ENV_FILE"); found { 19 | _ = godotenv.Load(filename) 20 | } 21 | 22 | if _, err := os.Stat("/run/drone/env"); err == nil { 23 | _ = godotenv.Overload("/run/drone/env") 24 | } 25 | 26 | app := cli.NewApp() 27 | app.Name = "telegram plugin" 28 | app.Usage = "telegram plugin" 29 | app.Action = run 30 | app.Version = Version 31 | app.Flags = []cli.Flag{ 32 | cli.StringFlag{ 33 | Name: "token", 34 | Usage: "telegram token", 35 | EnvVar: "PLUGIN_TOKEN,TELEGRAM_TOKEN,INPUT_TOKEN", 36 | }, 37 | cli.StringSliceFlag{ 38 | Name: "to", 39 | Usage: "telegram user", 40 | EnvVar: "PLUGIN_TO,TELEGRAM_TO,INPUT_TO", 41 | }, 42 | cli.StringFlag{ 43 | Name: "message", 44 | Usage: "send telegram message", 45 | EnvVar: "PLUGIN_MESSAGE,TELEGRAM_MESSAGE,INPUT_MESSAGE", 46 | }, 47 | cli.StringFlag{ 48 | Name: "message.file", 49 | Usage: "send telegram message from file", 50 | EnvVar: "PLUGIN_MESSAGE_FILE,TELEGRAM_MESSAGE_FILE,INPUT_MESSAGE_FILE", 51 | }, 52 | cli.StringFlag{ 53 | Name: "template.vars", 54 | Usage: "additional template vars to be used in message, as JSON string", 55 | EnvVar: "PLUGIN_TEMPLATE_VARS,TELEGRAM_TEMPLATE_VARS,INPUT_TEMPLATE_VARS", 56 | }, 57 | cli.StringFlag{ 58 | Name: "template.vars.file", 59 | Usage: "load additional template vars to be used in message, from json file", 60 | EnvVar: "PLUGIN_TEMPLATE_VARS_FILE,TELEGRAM_TEMPLATE_VARS_FILE", 61 | }, 62 | cli.StringSliceFlag{ 63 | Name: "photo", 64 | Usage: "send photo message", 65 | EnvVar: "PLUGIN_PHOTO,PHOTO,INPUT_PHOTO", 66 | }, 67 | cli.StringSliceFlag{ 68 | Name: "document", 69 | Usage: "send document message", 70 | EnvVar: "PLUGIN_DOCUMENT,DOCUMENT,INPUT_DOCUMENT", 71 | }, 72 | cli.StringSliceFlag{ 73 | Name: "sticker", 74 | Usage: "send sticker message", 75 | EnvVar: "PLUGIN_STICKER,STICKER,INPUT_STICKER", 76 | }, 77 | cli.StringSliceFlag{ 78 | Name: "audio", 79 | Usage: "send audio message", 80 | EnvVar: "PLUGIN_AUDIO,AUDIO,INPUT_AUDIO", 81 | }, 82 | cli.StringSliceFlag{ 83 | Name: "voice", 84 | Usage: "send voice message", 85 | EnvVar: "PLUGIN_VOICE,VOICE,INPUT_VOICE", 86 | }, 87 | cli.StringSliceFlag{ 88 | Name: "location", 89 | Usage: "send location message", 90 | EnvVar: "PLUGIN_LOCATION,LOCATION,INPUT_LOCATION", 91 | }, 92 | cli.StringSliceFlag{ 93 | Name: "venue", 94 | Usage: "send venue message", 95 | EnvVar: "PLUGIN_VENUE,VENUE,INPUT_VENUE", 96 | }, 97 | cli.StringSliceFlag{ 98 | Name: "video", 99 | Usage: "send video message", 100 | EnvVar: "PLUGIN_VIDEO,VIDEO,INPUT_VIDEO", 101 | }, 102 | cli.BoolFlag{ 103 | Name: "debug", 104 | Usage: "enable debug message", 105 | EnvVar: "PLUGIN_DEBUG,DEBUG,INPUT_DEBUG", 106 | }, 107 | cli.BoolFlag{ 108 | Name: "match.email", 109 | Usage: "send message when only match email", 110 | EnvVar: "PLUGIN_ONLY_MATCH_EMAIL,INPUT_ONLY_MATCH_EMAIL", 111 | }, 112 | cli.BoolFlag{ 113 | Name: "disable.webpage.preview", 114 | Usage: "disables link previews for links in this message", 115 | EnvVar: "PLUGIN_DISABLE_WEB_PAGE_PREVIEW,INPUT_DISABLE_WEB_PAGE_PREVIEW", 116 | }, 117 | cli.BoolFlag{ 118 | Name: "disable.notification", 119 | Usage: "sends the message silently. users will receive a notification with no sound.", 120 | EnvVar: "PLUGIN_DISABLE_NOTIFICATION,INPUT_DISABLE_NOTIFICATION", 121 | }, 122 | cli.StringFlag{ 123 | Name: "format", 124 | Value: formatMarkdown, 125 | Usage: "telegram message format (Markdown or HTML)", 126 | EnvVar: "PLUGIN_FORMAT,FORMAT,INPUT_FORMAT", 127 | }, 128 | cli.StringFlag{ 129 | Name: "repo", 130 | Usage: "repository owner and repository name", 131 | EnvVar: "DRONE_REPO,GITHUB_REPOSITORY", 132 | }, 133 | cli.StringFlag{ 134 | Name: "repo.namespace", 135 | Usage: "repository namespace", 136 | EnvVar: "DRONE_REPO_OWNER,DRONE_REPO_NAMESPACE,GITHUB_ACTOR", 137 | }, 138 | cli.StringFlag{ 139 | Name: "repo.name", 140 | Usage: "repository name", 141 | EnvVar: "DRONE_REPO_NAME", 142 | }, 143 | cli.StringFlag{ 144 | Name: "commit.sha", 145 | Usage: "git commit sha", 146 | EnvVar: "DRONE_COMMIT_SHA,GITHUB_SHA", 147 | }, 148 | cli.StringFlag{ 149 | Name: "commit.ref", 150 | Usage: "git commit ref", 151 | EnvVar: "DRONE_COMMIT_REF,GITHUB_REF", 152 | }, 153 | cli.StringFlag{ 154 | Name: "commit.branch", 155 | Value: "master", 156 | Usage: "git commit branch", 157 | EnvVar: "DRONE_COMMIT_BRANCH", 158 | }, 159 | cli.StringFlag{ 160 | Name: "commit.link", 161 | Usage: "git commit link", 162 | EnvVar: "DRONE_COMMIT_LINK", 163 | }, 164 | cli.StringFlag{ 165 | Name: "commit.author", 166 | Usage: "git author name", 167 | EnvVar: "DRONE_COMMIT_AUTHOR", 168 | }, 169 | cli.StringFlag{ 170 | Name: "commit.author.email", 171 | Usage: "git author email", 172 | EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL", 173 | }, 174 | cli.StringFlag{ 175 | Name: "commit.author.avatar", 176 | Usage: "git author avatar", 177 | EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR", 178 | }, 179 | cli.StringFlag{ 180 | Name: "commit.message", 181 | Usage: "commit message", 182 | EnvVar: "DRONE_COMMIT_MESSAGE", 183 | }, 184 | cli.StringFlag{ 185 | Name: "build.event", 186 | Value: "push", 187 | Usage: "build event", 188 | EnvVar: "DRONE_BUILD_EVENT", 189 | }, 190 | cli.IntFlag{ 191 | Name: "build.number", 192 | Usage: "build number", 193 | EnvVar: "DRONE_BUILD_NUMBER", 194 | }, 195 | cli.StringFlag{ 196 | Name: "build.status", 197 | Usage: "build status", 198 | Value: "success", 199 | EnvVar: "DRONE_BUILD_STATUS", 200 | }, 201 | cli.StringFlag{ 202 | Name: "build.link", 203 | Usage: "build link", 204 | EnvVar: "DRONE_BUILD_LINK", 205 | }, 206 | cli.StringFlag{ 207 | Name: "build.tag", 208 | Usage: "build tag", 209 | EnvVar: "DRONE_TAG", 210 | }, 211 | cli.StringFlag{ 212 | Name: "pull.request", 213 | Usage: "pull request", 214 | EnvVar: "DRONE_PULL_REQUEST", 215 | }, 216 | cli.Int64Flag{ 217 | Name: "build.started", 218 | Usage: "build started", 219 | EnvVar: "DRONE_STAGE_STARTED", 220 | }, 221 | cli.Int64Flag{ 222 | Name: "build.finished", 223 | Usage: "build finished", 224 | EnvVar: "DRONE_BUILD_FINISHED", 225 | }, 226 | cli.BoolFlag{ 227 | Name: "github", 228 | Usage: "Boolean value, indicates the runtime environment is GitHub Action.", 229 | EnvVar: "PLUGIN_GITHUB,GITHUB", 230 | }, 231 | cli.StringFlag{ 232 | Name: "github.workflow", 233 | Usage: "The name of the workflow.", 234 | EnvVar: "GITHUB_WORKFLOW", 235 | }, 236 | cli.StringFlag{ 237 | Name: "github.action", 238 | Usage: "The name of the action.", 239 | EnvVar: "GITHUB_ACTION", 240 | }, 241 | cli.StringFlag{ 242 | Name: "github.event.name", 243 | Usage: "The webhook name of the event that triggered the workflow.", 244 | EnvVar: "GITHUB_EVENT_NAME", 245 | }, 246 | cli.StringFlag{ 247 | Name: "github.event.path", 248 | Usage: "The path to a file that contains the payload of the event that triggered the workflow. Value: /github/workflow/event.json.", 249 | EnvVar: "GITHUB_EVENT_PATH", 250 | }, 251 | cli.StringFlag{ 252 | Name: "github.workspace", 253 | Usage: "The GitHub workspace path. Value: /github/workspace.", 254 | EnvVar: "GITHUB_WORKSPACE", 255 | }, 256 | cli.StringFlag{ 257 | Name: "deploy.to", 258 | Usage: "Provides the target deployment environment for the running build. This value is only available to promotion and rollback pipelines.", 259 | EnvVar: "DRONE_DEPLOY_TO", 260 | }, 261 | cli.StringFlag{ 262 | Name: "socks5", 263 | Usage: "Socks5 proxy URL", 264 | EnvVar: "PLUGIN_SOCKS5,SOCKS5,INPUT_SOCKS5", 265 | }, 266 | } 267 | 268 | if err := app.Run(os.Args); err != nil { 269 | log.Fatal(err) 270 | } 271 | } 272 | 273 | func run(c *cli.Context) error { 274 | plugin := Plugin{ 275 | GitHub: GitHub{ 276 | Workflow: c.String("github.workflow"), 277 | Workspace: c.String("github.workspace"), 278 | Action: c.String("github.action"), 279 | EventName: c.String("github.event.name"), 280 | EventPath: c.String("github.event.path"), 281 | }, 282 | Repo: Repo{ 283 | FullName: c.String("repo"), 284 | Namespace: c.String("repo.namespace"), 285 | Name: c.String("repo.name"), 286 | }, 287 | Commit: Commit{ 288 | Sha: c.String("commit.sha"), 289 | Ref: c.String("commit.ref"), 290 | Branch: c.String("commit.branch"), 291 | Link: c.String("commit.link"), 292 | Author: c.String("commit.author"), 293 | Email: c.String("commit.author.email"), 294 | Avatar: c.String("commit.author.avatar"), 295 | Message: c.String("commit.message"), 296 | }, 297 | Build: Build{ 298 | Tag: c.String("build.tag"), 299 | Number: c.Int("build.number"), 300 | Event: c.String("build.event"), 301 | Status: c.String("build.status"), 302 | Link: c.String("build.link"), 303 | Started: c.Int64("build.started"), 304 | Finished: c.Int64("build.finished"), 305 | PR: c.String("pull.request"), 306 | DeployTo: c.String("deploy.to"), 307 | }, 308 | Config: Config{ 309 | Token: c.String("token"), 310 | Debug: c.Bool("debug"), 311 | MatchEmail: c.Bool("match.email"), 312 | To: c.StringSlice("to"), 313 | Message: c.String("message"), 314 | MessageFile: c.String("message.file"), 315 | TemplateVars: c.String("template.vars"), 316 | TemplateVarsFile: c.String("template.vars.file"), 317 | Photo: c.StringSlice("photo"), 318 | Document: c.StringSlice("document"), 319 | Sticker: c.StringSlice("sticker"), 320 | Audio: c.StringSlice("audio"), 321 | Voice: c.StringSlice("voice"), 322 | Location: c.StringSlice("location"), 323 | Video: c.StringSlice("video"), 324 | Venue: c.StringSlice("venue"), 325 | Format: c.String("format"), 326 | GitHub: c.Bool("github"), 327 | Socks5: c.String("socks5"), 328 | 329 | DisableWebPagePreview: c.Bool("disable.webpage.preview"), 330 | DisableNotification: c.Bool("disable.notification"), 331 | }, 332 | } 333 | 334 | return plugin.Exec() 335 | } 336 | -------------------------------------------------------------------------------- /plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "html" 9 | "io" 10 | "log" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "path/filepath" 15 | "strconv" 16 | "strings" 17 | 18 | "github.com/appleboy/drone-template-lib/template" 19 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" 20 | ) 21 | 22 | const ( 23 | formatMarkdown = "Markdown" 24 | formatHTML = "HTML" 25 | ) 26 | 27 | type ( 28 | // GitHub information. 29 | GitHub struct { 30 | Workflow string 31 | Workspace string 32 | Action string 33 | EventName string 34 | EventPath string 35 | } 36 | 37 | // Repo information. 38 | Repo struct { 39 | FullName string 40 | Namespace string 41 | Name string 42 | } 43 | 44 | // Commit information. 45 | Commit struct { 46 | Sha string 47 | Ref string 48 | Branch string 49 | Link string 50 | Author string 51 | Avatar string 52 | Email string 53 | Message string 54 | } 55 | 56 | // Build information. 57 | Build struct { 58 | Tag string 59 | Event string 60 | Number int 61 | Status string 62 | Link string 63 | Started int64 64 | Finished int64 65 | PR string 66 | DeployTo string 67 | } 68 | 69 | // Config for the plugin. 70 | Config struct { 71 | Token string 72 | Debug bool 73 | MatchEmail bool 74 | To []string 75 | Message string 76 | MessageFile string 77 | TemplateVarsFile string 78 | TemplateVars string 79 | Photo []string 80 | Document []string 81 | Sticker []string 82 | Audio []string 83 | Voice []string 84 | Location []string 85 | Video []string 86 | Venue []string 87 | Format string 88 | GitHub bool 89 | Socks5 string 90 | 91 | DisableWebPagePreview bool 92 | DisableNotification bool 93 | } 94 | 95 | // Plugin values. 96 | Plugin struct { 97 | GitHub GitHub 98 | Repo Repo 99 | Commit Commit 100 | Build Build 101 | Config Config 102 | Tpl map[string]string 103 | } 104 | 105 | // Location format 106 | Location struct { 107 | Title string 108 | Address string 109 | Latitude float64 110 | Longitude float64 111 | } 112 | ) 113 | 114 | var icons = map[string]string{ 115 | "failure": "❌", 116 | "cancelled": "❕", 117 | "success": "✅", 118 | } 119 | 120 | func trimElement(keys []string) []string { 121 | var newKeys []string 122 | 123 | for _, value := range keys { 124 | value = strings.Trim(value, " ") 125 | if len(value) == 0 { 126 | continue 127 | } 128 | newKeys = append(newKeys, value) 129 | } 130 | 131 | return newKeys 132 | } 133 | 134 | func escapeMarkdown(keys []string) []string { 135 | var newKeys []string 136 | 137 | for _, value := range keys { 138 | value = escapeMarkdownOne(value) 139 | if len(value) == 0 { 140 | continue 141 | } 142 | newKeys = append(newKeys, value) 143 | } 144 | 145 | return newKeys 146 | } 147 | 148 | func escapeMarkdownOne(str string) string { 149 | str = strings.ReplaceAll(str, `\_`, `_`) 150 | str = strings.ReplaceAll(str, `_`, `\_`) 151 | 152 | return str 153 | } 154 | 155 | func globList(keys []string) []string { 156 | var newKeys []string 157 | 158 | for _, pattern := range keys { 159 | pattern = strings.Trim(pattern, " ") 160 | matches, err := filepath.Glob(pattern) 161 | if err != nil { 162 | fmt.Printf("Glob error for %q: %s\n", pattern, err) 163 | continue 164 | } 165 | newKeys = append(newKeys, matches...) 166 | } 167 | 168 | return newKeys 169 | } 170 | 171 | func convertLocation(value string) (Location, bool) { 172 | var latitude, longitude float64 173 | var title, address string 174 | var err error 175 | values := trimElement(strings.Split(value, " ")) 176 | 177 | if len(values) < 2 { 178 | return Location{}, true 179 | } 180 | 181 | if len(values) > 2 { 182 | title = values[2] 183 | } 184 | 185 | if len(values) > 3 { 186 | title = values[2] 187 | address = values[3] 188 | } 189 | 190 | latitude, err = strconv.ParseFloat(values[0], 64) 191 | if err != nil { 192 | log.Println(err.Error()) 193 | return Location{}, true 194 | } 195 | 196 | longitude, err = strconv.ParseFloat(values[1], 64) 197 | if err != nil { 198 | log.Println(err.Error()) 199 | return Location{}, true 200 | } 201 | 202 | return Location{ 203 | Title: title, 204 | Address: address, 205 | Latitude: latitude, 206 | Longitude: longitude, 207 | }, false 208 | } 209 | 210 | func loadTextFromFile(filename string) ([]string, error) { 211 | f, err := os.Open(filename) 212 | if err != nil { 213 | return nil, err 214 | } 215 | defer f.Close() 216 | r := bufio.NewReader(f) 217 | content, err := io.ReadAll(r) 218 | if err != nil { 219 | return nil, err 220 | } 221 | return []string{string(content)}, nil 222 | } 223 | 224 | func parseTo(to []string, authorEmail string, matchEmail bool) []int64 { 225 | var emails []int64 226 | var ids []int64 227 | attachEmail := true 228 | 229 | for _, value := range trimElement(to) { 230 | idArray := trimElement(strings.Split(value, ":")) 231 | 232 | // check id 233 | id, err := strconv.ParseInt(idArray[0], 10, 64) 234 | if err != nil { 235 | continue 236 | } 237 | 238 | // check match author email 239 | if len(idArray) > 1 { 240 | if email := idArray[1]; email != authorEmail { 241 | continue 242 | } 243 | 244 | emails = append(emails, id) 245 | attachEmail = false 246 | continue 247 | } 248 | 249 | ids = append(ids, id) 250 | } 251 | 252 | if matchEmail && !attachEmail { 253 | return emails 254 | } 255 | 256 | ids = append(ids, emails...) 257 | 258 | return ids 259 | } 260 | 261 | func templateMessage(t string, plugin Plugin) (string, error) { 262 | return template.RenderTrim(t, plugin) 263 | } 264 | 265 | // Exec executes the plugin. 266 | func (p Plugin) Exec() (err error) { 267 | if len(p.Config.Token) == 0 || len(p.Config.To) == 0 { 268 | return errors.New("missing telegram token or user list") 269 | } 270 | 271 | var message []string 272 | switch { 273 | case len(p.Config.MessageFile) > 0: 274 | message, err = loadTextFromFile(p.Config.MessageFile) 275 | if err != nil { 276 | return fmt.Errorf("error loading message file '%s': %v", p.Config.MessageFile, err) 277 | } 278 | case len(p.Config.Message) > 0: 279 | message = []string{p.Config.Message} 280 | default: 281 | p.Config.Format = formatMarkdown 282 | message = p.Message() 283 | } 284 | 285 | if p.Config.TemplateVars != "" { 286 | p.Tpl = make(map[string]string) 287 | if err = json.Unmarshal([]byte(p.Config.TemplateVars), &p.Tpl); err != nil { 288 | return fmt.Errorf("unable to unmarshall template vars from JSON string '%s': %v", p.Config.TemplateVars, err) 289 | } 290 | } 291 | 292 | if p.Config.TemplateVarsFile != "" { 293 | content, err := os.ReadFile(p.Config.TemplateVarsFile) 294 | if err != nil { 295 | return fmt.Errorf("unable to read file with template vars '%s': %v", p.Config.TemplateVarsFile, err) 296 | } 297 | vars := make(map[string]string) 298 | if err = json.Unmarshal(content, &vars); err != nil { 299 | return fmt.Errorf("unable to unmarshall template vars from JSON file '%s': %v", p.Config.TemplateVarsFile, err) 300 | } 301 | // Merging templates variables from file to the variables form plugin settings (variables from file takes precedence) 302 | if p.Tpl == nil { 303 | p.Tpl = vars 304 | } else { 305 | for k, v := range vars { 306 | p.Tpl[k] = v 307 | } 308 | } 309 | } 310 | 311 | var proxyURL *url.URL 312 | if proxyURL, err = url.Parse(p.Config.Socks5); err != nil { 313 | return fmt.Errorf("unable to unmarshall socks5 proxy url from string '%s': %v", p.Config.Socks5, err) 314 | } 315 | 316 | var bot *tgbotapi.BotAPI 317 | if len(p.Config.Socks5) > 0 { 318 | proxyClient := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}} 319 | bot, err = tgbotapi.NewBotAPIWithClient(p.Config.Token, proxyClient) 320 | } else { 321 | bot, err = tgbotapi.NewBotAPI(p.Config.Token) 322 | } 323 | 324 | if err != nil { 325 | return err 326 | } 327 | 328 | bot.Debug = p.Config.Debug 329 | 330 | ids := parseTo(p.Config.To, p.Commit.Email, p.Config.MatchEmail) 331 | photos := globList(trimElement(p.Config.Photo)) 332 | documents := globList(trimElement(p.Config.Document)) 333 | stickers := globList(trimElement(p.Config.Sticker)) 334 | audios := globList(trimElement(p.Config.Audio)) 335 | voices := globList(trimElement(p.Config.Voice)) 336 | videos := globList(trimElement(p.Config.Video)) 337 | locations := trimElement(p.Config.Location) 338 | venues := trimElement(p.Config.Venue) 339 | 340 | message = trimElement(message) 341 | 342 | if p.Config.Format == formatMarkdown { 343 | message = escapeMarkdown(message) 344 | 345 | p.Commit.Message = escapeMarkdownOne(p.Commit.Message) 346 | p.Commit.Branch = escapeMarkdownOne(p.Commit.Branch) 347 | p.Commit.Link = escapeMarkdownOne(p.Commit.Link) 348 | p.Commit.Author = escapeMarkdownOne(p.Commit.Author) 349 | p.Commit.Email = escapeMarkdownOne(p.Commit.Email) 350 | 351 | p.Build.Tag = escapeMarkdownOne(p.Build.Tag) 352 | p.Build.Link = escapeMarkdownOne(p.Build.Link) 353 | p.Build.PR = escapeMarkdownOne(p.Build.PR) 354 | 355 | p.Repo.Namespace = escapeMarkdownOne(p.Repo.Namespace) 356 | p.Repo.Name = escapeMarkdownOne(p.Repo.Name) 357 | } 358 | 359 | // send message. 360 | for _, user := range ids { 361 | for _, value := range message { 362 | txt, err := templateMessage(value, p) 363 | if err != nil { 364 | return err 365 | } 366 | 367 | txt = html.UnescapeString(txt) 368 | 369 | msg := tgbotapi.NewMessage(user, txt) 370 | msg.ParseMode = p.Config.Format 371 | msg.DisableWebPagePreview = p.Config.DisableWebPagePreview 372 | msg.DisableNotification = p.Config.DisableNotification 373 | if err := p.Send(bot, msg); err != nil { 374 | return err 375 | } 376 | } 377 | 378 | for _, value := range photos { 379 | msg := tgbotapi.NewPhotoUpload(user, value) 380 | if err := p.Send(bot, msg); err != nil { 381 | return err 382 | } 383 | } 384 | 385 | for _, value := range documents { 386 | msg := tgbotapi.NewDocumentUpload(user, value) 387 | if err := p.Send(bot, msg); err != nil { 388 | return err 389 | } 390 | } 391 | 392 | for _, value := range stickers { 393 | msg := tgbotapi.NewStickerUpload(user, value) 394 | if err := p.Send(bot, msg); err != nil { 395 | return err 396 | } 397 | } 398 | 399 | for _, value := range audios { 400 | msg := tgbotapi.NewAudioUpload(user, value) 401 | msg.Title = "Audio Message." 402 | if err := p.Send(bot, msg); err != nil { 403 | return err 404 | } 405 | } 406 | 407 | for _, value := range voices { 408 | msg := tgbotapi.NewVoiceUpload(user, value) 409 | if err := p.Send(bot, msg); err != nil { 410 | return err 411 | } 412 | } 413 | 414 | for _, value := range videos { 415 | msg := tgbotapi.NewVideoUpload(user, value) 416 | msg.Caption = "Video Message" 417 | if err := p.Send(bot, msg); err != nil { 418 | return err 419 | } 420 | } 421 | 422 | for _, value := range locations { 423 | location, empty := convertLocation(value) 424 | 425 | if empty { 426 | continue 427 | } 428 | 429 | msg := tgbotapi.NewLocation(user, location.Latitude, location.Longitude) 430 | if err := p.Send(bot, msg); err != nil { 431 | return err 432 | } 433 | } 434 | 435 | for _, value := range venues { 436 | location, empty := convertLocation(value) 437 | 438 | if empty { 439 | continue 440 | } 441 | 442 | msg := tgbotapi.NewVenue(user, location.Title, location.Address, location.Latitude, location.Longitude) 443 | if err := p.Send(bot, msg); err != nil { 444 | return err 445 | } 446 | } 447 | } 448 | 449 | return nil 450 | } 451 | 452 | // Send bot message. 453 | func (p Plugin) Send(bot *tgbotapi.BotAPI, msg tgbotapi.Chattable) error { 454 | message, err := bot.Send(msg) 455 | 456 | if p.Config.Debug { 457 | log.Println("=====================") 458 | log.Printf("Response Message: %#v\n", message) 459 | log.Println("=====================") 460 | } 461 | 462 | if err == nil { 463 | return nil 464 | } 465 | 466 | return errors.New(strings.ReplaceAll(err.Error(), p.Config.Token, "")) 467 | } 468 | 469 | // Message is plugin default message. 470 | func (p Plugin) Message() []string { 471 | icon := icons[strings.ToLower(p.Build.Status)] 472 | 473 | if p.Config.GitHub { 474 | return []string{fmt.Sprintf("%s/%s triggered by %s (%s)", 475 | p.Repo.FullName, 476 | p.GitHub.Workflow, 477 | p.Repo.Namespace, 478 | p.GitHub.EventName, 479 | )} 480 | } 481 | 482 | // ✅ Build #106 of drone-telegram succeeded. 483 | // 484 | // 📝 Commit by appleboy on master: 485 | // chore: update default template 486 | // 487 | // 🌐 https://cloud.drone.io/appleboy/drone-telegram/106 488 | return []string{fmt.Sprintf("%s Build #%d of `%s` %s.\n\n📝 Commit by %s on `%s`:\n``` %s ```\n\n🌐 %s", 489 | icon, 490 | p.Build.Number, 491 | p.Repo.FullName, 492 | p.Build.Status, 493 | p.Commit.Author, 494 | p.Commit.Branch, 495 | p.Commit.Message, 496 | p.Build.Link, 497 | )} 498 | } 499 | -------------------------------------------------------------------------------- /plugin_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | "github.com/appleboy/drone-template-lib/template" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestMissingDefaultConfig(t *testing.T) { 13 | var plugin Plugin 14 | 15 | err := plugin.Exec() 16 | 17 | assert.NotNil(t, err) 18 | } 19 | 20 | func TestMissingUserConfig(t *testing.T) { 21 | plugin := Plugin{ 22 | Config: Config{ 23 | Token: "123456789", 24 | }, 25 | } 26 | 27 | err := plugin.Exec() 28 | 29 | assert.NotNil(t, err) 30 | } 31 | 32 | func TestDefaultMessageFormat(t *testing.T) { 33 | plugin := Plugin{ 34 | Repo: Repo{ 35 | FullName: "appleboy/go-hello", 36 | Name: "go-hello", 37 | Namespace: "appleboy", 38 | }, 39 | Commit: Commit{ 40 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 41 | Author: "Bo-Yi Wu", 42 | Branch: "master", 43 | Message: "update travis", 44 | }, 45 | Build: Build{ 46 | Number: 101, 47 | Status: "success", 48 | Link: "https://github.com/appleboy/go-hello", 49 | }, 50 | } 51 | 52 | message := plugin.Message() 53 | 54 | assert.Equal(t, []string{"✅ Build #101 of `appleboy/go-hello` success.\n\n📝 Commit by Bo-Yi Wu on `master`:\n``` update travis ```\n\n🌐 https://github.com/appleboy/go-hello"}, message) 55 | } 56 | 57 | func TestDefaultMessageFormatFromGitHub(t *testing.T) { 58 | plugin := Plugin{ 59 | Config: Config{ 60 | GitHub: true, 61 | }, 62 | Repo: Repo{ 63 | FullName: "appleboy/go-hello", 64 | Name: "go-hello", 65 | Namespace: "appleboy", 66 | }, 67 | GitHub: GitHub{ 68 | Workflow: "test-workflow", 69 | Action: "send notification", 70 | EventName: "push", 71 | }, 72 | } 73 | 74 | message := plugin.Message() 75 | 76 | assert.Equal(t, []string{"appleboy/go-hello/test-workflow triggered by appleboy (push)"}, message) 77 | } 78 | 79 | func TestSendMessage(t *testing.T) { 80 | plugin := Plugin{ 81 | Repo: Repo{ 82 | Name: "go-hello", 83 | Namespace: "appleboy", 84 | }, 85 | Commit: Commit{ 86 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 87 | Author: "Bo-Yi Wu", 88 | Branch: "master", 89 | Message: "update travis by drone plugin", 90 | Email: "test@gmail.com", 91 | }, 92 | Build: Build{ 93 | Tag: "1.0.0", 94 | Number: 101, 95 | Status: "success", 96 | Link: "https://github.com/appleboy/go-hello", 97 | }, 98 | 99 | Config: Config{ 100 | Token: os.Getenv("TELEGRAM_TOKEN"), 101 | To: []string{os.Getenv("TELEGRAM_TO"), os.Getenv("TELEGRAM_TO") + ":appleboy@gmail.com", "中文ID", "1234567890"}, 102 | Message: "Test Telegram Chat Bot From Travis or Local, commit message: 『{{ build.message }}』", 103 | Photo: []string{"tests/github.png", "1234", " "}, 104 | Document: []string{"tests/gophercolor.png", "1234", " "}, 105 | Sticker: []string{"tests/github-logo.png", "tests/github.png", "1234", " "}, 106 | Audio: []string{"tests/audio.mp3", "1234", " "}, 107 | Voice: []string{"tests/voice.ogg", "1234", " "}, 108 | Location: []string{"24.9163213 121.1424972", "1", " "}, 109 | Venue: []string{"35.661777 139.704051 竹北體育館 新竹縣竹北市", "24.9163213 121.1424972", "1", " "}, 110 | Video: []string{"tests/video.mp4", "1234", " "}, 111 | Debug: false, 112 | }, 113 | } 114 | 115 | err := plugin.Exec() 116 | assert.NotNil(t, err) 117 | 118 | plugin.Config.Format = formatMarkdown 119 | plugin.Config.Message = "Test escape under_score" 120 | err = plugin.Exec() 121 | assert.NotNil(t, err) 122 | 123 | // disable message 124 | plugin.Config.Message = "" 125 | err = plugin.Exec() 126 | assert.NotNil(t, err) 127 | } 128 | 129 | func TestDisableWebPagePreviewMessage(t *testing.T) { 130 | plugin := Plugin{ 131 | Config: Config{ 132 | Token: os.Getenv("TELEGRAM_TOKEN"), 133 | To: []string{os.Getenv("TELEGRAM_TO")}, 134 | DisableWebPagePreview: true, 135 | Debug: false, 136 | }, 137 | } 138 | 139 | plugin.Config.Message = "DisableWebPagePreview https://www.google.com.tw" 140 | err := plugin.Exec() 141 | assert.Nil(t, err) 142 | 143 | // disable message 144 | plugin.Config.DisableWebPagePreview = false 145 | plugin.Config.Message = "EnableWebPagePreview https://www.google.com.tw" 146 | err = plugin.Exec() 147 | assert.Nil(t, err) 148 | } 149 | 150 | func TestDisableNotificationMessage(t *testing.T) { 151 | plugin := Plugin{ 152 | Config: Config{ 153 | Token: os.Getenv("TELEGRAM_TOKEN"), 154 | To: []string{os.Getenv("TELEGRAM_TO")}, 155 | DisableNotification: true, 156 | Debug: false, 157 | }, 158 | } 159 | 160 | plugin.Config.Message = "DisableNotification https://www.google.com.tw" 161 | err := plugin.Exec() 162 | assert.Nil(t, err) 163 | 164 | // disable message 165 | plugin.Config.DisableNotification = false 166 | plugin.Config.Message = "EnableNotification https://www.google.com.tw" 167 | err = plugin.Exec() 168 | assert.Nil(t, err) 169 | } 170 | 171 | func TestBotError(t *testing.T) { 172 | plugin := Plugin{ 173 | Repo: Repo{ 174 | Name: "go-hello", 175 | Namespace: "appleboy", 176 | }, 177 | Commit: Commit{ 178 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 179 | Author: "Bo-Yi Wu", 180 | Branch: "master", 181 | Message: "update travis by drone plugin", 182 | }, 183 | Build: Build{ 184 | Number: 101, 185 | Status: "success", 186 | Link: "https://github.com/appleboy/go-hello", 187 | }, 188 | 189 | Config: Config{ 190 | Token: "appleboy", 191 | To: []string{os.Getenv("TELEGRAM_TO"), "中文ID", "1234567890"}, 192 | Message: "Test Telegram Chat Bot From Travis or Local", 193 | }, 194 | } 195 | 196 | err := plugin.Exec() 197 | assert.NotNil(t, err) 198 | } 199 | 200 | func TestTrimElement(t *testing.T) { 201 | var input, result []string 202 | 203 | input = []string{"1", " ", "3"} 204 | result = []string{"1", "3"} 205 | 206 | assert.Equal(t, result, trimElement(input)) 207 | 208 | input = []string{"1", "2"} 209 | result = []string{"1", "2"} 210 | 211 | assert.Equal(t, result, trimElement(input)) 212 | } 213 | 214 | func TestEscapeMarkdown(t *testing.T) { 215 | provider := [][][]string{ 216 | { 217 | {"user", "repo"}, 218 | {"user", "repo"}, 219 | }, 220 | { 221 | {"user_name", "repo_name"}, 222 | {`user\_name`, `repo\_name`}, 223 | }, 224 | { 225 | {"user_name_long", "user_name_long"}, 226 | {`user\_name\_long`, `user\_name\_long`}, 227 | }, 228 | { 229 | {`user\_name\_long`, `repo\_name\_long`}, 230 | {`user\_name\_long`, `repo\_name\_long`}, 231 | }, 232 | { 233 | {`user\_name\_long`, `repo\_name\_long`, ""}, 234 | {`user\_name\_long`, `repo\_name\_long`}, 235 | }, 236 | } 237 | 238 | for _, testCase := range provider { 239 | assert.Equal(t, testCase[1], escapeMarkdown(testCase[0])) 240 | } 241 | } 242 | 243 | func TestEscapeMarkdownOne(t *testing.T) { 244 | provider := [][]string{ 245 | {"user", "user"}, 246 | {"user_name", `user\_name`}, 247 | {"user_name_long", `user\_name\_long`}, 248 | {`user\_name\_escaped`, `user\_name\_escaped`}, 249 | } 250 | 251 | for _, testCase := range provider { 252 | assert.Equal(t, testCase[1], escapeMarkdownOne(testCase[0])) 253 | } 254 | } 255 | 256 | func TestParseTo(t *testing.T) { 257 | input := []string{"0", "1:1@gmail.com", "2:2@gmail.com", "3:3@gmail.com", "4", "5"} 258 | 259 | ids := parseTo(input, "1@gmail.com", false) 260 | assert.Equal(t, []int64{0, 4, 5, 1}, ids) 261 | 262 | ids = parseTo(input, "1@gmail.com", true) 263 | assert.Equal(t, []int64{1}, ids) 264 | 265 | ids = parseTo(input, "a@gmail.com", false) 266 | assert.Equal(t, []int64{0, 4, 5}, ids) 267 | 268 | ids = parseTo(input, "a@gmail.com", true) 269 | assert.Equal(t, []int64{0, 4, 5}, ids) 270 | 271 | // test empty ids 272 | ids = parseTo([]string{"", " ", " "}, "a@gmail.com", true) 273 | assert.Equal(t, 0, len(ids)) 274 | } 275 | 276 | func TestGlobList(t *testing.T) { 277 | var input []string 278 | var result []string 279 | 280 | input = []string{"tests/gophercolor.png", "測試", "3"} 281 | result = []string{"tests/gophercolor.png"} 282 | assert.Equal(t, result, globList(input)) 283 | 284 | input = []string{"tests/*.mp3"} 285 | result = []string{"tests/audio.mp3"} 286 | assert.Equal(t, result, globList(input)) 287 | } 288 | 289 | func TestConvertLocation(t *testing.T) { 290 | var input string 291 | var result Location 292 | var empty bool 293 | 294 | input = "1" 295 | result, empty = convertLocation(input) 296 | 297 | assert.Equal(t, true, empty) 298 | assert.Equal(t, Location{}, result) 299 | 300 | // strconv.ParseInt: parsing "測試": invalid syntax 301 | input = "測試 139.704051" 302 | result, empty = convertLocation(input) 303 | 304 | assert.Equal(t, true, empty) 305 | assert.Equal(t, Location{}, result) 306 | 307 | // strconv.ParseInt: parsing "測試": invalid syntax 308 | input = "35.661777 測試" 309 | result, empty = convertLocation(input) 310 | 311 | assert.Equal(t, true, empty) 312 | assert.Equal(t, Location{}, result) 313 | 314 | input = "35.661777 139.704051" 315 | result, empty = convertLocation(input) 316 | 317 | assert.Equal(t, false, empty) 318 | assert.Equal(t, Location{ 319 | Latitude: float64(35.661777), 320 | Longitude: float64(139.704051), 321 | }, result) 322 | 323 | input = "35.661777 139.704051 title" 324 | result, empty = convertLocation(input) 325 | 326 | assert.Equal(t, false, empty) 327 | assert.Equal(t, Location{ 328 | Title: "title", 329 | Address: "", 330 | Latitude: float64(35.661777), 331 | Longitude: float64(139.704051), 332 | }, result) 333 | 334 | input = "35.661777 139.704051 title address" 335 | result, empty = convertLocation(input) 336 | 337 | assert.Equal(t, false, empty) 338 | assert.Equal(t, Location{ 339 | Title: "title", 340 | Address: "address", 341 | Latitude: float64(35.661777), 342 | Longitude: float64(139.704051), 343 | }, result) 344 | } 345 | 346 | func TestHTMLMessage(t *testing.T) { 347 | plugin := Plugin{ 348 | Repo: Repo{ 349 | Name: "go-hello", 350 | Namespace: "appleboy", 351 | }, 352 | Commit: Commit{ 353 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 354 | Author: "Bo-Yi Wu", 355 | Branch: "master", 356 | Message: "test", 357 | }, 358 | Build: Build{ 359 | Number: 101, 360 | Status: "success", 361 | Link: "https://github.com/appleboy/go-hello", 362 | }, 363 | 364 | Config: Config{ 365 | Token: os.Getenv("TELEGRAM_TOKEN"), 366 | To: []string{os.Getenv("TELEGRAM_TO")}, 367 | Message: ` 368 | Test HTML Format 369 | Google .com 1 370 | Google .com 2 371 | Google .com 3 372 | `, 373 | Format: formatHTML, 374 | }, 375 | } 376 | 377 | assert.Nil(t, plugin.Exec()) 378 | 379 | plugin.Config.MessageFile = "tests/message_html.txt" 380 | assert.Nil(t, plugin.Exec()) 381 | } 382 | 383 | func TestMessageFile(t *testing.T) { 384 | plugin := Plugin{ 385 | Repo: Repo{ 386 | Name: "go-hello", 387 | Namespace: "appleboy", 388 | }, 389 | Commit: Commit{ 390 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 391 | Author: "Bo-Yi Wu", 392 | Branch: "master", 393 | Message: "Freakin' macOS isn't fully case-sensitive..", 394 | }, 395 | Build: Build{ 396 | Number: 101, 397 | Status: "success", 398 | Link: "https://github.com/appleboy/go-hello", 399 | Started: time.Now().Unix(), 400 | Finished: time.Now().Add(180 * time.Second).Unix(), 401 | }, 402 | 403 | Config: Config{ 404 | Token: os.Getenv("TELEGRAM_TOKEN"), 405 | To: []string{os.Getenv("TELEGRAM_TO")}, 406 | MessageFile: "tests/message.txt", 407 | }, 408 | } 409 | 410 | err := plugin.Exec() 411 | assert.Nil(t, err) 412 | } 413 | 414 | func TestTemplateVars(t *testing.T) { 415 | plugin := Plugin{ 416 | Repo: Repo{ 417 | Name: "go-hello", 418 | Namespace: "appleboy", 419 | }, 420 | Commit: Commit{ 421 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 422 | Author: "Bo-Yi Wu", 423 | Branch: "master", 424 | Message: "This is a test commit msg", 425 | }, 426 | Build: Build{ 427 | Number: 101, 428 | Status: "success", 429 | Link: "https://github.com/appleboy/go-hello", 430 | Started: time.Now().Unix(), 431 | Finished: time.Now().Add(180 * time.Second).Unix(), 432 | }, 433 | 434 | Config: Config{ 435 | Token: os.Getenv("TELEGRAM_TOKEN"), 436 | To: []string{os.Getenv("TELEGRAM_TO")}, 437 | Format: formatMarkdown, 438 | MessageFile: "tests/message_template.txt", 439 | TemplateVars: `{"env":"testing","version":"1.2.0-SNAPSHOT"}`, 440 | }, 441 | } 442 | 443 | err := plugin.Exec() 444 | assert.Nil(t, err) 445 | } 446 | 447 | func TestTemplateVarsFile(t *testing.T) { 448 | plugin := Plugin{ 449 | Repo: Repo{ 450 | Name: "go-hello", 451 | Namespace: "appleboy", 452 | }, 453 | Commit: Commit{ 454 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 455 | Author: "Bo-Yi Wu", 456 | Branch: "master", 457 | Message: "This is a test commit msg", 458 | }, 459 | Build: Build{ 460 | Number: 101, 461 | Status: "success", 462 | Link: "https://github.com/appleboy/go-hello", 463 | }, 464 | 465 | Config: Config{ 466 | Token: os.Getenv("TELEGRAM_TOKEN"), 467 | To: []string{os.Getenv("TELEGRAM_TO")}, 468 | Format: formatMarkdown, 469 | MessageFile: "tests/message_template.txt", 470 | TemplateVarsFile: "tests/vars.json", 471 | }, 472 | } 473 | 474 | err := plugin.Exec() 475 | assert.Nil(t, err) 476 | } 477 | 478 | func TestProxySendMessage(t *testing.T) { 479 | plugin := Plugin{ 480 | Repo: Repo{ 481 | Name: "go-hello", 482 | Namespace: "appleboy", 483 | }, 484 | Commit: Commit{ 485 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 486 | Author: "Bo-Yi Wu", 487 | Branch: "master", 488 | Message: "start use proxy", 489 | Email: "test@gmail.com", 490 | }, 491 | Build: Build{ 492 | Tag: "1.0.0", 493 | Number: 101, 494 | Status: "success", 495 | Link: "https://github.com/appleboy/go-hello", 496 | }, 497 | 498 | Config: Config{ 499 | Token: os.Getenv("TELEGRAM_TOKEN"), 500 | To: []string{os.Getenv("TELEGRAM_TO")}, 501 | Message: "Send message from socks5 proxy URL.", 502 | Debug: false, 503 | Socks5: os.Getenv("SOCKS5"), 504 | }, 505 | } 506 | 507 | err := plugin.Exec() 508 | assert.Nil(t, err) 509 | } 510 | 511 | func TestBuildTemplate(t *testing.T) { 512 | plugin := Plugin{ 513 | Commit: Commit{ 514 | Sha: "e7c4f0a63ceeb42a39ac7806f7b51f3f0d204fd2", 515 | Author: "Bo-Yi Wu", 516 | Branch: "master", 517 | Message: "This is a test commit msg", 518 | }, 519 | Build: Build{ 520 | Number: 101, 521 | Status: "success", 522 | Link: "https://github.com/appleboy/go-hello", 523 | Started: time.Now().Unix(), 524 | Finished: time.Now().Add(180 * time.Second).Unix(), 525 | }, 526 | } 527 | 528 | _, err := template.RenderTrim( 529 | ` 530 | Sample message loaded from file. 531 | 532 | Commit msg: {{uppercasefirst commit.message}} 533 | 534 | duration: {{duration build.started build.finished}} 535 | `, plugin) 536 | assert.Nil(t, err) 537 | } 538 | --------------------------------------------------------------------------------