├── README.md
├── img
└── telegram_py.png
└── telegram.py
/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Callback plugin for Telegram
2 | Used for your ansible playbook notification delivery
3 | Its highly recommended to use socks5 proxy to bypass RKN's restrictions but its not required (installed tor with socks5 proxy are perfect)
4 |
5 | ## Requiremets
6 | This plugin requires python libs:
7 | - pyTelegramBotApi
8 | - prettytable
9 |
10 | ## Install
11 | 1. Install python libraries and upgrade requests lib to latest
12 |
13 | ```sh
14 | $ pip install pyTelegramBotApi
15 | $ pip install prettytable
16 | $ pip install requests --upgrade
17 | ```
18 |
19 | 2. Download plugin and put it to ansible
20 |
21 | ```sh
22 | $ cd /path/to/your/ansible/plugins/callback
23 | $ curl -O https://raw.githubusercontent.com/dfwmlb/ansible-callback-telegram/master/telegram.py
24 | ```
25 |
26 | 3. Add configuration to your ansible.cfg
27 |
28 | ```sh
29 | callback_whitelist = telegram
30 |
31 | [callback_telegram]
32 | tg_token = ENTER_TOKEN
33 | tg_chat_id = ENTER_CHAT_ID
34 | socks5_uri = socks5://localhost:9050
35 | ```
36 |
37 | ## Screens
38 |
39 |
40 |
--------------------------------------------------------------------------------
/img/telegram_py.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfwmlb/ansible-callback-telegram/f74330b2f6a5a3b0992d698347054d0cc585d8d3/img/telegram_py.png
--------------------------------------------------------------------------------
/telegram.py:
--------------------------------------------------------------------------------
1 | from __future__ import (absolute_import, division, print_function)
2 | __metaclass__ = type
3 |
4 | DOCUMENTATION = '''
5 | callback: telegram
6 | callback_type: notification
7 | requirements:
8 | - whitelist in configuration
9 | - telebot (pip install pyTelegramBotApi)
10 | - prettytable (pip install prettytable)
11 | - latest requests (pip install requests --upgrade)
12 | short_description: Sends play events to a telegram channel
13 | version_added: "2.1"
14 | description:
15 | - This is an ansible callback plugin that sends status updates to a telegram channel during playbook execution.
16 | - Before 2.4 only environment variables were available for configuring this plugin
17 | options:
18 | tg_token:
19 | required: True
20 | description: telegram bot token
21 | env:
22 | - name: TG_TOKEN
23 | ini:
24 | - section: callback_telegram
25 | key: tg_token
26 | tg_chat_id:
27 | required: True
28 | description: telegram chat id to post in.
29 | env:
30 | - name: TG_CHAT_ID
31 | ini:
32 | - section: callback_telegram
33 | key: tg_chat_id
34 | socks5_uri:
35 | description: socks5 proxy uri to bypass rkn's restarictions
36 | env:
37 | - name: SOCKS5_URI
38 | ini:
39 | - section: callback_telegram
40 | key: socks5_uri
41 | '''
42 |
43 | import os
44 | from datetime import datetime
45 |
46 | from ansible import context
47 | from ansible.module_utils._text import to_text
48 | from ansible.module_utils.urls import open_url
49 | from ansible.plugins.callback import CallbackBase
50 |
51 | try:
52 | import telebot
53 | from telebot import apihelper
54 | HAS_TELEBOT = True
55 | except ImportError:
56 | HAS_TELEBOT = False
57 |
58 | try:
59 | import prettytable
60 | HAS_PRETTYTABLE = True
61 | except ImportError:
62 | HAS_PRETTYTABLE = False
63 |
64 | class CallbackModule(CallbackBase):
65 | """This is an ansible callback plugin that sends status
66 | updates to a telegram channel during playbook execution.
67 | """
68 | CALLBACK_VERSION = 2.0
69 | CALLBACK_TYPE = 'notification'
70 | CALLBACK_NAME = 'telegram'
71 | CALLBACK_NEEDS_WHITELIST = True
72 |
73 | def __init__(self, display=None):
74 |
75 | super(CallbackModule, self).__init__(display=display)
76 |
77 | if not HAS_TELEBOT:
78 | self.disabled = True
79 | self._display.warning('The `telebot` python module is not '
80 | 'installed. Disabling the Slack callback '
81 | 'plugin.')
82 |
83 | if not HAS_PRETTYTABLE:
84 | self.disabled = True
85 | self._display.warning('The `prettytable` python module is not '
86 | 'installed. Disabling the Slack callback '
87 | 'plugin.')
88 |
89 | self.playbook_name = None
90 | self.play = None
91 | self.now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
92 |
93 | def set_options(self, task_keys=None, var_options=None, direct=None):
94 |
95 | super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
96 |
97 | self.tg_token = self.get_option('tg_token')
98 | self.tg_chat_id = self.get_option('tg_chat_id')
99 | self.socks5_uri = self.get_option('socks5_uri')
100 |
101 | if self.tg_token is None:
102 | self.disabled = True
103 | self._display.warning('tg_token was not provided. The '
104 | 'tg_token can be provided using '
105 | 'the `TG_TOKEN` environment '
106 | 'variable.')
107 |
108 | if self.tg_chat_id is None:
109 | self.disabled = True
110 | self._display.warning('tg_chat_id was not provided. The '
111 | 'tg_chat_id can be provided using '
112 | 'the `TG_CHAT_ID` environment '
113 | 'variable.')
114 |
115 | def send_msg(self, msg):
116 | if self.socks5_uri is not None:
117 | apihelper.proxy = {'https': self.socks5_uri}
118 | bot = telebot.TeleBot(self.tg_token)
119 | bot.send_message(self.tg_chat_id, msg, parse_mode='HTML')
120 |
121 | def v2_playbook_on_start(self, playbook):
122 |
123 | self.playbook_name = os.path.abspath(playbook._file_name)
124 |
125 | def v2_playbook_on_play_start(self, play):
126 | self.play = play
127 |
128 | title = [
129 | 'Ansible: STARTED ⚙️'
130 | ]
131 |
132 | msg_items = [' '.join(title)]
133 | msg_items.append('\n time: ' + '' + str(self.now) + '
')
134 | msg_items.append('playbook: ' + '' + self.playbook_name + '
')
135 | msg_items.append(' hosts:')
136 | for host in play.hosts:
137 | msg_items.append(' - ' + host + '
')
138 | msg_items.append(' tags:')
139 | for tag in play.only_tags:
140 | msg_items.append(' - ' + tag + '
')
141 | msg = '\n'.join(msg_items)
142 | self.send_msg(msg=msg)
143 |
144 | def v2_runner_on_failed(self, result, ignore_errors=False):
145 |
146 | msg = []
147 | title = [
148 | 'Ansible: FAILED ❌'
149 | ]
150 | msg_items = [' '.join(title)]
151 | msg_items.append('\n time: ' + '' + str(self.now) + '
')
152 | msg_items.append('playbook: ' + '' + self.playbook_name + '
')
153 | msg_items.append(' host: ' + '' + result._host.get_name() + '
')
154 | msg_items.append(' stderr: ' + '' + result._result['stderr'] + '
')
155 |
156 | msg = '\n'.join(msg_items)
157 |
158 | self.send_msg(msg=msg)
159 |
160 | def v2_playbook_on_stats(self, stats):
161 | """Display info about playbook statistics"""
162 |
163 | hosts = sorted(stats.processed.keys())
164 |
165 | t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',
166 | 'Failures', 'Rescued', 'Ignored'])
167 |
168 | failures = False
169 | unreachable = False
170 |
171 | for h in hosts:
172 | s = stats.summarize(h)
173 |
174 | if s['failures'] > 0:
175 | failures = True
176 | if s['unreachable'] > 0:
177 | unreachable = True
178 |
179 | t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',
180 | 'failures', 'rescued', 'ignored']])
181 |
182 | msg = []
183 | title = 'Ansible: ENDED'
184 | if failures or unreachable:
185 | msg_items = [
186 | title + ' ❌'
187 | ]
188 | else:
189 | msg_items = [
190 | title + ' ✅'
191 | ]
192 | msg_items.append('\n time: ' + '' + str(self.now) + '
')
193 | msg_items.append('playbook: ' + '' + self.playbook_name + '
')
194 | msg_items.append('\n%s\n
' % t)
195 |
196 | msg = '\n'.join(msg_items)
197 |
198 | self.send_msg(msg=msg)
--------------------------------------------------------------------------------