├── .gitignore ├── requirements.txt ├── .github ├── FUNDING.yml └── workflows │ ├── docker-image.yml │ └── codeql-analysis.yml ├── docker ├── Dockerfile └── run.sh ├── flaskAlert.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | flaskAlert.log 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | python-telegram-bot 3 | Flask-BasicAuth 4 | gunicorn 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: carlosmalucelli 4 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build the Docker image 18 | run: docker build . --file docker/Dockerfile --tag my-image-name:$(date +%s) 19 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | 3 | LABEL maintainer="Carlos Augusto Malucelli " 4 | 5 | WORKDIR /alertmanager-webhook-telegram 6 | 7 | RUN apk update \ 8 | && apk add py-pip bash gcc python-dev musl-dev libffi-dev openssl-dev unzip \ 9 | && rm -rf /var/cache/apk/* \ 10 | && wget https://github.com/nopp/alertmanager-webhook-telegram-python/archive/master.zip \ 11 | && unzip -j master.zip \ 12 | && pip install -r requirements.txt \ 13 | && chmod +x run.sh 14 | 15 | EXPOSE 9119 16 | 17 | ENTRYPOINT ["./run.sh"] 18 | -------------------------------------------------------------------------------- /docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "x" == "x$username" ]; then 4 | echo "warning: username is not set" 5 | else 6 | echo "admin user is $username" 7 | fi 8 | 9 | if [ "x" == "x$password" ]; then 10 | echo "warning: password is not set" 11 | else 12 | echo "password is set (not visible)" 13 | fi 14 | 15 | if [ "x" == "x$bottoken" ]; then 16 | echo "FAIL: bottoken is not set" 17 | exit 1 18 | else 19 | echo "bottoken is set (not visible)" 20 | fi 21 | 22 | if [ "x" == "x$chatid" ]; then 23 | echo "FAIL: chatid is not set" 24 | exit 2 25 | else 26 | echo "chatid is $chatid" 27 | fi 28 | 29 | sed -i s/botToken/"$bottoken"/ flaskAlert.py 30 | sed -i s/xchatIDx/"$chatid"/ flaskAlert.py 31 | sed -i s/XXXUSERNAME/"$username"/ flaskAlert.py 32 | sed -i s/XXXPASSWORD/"$password"/ flaskAlert.py 33 | 34 | /usr/bin/gunicorn -w 4 -b 0.0.0.0:9119 flaskAlert:app 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Code scanning - action" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 5 * * 2' 8 | 9 | jobs: 10 | CodeQL-Build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | with: 18 | # We must fetch at least the immediate parents so that if this is 19 | # a pull request then we can checkout the head. 20 | fetch-depth: 2 21 | 22 | # If this run was triggered by a pull request event, then checkout 23 | # the head of the pull request instead of the merge commit. 24 | - run: git checkout HEAD^2 25 | if: ${{ github.event_name == 'pull_request' }} 26 | 27 | # Initializes the CodeQL tools for scanning. 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v1 30 | # Override language selection by uncommenting this and choosing your languages 31 | # with: 32 | # languages: go, javascript, csharp, python, cpp, java 33 | 34 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 35 | # If this step fails, then you should remove it and run the build manually (see below) 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v1 38 | 39 | # ℹ️ Command-line programs to run using the OS shell. 40 | # 📚 https://git.io/JvXDl 41 | 42 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 43 | # and modify them (or add more) to build your code if your project 44 | # uses a compiled language 45 | 46 | #- run: | 47 | # make bootstrap 48 | # make release 49 | 50 | - name: Perform CodeQL Analysis 51 | uses: github/codeql-action/analyze@v1 52 | -------------------------------------------------------------------------------- /flaskAlert.py: -------------------------------------------------------------------------------- 1 | import telegram, json, logging 2 | from dateutil import parser 3 | from flask import Flask 4 | from flask import request 5 | from flask_basicauth import BasicAuth 6 | 7 | app = Flask(__name__) 8 | app.secret_key = 'changeKeyHeere' 9 | basic_auth = BasicAuth(app) 10 | 11 | # Yes need to have -, change it! 12 | chatID = "-xchatIDx" 13 | 14 | # Authentication conf, change it! 15 | app.config['BASIC_AUTH_FORCE'] = True 16 | app.config['BASIC_AUTH_USERNAME'] = 'XXXUSERNAME' 17 | app.config['BASIC_AUTH_PASSWORD'] = 'XXXPASSWORD' 18 | 19 | # Bot token, change it! 20 | bot = telegram.Bot(token="botToken") 21 | 22 | @app.route('/alert', methods = ['POST']) 23 | def postAlertmanager(): 24 | 25 | try: 26 | content = json.loads(request.get_data()) 27 | for alert in content['alerts']: 28 | message = "Status: "+alert['status']+"\n" 29 | if 'name' in alert['labels']: 30 | message += "Instance: "+alert['labels']['instance']+"("+alert['labels']['name']+")\n" 31 | else: 32 | message += "Instance: "+alert['labels']['instance']+"\n" 33 | if 'info' in alert['annotations']: 34 | message += "Info: "+alert['annotations']['info']+"\n" 35 | if 'summary' in alert['annotations']: 36 | message += "Summary: "+alert['annotations']['summary']+"\n" 37 | if 'description' in alert['annotations']: 38 | message += "Description: "+alert['annotations']['description']+"\n" 39 | if alert['status'] == "resolved": 40 | correctDate = parser.parse(alert['endsAt']).strftime('%Y-%m-%d %H:%M:%S') 41 | message += "Resolved: "+correctDate 42 | elif alert['status'] == "firing": 43 | correctDate = parser.parse(alert['startsAt']).strftime('%Y-%m-%d %H:%M:%S') 44 | message += "Started: "+correctDate 45 | bot.sendMessage(chat_id=chatID, text=message) 46 | return "Alert OK", 200 47 | except RetryAfter: 48 | sleep(30) 49 | bot.sendMessage(chat_id=chatID, text=message) 50 | return "Alert OK", 200 51 | except TimedOut as e: 52 | sleep(60) 53 | bot.sendMessage(chat_id=chatID, text=message) 54 | return "Alert OK", 200 55 | except NetworkError as e: 56 | sleep(60) 57 | bot.sendMessage(chat_id=chatID, text=message) 58 | return "Alert OK", 200 59 | except Exception as error: 60 | bot.sendMessage(chat_id=chatID, text="Error: "+str(error)) 61 | app.logger.info("\t%s",error) 62 | return "Alert fail", 200 63 | 64 | if __name__ == '__main__': 65 | logging.basicConfig(level=logging.INFO) 66 | app.run(host='0.0.0.0', port=9119) 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alertmanager webhook for Telegram (Python Version) 2 | 3 | ![Docker Image CI](https://github.com/nopp/alertmanager-webhook-telegram-python/workflows/Docker%20Image%20CI/badge.svg) 4 | ![Code scanning - action](https://github.com/nopp/alertmanager-webhook-telegram-python/workflows/Code%20scanning%20-%20action/badge.svg) 5 | 6 | GO Version (https://github.com/nopp/alertmanager-webhook-telegram-go) 7 | 8 | Python version 3 9 | 10 | ## INSTALL 11 | 12 | * pip install -r requirements.txt 13 | 14 | Change on flaskAlert.py 15 | ======================= 16 | * botToken 17 | * chatID 18 | 19 | If you'll use with authentication, change too 20 | 21 | * XXXUSERNAME 22 | * XXXPASSWORD 23 | 24 | Disabling authentication 25 | ======================== 26 | On flaskAlert.py change app.config['BASIC_AUTH_FORCE'] = True to app.config['BASIC_AUTH_FORCE'] = False 27 | 28 | Alertmanager configuration example 29 | ================================== 30 | 31 | receivers: 32 | - name: 'telegram-webhook' 33 | webhook_configs: 34 | - url: http://ipFlaskAlert:9119/alert 35 | send_resolved: true 36 | http_config: 37 | basic_auth: 38 | username: 'admin' 39 | password: 'password' 40 | 41 | One way to get the chat ID 42 | ========================== 43 | 1) Access https://web.telegram.org/ 44 | 2) Click to specific chat to the left 45 | 3) At the url, you can get the chat ID(Ex: https://web.telegram.org/#/im?p=g1234567, so the chatID is 1234567) 46 | 47 | Running 48 | ======= 49 | * python flaskAlert.py 50 | 51 | Running on docker 52 | ================= 53 | git clone https://github.com/nopp/alertmanager-webhook-telegram.git 54 | cd alertmanager-webhook-telegram/docker/ 55 | docker build -t alertmanager-webhook-telegram:1.0 . 56 | 57 | docker run -d --name telegram-bot \ 58 | -e "bottoken=telegramBotToken" \ 59 | -e "chatid=telegramChatID" \ 60 | -e "username=" \ 61 | -e "password=" \ 62 | -p 9119:9119 alertmanager-webhook-telegram:1.0 63 | 64 | Example to test 65 | =============== 66 | curl -XPOST --data '{"status":"resolved","groupLabels":{"alertname":"instance_down"},"commonAnnotations":{"description":"i-0d7188fkl90bac100 of job ec2-sp-node_exporter has been down for more than 2 minutes.","summary":"Instance i-0d7188fkl90bac100 down"},"alerts":[{"status":"resolved","labels":{"name":"olokinho01-prod","instance":"i-0d7188fkl90bac100","job":"ec2-sp-node_exporter","alertname":"instance_down","os":"linux","severity":"page"},"endsAt":"2019-07-01T16:16:19.376244942-03:00","generatorURL":"http://pmts.io:9090","startsAt":"2019-07-01T16:02:19.376245319-03:00","annotations":{"description":"i-0d7188fkl90bac100 of job ec2-sp-node_exporter has been down for more than 2 minutes.","summary":"Instance i-0d7188fkl90bac100 down"}}],"version":"4","receiver":"infra-alert","externalURL":"http://alm.io:9093","commonLabels":{"name":"olokinho01-prod","instance":"i-0d7188fkl90bac100","job":"ec2-sp-node_exporter","alertname":"instance_down","os":"linux","severity":"page"}}' http://username:password@flaskAlert:9119/alert 67 | --------------------------------------------------------------------------------