1 |
jpp |
1.1 |
diff -Nur --no-dereference smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/cert_expire.pl smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/cert_expire.pl |
2 |
|
|
--- smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/cert_expire.pl 1969-12-31 19:00:00.000000000 -0500 |
3 |
|
|
+++ smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/cert_expire.pl 2021-11-08 22:00:50.335000000 -0500 |
4 |
|
|
@@ -0,0 +1,139 @@ |
5 |
|
|
+#!/usr/bin/perl -w |
6 |
|
|
+# Check peer certificate validity for Zabbix |
7 |
|
|
+# Require perl module : IO::Socket, Net::SSLeay, Date::Parse |
8 |
|
|
+# Require unix programs : openssl, echo, sendmail |
9 |
|
|
+# |
10 |
|
|
+# Based on sslexpire from Emmanuel Lacour <elacour@home-dn.net> |
11 |
|
|
+# |
12 |
|
|
+# This file is free software; you can redistribute it and/or modify it |
13 |
|
|
+# under the terms of the GNU General Public License as published by the |
14 |
|
|
+# Free Software Foundation; either version 2, or (at your option) any |
15 |
|
|
+# later version. |
16 |
|
|
+# |
17 |
|
|
+# This file is distributed in the hope that it will be |
18 |
|
|
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
19 |
|
|
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
20 |
|
|
+# General Public License for more details. |
21 |
|
|
+# |
22 |
|
|
+# You should have received a copy of the GNU General Public License |
23 |
|
|
+# along with this file; see the file COPYING. If not, write to the Free |
24 |
|
|
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
25 |
|
|
+# 02110-1301, USA. |
26 |
|
|
+# |
27 |
|
|
+ |
28 |
|
|
+ |
29 |
|
|
+use strict; |
30 |
|
|
+use IO::Socket; |
31 |
|
|
+use Net::SSLeay; |
32 |
|
|
+use Getopt::Long; |
33 |
|
|
+use Date::Parse; |
34 |
|
|
+ |
35 |
|
|
+Net::SSLeay::SSLeay_add_ssl_algorithms(); |
36 |
|
|
+Net::SSLeay::randomize(); |
37 |
|
|
+ |
38 |
|
|
+# Default values |
39 |
|
|
+my $opensslpath = "/usr/bin/openssl"; |
40 |
|
|
+my $host = '127.0.0.1'; |
41 |
|
|
+my $port = '443'; |
42 |
|
|
+ |
43 |
|
|
+my %opts; |
44 |
|
|
+GetOptions (\%opts, |
45 |
|
|
+ 'host|h=s', |
46 |
|
|
+ 'port|p=s', |
47 |
|
|
+ 'help', |
48 |
|
|
+); |
49 |
|
|
+ |
50 |
|
|
+if ($opts{'host'}) { |
51 |
|
|
+ $host = $opts{'host'}; |
52 |
|
|
+} |
53 |
|
|
+if ($opts{'port'}){ |
54 |
|
|
+ $port = $opts{'port'}; |
55 |
|
|
+} |
56 |
|
|
+ |
57 |
|
|
+if ($opts{'help'}) { |
58 |
|
|
+ &usage; |
59 |
|
|
+} |
60 |
|
|
+ |
61 |
|
|
+# Print program usage |
62 |
|
|
+sub usage { |
63 |
|
|
+ print "Usage: sslexpire [OPTION]... |
64 |
|
|
+-h, --host=HOST check this host |
65 |
|
|
+-p, --port=TCPPORT check this port on the previous host |
66 |
|
|
+ --help print this help, then exit |
67 |
|
|
+"; |
68 |
|
|
+ exit; |
69 |
|
|
+} |
70 |
|
|
+ |
71 |
|
|
+# This will return the expiration date |
72 |
|
|
+sub getExpire { |
73 |
|
|
+ |
74 |
|
|
+ my ($l_host,$l_port) = @_; |
75 |
|
|
+ my ($l_expdate,$l_comment); |
76 |
|
|
+ |
77 |
|
|
+ # Connect to $l_host:$l_port |
78 |
|
|
+ my $socket = IO::Socket::INET->new( |
79 |
|
|
+ Proto => "tcp", |
80 |
|
|
+ PeerAddr => $l_host, |
81 |
|
|
+ PeerPort => $l_port |
82 |
|
|
+ ); |
83 |
|
|
+ # If we connected successfully |
84 |
|
|
+ if ($socket) { |
85 |
|
|
+ # Intiate ssl |
86 |
|
|
+ my $l_ctx = Net::SSLeay::CTX_new(); |
87 |
|
|
+ my $l_ssl = Net::SSLeay::new($l_ctx); |
88 |
|
|
+ |
89 |
|
|
+ Net::SSLeay::set_fd($l_ssl, fileno($socket)); |
90 |
|
|
+ my $res = Net::SSLeay::connect($l_ssl); |
91 |
|
|
+ |
92 |
|
|
+ # Get peer certificate |
93 |
|
|
+ my $l_x509 = Net::SSLeay::get_peer_certificate($l_ssl); |
94 |
|
|
+ if ($l_x509) { |
95 |
|
|
+ my $l_string = Net::SSLeay::PEM_get_string_X509($l_x509); |
96 |
|
|
+ # Get the expiration date, using openssl |
97 |
|
|
+ $l_expdate = `echo "$l_string" | $opensslpath x509 -enddate -noout 2>&1`; |
98 |
|
|
+ $l_expdate =~ s/.*=//; |
99 |
|
|
+ chomp($l_expdate); |
100 |
|
|
+ } |
101 |
|
|
+ else { |
102 |
|
|
+ $l_expdate = 1; |
103 |
|
|
+ } |
104 |
|
|
+ |
105 |
|
|
+ # Close and cleanup |
106 |
|
|
+ Net::SSLeay::free($l_ssl); |
107 |
|
|
+ Net::SSLeay::CTX_free($l_ctx); |
108 |
|
|
+ close $socket; |
109 |
|
|
+ } |
110 |
|
|
+ else { |
111 |
|
|
+ $l_expdate = 1; |
112 |
|
|
+ } |
113 |
|
|
+ return $l_expdate; |
114 |
|
|
+} |
115 |
|
|
+ |
116 |
|
|
+ |
117 |
|
|
+# Print remaining days before expiration |
118 |
|
|
+sub report { |
119 |
|
|
+ # Convert date into epoch using date command |
120 |
|
|
+ my ($l_expdate) = @_; |
121 |
|
|
+ |
122 |
|
|
+ if ($l_expdate ne "1") { |
123 |
|
|
+ # The current date |
124 |
|
|
+ my $l_today = time; |
125 |
|
|
+ my $l_epochdate = str2time($l_expdate); |
126 |
|
|
+ |
127 |
|
|
+ # Calculate diff between expiration date and today |
128 |
|
|
+ my $l_diff = ($l_epochdate - $l_today)/(3600*24); |
129 |
|
|
+ |
130 |
|
|
+ # Report if needed |
131 |
|
|
+ printf "%.0f\n", $l_diff; |
132 |
|
|
+ } |
133 |
|
|
+ else { |
134 |
|
|
+ print "Unable to read certificate!\n"; |
135 |
|
|
+ exit (1); |
136 |
|
|
+ } |
137 |
|
|
+} |
138 |
|
|
+ |
139 |
|
|
+# Get expiration date |
140 |
|
|
+my $expdate = getExpire($host,$port); |
141 |
|
|
+ |
142 |
|
|
+# Report |
143 |
|
|
+report("$expdate"); |
144 |
|
|
diff -Nur --no-dereference smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/check_cert.pl smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/check_cert.pl |
145 |
|
|
--- smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/check_cert.pl 1969-12-31 19:00:00.000000000 -0500 |
146 |
|
|
+++ smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/check_cert.pl 2021-11-08 22:00:50.719000000 -0500 |
147 |
|
|
@@ -0,0 +1,109 @@ |
148 |
|
|
+#!/usr/bin/perl |
149 |
|
|
+ |
150 |
|
|
+=head1 SYNOPSIS |
151 |
|
|
+ |
152 |
|
|
+ check_ssl_certificate.pl |
153 |
|
|
+ --url,-u URL |
154 |
|
|
+ --sni,-s HOSTNAME SNI servername (SSL vhost) that will be requested during SSL handshake. |
155 |
|
|
+ This tells the server which certificate to return. |
156 |
|
|
+ Default to the host passed with --url |
157 |
|
|
+ |
158 |
|
|
+=cut |
159 |
|
|
+ |
160 |
|
|
+use strict; |
161 |
|
|
+use warnings; |
162 |
|
|
+use IO::Socket::SSL; |
163 |
|
|
+use LWP::UserAgent; |
164 |
|
|
+use URI::URL; |
165 |
|
|
+use DateTime::Format::ISO8601; |
166 |
|
|
+use Getopt::Long qw/:config auto_help/; |
167 |
|
|
+use Pod::Usage; |
168 |
|
|
+use JSON qw(to_json); |
169 |
|
|
+ |
170 |
|
|
+use constant TIMEOUT => 10; |
171 |
|
|
+ |
172 |
|
|
+my ($url, $sni, $status, @san); |
173 |
|
|
+ |
174 |
|
|
+sub ssl_opts { |
175 |
|
|
+ my ($sni, $expiration_date_ref, $status_ref, $san_ref) = @_; |
176 |
|
|
+ return ( |
177 |
|
|
+ 'verify_hostname' => 0, |
178 |
|
|
+ 'SSL_ca_file' => '/etc/pki/tls/certs/ca-bundle.crt', |
179 |
|
|
+ 'SSL_hostname' => $sni, |
180 |
|
|
+ 'SSL_verifycn_name' => $sni, |
181 |
|
|
+ 'SSL_verify_scheme' => 'http', |
182 |
|
|
+ 'SSL_verify_callback' => sub { |
183 |
|
|
+ my (undef, $ctx_store) = @_; |
184 |
|
|
+ # Get the error message from openssl verification |
185 |
|
|
+ $$status_ref = Net::SSLeay::X509_verify_cert_error_string(Net::SSLeay::X509_STORE_CTX_get_error($ctx_store)); |
186 |
|
|
+ # Get the raw cert, to extract the expiration |
187 |
|
|
+ my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($ctx_store); |
188 |
|
|
+ $$expiration_date_ref = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notAfter($cert)); |
189 |
|
|
+ # Get Alt names so we can check later if the hostname match |
190 |
|
|
+ @$san_ref = Net::SSLeay::X509_get_subjectAltNames($cert); |
191 |
|
|
+ # Keep only odd elements. Even ones contains subject types which we're not interested in |
192 |
|
|
+ @$san_ref = @$san_ref[grep $_ % 2, 0..scalar(@$san_ref)]; |
193 |
|
|
+ # Always return success |
194 |
|
|
+ return 1; |
195 |
|
|
+ } |
196 |
|
|
+ ) |
197 |
|
|
+} |
198 |
|
|
+ |
199 |
|
|
+sub https_get { |
200 |
|
|
+ my ($url, $sni, $expiration_date_ref, $status_ref, $san_ref) = @_; |
201 |
|
|
+ |
202 |
|
|
+ my $ua = LWP::UserAgent->new(); |
203 |
|
|
+ $ua->timeout(TIMEOUT); |
204 |
|
|
+ $ua->ssl_opts( ssl_opts($sni, $expiration_date_ref, $status_ref, $san_ref) ); |
205 |
|
|
+ my $request = HTTP::Request->new('GET', $url); |
206 |
|
|
+ $request->header(Host => $sni); |
207 |
|
|
+ my $response = $ua->simple_request($request); |
208 |
|
|
+ return $response; |
209 |
|
|
+} |
210 |
|
|
+ |
211 |
|
|
+sub wildcard_match { |
212 |
|
|
+ my ($cn, $host) = @_; |
213 |
|
|
+ my $match = 0; |
214 |
|
|
+ return 0 if $cn !~ m/^\*\.(.*)$/; |
215 |
|
|
+ my $cn_dom = $1; |
216 |
|
|
+ my $host_dom = ($sni =~ m/^[^\.]+\.(.*)$/)[0]; |
217 |
|
|
+ return ($cn_dom eq $host_dom); |
218 |
|
|
+} |
219 |
|
|
+ |
220 |
|
|
+GetOptions ("url|u=s" => \$url, |
221 |
|
|
+ "sni|s=s" => \$sni) or pod2usage(1); |
222 |
|
|
+if (@ARGV) { |
223 |
|
|
+ print "This script takes no arguments...\n"; |
224 |
|
|
+ pod2usage(1); |
225 |
|
|
+} |
226 |
|
|
+pod2usage(1) if (!$url); |
227 |
|
|
+ |
228 |
|
|
+my $expiration_date; |
229 |
|
|
+my $uri = URI->new($url); |
230 |
|
|
+die "Only https urls are supported\n" unless $uri->scheme eq 'https'; |
231 |
|
|
+$sni ||= $uri->host; |
232 |
|
|
+my $response = https_get($url, $sni, \$expiration_date, \$status, \@san); |
233 |
|
|
+ |
234 |
|
|
+my $out = { |
235 |
|
|
+ code => $response->code, |
236 |
|
|
+ status => $response->message, |
237 |
|
|
+ days_left => undef, |
238 |
|
|
+ cert_cn => undef, |
239 |
|
|
+ issuer => undef |
240 |
|
|
+}; |
241 |
|
|
+ |
242 |
|
|
+if ($response->code != 500) { # Even a 404 is good enough, as far as cert validation goes... |
243 |
|
|
+ my $now = DateTime->now; |
244 |
|
|
+ $expiration_date = DateTime::Format::ISO8601->parse_datetime( $expiration_date ); |
245 |
|
|
+ |
246 |
|
|
+ $out->{issuer} = $response->headers->{'client-ssl-cert-issuer'}; |
247 |
|
|
+ $out->{cert_cn} = ($response->headers->{'client-ssl-cert-subject'} =~ m/CN=(.*)$/)[0]; |
248 |
|
|
+ $status = "no common name" if !$out->{cert_cn}; |
249 |
|
|
+ $out->{status} = ($status eq 'ok' and !grep { $sni eq $_ } @san and !wildcard_match($out->{cert_cn},$sni)) ? |
250 |
|
|
+ $out->{status} = "hostname mismatch ($sni doesn't match any of " . join(" ", @san) . ")" : |
251 |
|
|
+ $status; |
252 |
|
|
+ $out->{days_left} = ($expiration_date < $now) ? -1 * $expiration_date->delta_days($now)->delta_days : |
253 |
|
|
+ $expiration_date->delta_days($now)->delta_days |
254 |
|
|
+} |
255 |
|
|
+ |
256 |
|
|
+print to_json($out, { pretty => 1 }); |
257 |
|
|
diff -Nur --no-dereference smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg_group.py smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg_group.py |
258 |
|
|
--- smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg_group.py 1969-12-31 19:00:00.000000000 -0500 |
259 |
|
|
+++ smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg_group.py 2021-11-08 22:00:51.153000000 -0500 |
260 |
|
|
@@ -0,0 +1,939 @@ |
261 |
|
|
+#!/usr/bin/env python |
262 |
|
|
+# coding: utf-8 |
263 |
|
|
+ |
264 |
|
|
+import sys |
265 |
|
|
+import os |
266 |
|
|
+import time |
267 |
|
|
+import random |
268 |
|
|
+import string |
269 |
|
|
+import requests |
270 |
|
|
+import json |
271 |
|
|
+import re |
272 |
|
|
+import stat |
273 |
|
|
+import hashlib |
274 |
|
|
+import subprocess |
275 |
|
|
+#import sqlite3 |
276 |
|
|
+from os.path import dirname |
277 |
|
|
+import zbxtg_settings |
278 |
|
|
+ |
279 |
|
|
+ |
280 |
|
|
+class Cache: |
281 |
|
|
+ def __init__(self, database): |
282 |
|
|
+ self.database = database |
283 |
|
|
+ |
284 |
|
|
+ def create_db(self, database): |
285 |
|
|
+ pass |
286 |
|
|
+ |
287 |
|
|
+ |
288 |
|
|
+class TelegramAPI: |
289 |
|
|
+ tg_url_bot_general = "https://api.telegram.org/bot" |
290 |
|
|
+ |
291 |
|
|
+ def http_get(self, url): |
292 |
|
|
+ answer = requests.get(url, proxies=self.proxies) |
293 |
|
|
+ self.result = answer.json() |
294 |
|
|
+ self.ok_update() |
295 |
|
|
+ return self.result |
296 |
|
|
+ |
297 |
|
|
+ def __init__(self, key): |
298 |
|
|
+ self.debug = False |
299 |
|
|
+ self.key = key |
300 |
|
|
+ self.proxies = {} |
301 |
|
|
+ self.type = "private" # 'private' for private chats or 'group' for group chats |
302 |
|
|
+ self.markdown = False |
303 |
|
|
+ self.html = False |
304 |
|
|
+ self.disable_web_page_preview = False |
305 |
|
|
+ self.disable_notification = False |
306 |
|
|
+ self.reply_to_message_id = 0 |
307 |
|
|
+ self.tmp_dir = None |
308 |
|
|
+ self.tmp_uids = None |
309 |
|
|
+ self.location = {"latitude": None, "longitude": None} |
310 |
|
|
+ self.update_offset = 0 |
311 |
|
|
+ self.image_buttons = False |
312 |
|
|
+ self.result = None |
313 |
|
|
+ self.ok = None |
314 |
|
|
+ self.error = None |
315 |
|
|
+ self.get_updates_from_file = False |
316 |
|
|
+ |
317 |
|
|
+ def get_me(self): |
318 |
|
|
+ url = self.tg_url_bot_general + self.key + "/getMe" |
319 |
|
|
+ me = self.http_get(url) |
320 |
|
|
+ return me |
321 |
|
|
+ |
322 |
|
|
+ def get_updates(self): |
323 |
|
|
+ url = self.tg_url_bot_general + self.key + "/getUpdates" |
324 |
|
|
+ params = {"offset": self.update_offset} |
325 |
|
|
+ if self.debug: |
326 |
|
|
+ print_message(url) |
327 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
328 |
|
|
+ self.result = answer.json() |
329 |
|
|
+ if self.get_updates_from_file: |
330 |
|
|
+ print_message("Getting updated from file getUpdates.txt") |
331 |
|
|
+ self.result = json.loads("".join(file_read("getUpdates.txt"))) |
332 |
|
|
+ if self.debug: |
333 |
|
|
+ print_message("Content of /getUpdates:") |
334 |
|
|
+ print_message(json.dumps(self.result)) |
335 |
|
|
+ self.ok_update() |
336 |
|
|
+ return self.result |
337 |
|
|
+ |
338 |
|
|
+ def send_message(self, to, message): |
339 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendMessage" |
340 |
|
|
+ message = "\n".join(message) |
341 |
|
|
+ params = {"chat_id": to, "text": message, "disable_web_page_preview": self.disable_web_page_preview, |
342 |
|
|
+ "disable_notification": self.disable_notification} |
343 |
|
|
+ if self.reply_to_message_id: |
344 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
345 |
|
|
+ if self.markdown or self.html: |
346 |
|
|
+ parse_mode = "HTML" |
347 |
|
|
+ if self.markdown: |
348 |
|
|
+ parse_mode = "Markdown" |
349 |
|
|
+ params["parse_mode"] = parse_mode |
350 |
|
|
+ if self.debug: |
351 |
|
|
+ print_message("Trying to /sendMessage:") |
352 |
|
|
+ print_message(url) |
353 |
|
|
+ print_message("post params: " + str(params)) |
354 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
355 |
|
|
+ if answer.status_code == 414: |
356 |
|
|
+ self.result = {"ok": False, "description": "414 URI Too Long"} |
357 |
|
|
+ else: |
358 |
|
|
+ self.result = answer.json() |
359 |
|
|
+ self.ok_update() |
360 |
|
|
+ return self.result |
361 |
|
|
+ |
362 |
|
|
+ def update_message(self, to, message_id, message): |
363 |
|
|
+ url = self.tg_url_bot_general + self.key + "/editMessageText" |
364 |
|
|
+ message = "\n".join(message) |
365 |
|
|
+ params = {"chat_id": to, "message_id": message_id, "text": message, |
366 |
|
|
+ "disable_web_page_preview": self.disable_web_page_preview, |
367 |
|
|
+ "disable_notification": self.disable_notification} |
368 |
|
|
+ if self.markdown or self.html: |
369 |
|
|
+ parse_mode = "HTML" |
370 |
|
|
+ if self.markdown: |
371 |
|
|
+ parse_mode = "Markdown" |
372 |
|
|
+ params["parse_mode"] = parse_mode |
373 |
|
|
+ if self.debug: |
374 |
|
|
+ print_message("Trying to /editMessageText:") |
375 |
|
|
+ print_message(url) |
376 |
|
|
+ print_message("post params: " + str(params)) |
377 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
378 |
|
|
+ self.result = answer.json() |
379 |
|
|
+ self.ok_update() |
380 |
|
|
+ return self.result |
381 |
|
|
+ |
382 |
|
|
+ def send_photo(self, to, message, path): |
383 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendPhoto" |
384 |
|
|
+ message = "\n".join(message) |
385 |
|
|
+ if self.image_buttons: |
386 |
|
|
+ reply_markup = json.dumps({"inline_keyboard": [[ |
387 |
|
|
+ {"text": "R", "callback_data": "graph_refresh"}, |
388 |
|
|
+ {"text": "1h", "callback_data": "graph_period_3600"}, |
389 |
|
|
+ {"text": "3h", "callback_data": "graph_period_10800"}, |
390 |
|
|
+ {"text": "6h", "callback_data": "graph_period_21600"}, |
391 |
|
|
+ {"text": "12h", "callback_data": "graph_period_43200"}, |
392 |
|
|
+ {"text": "24h", "callback_data": "graph_period_86400"}, |
393 |
|
|
+ ], ]}) |
394 |
|
|
+ else: |
395 |
|
|
+ reply_markup = json.dumps({}) |
396 |
|
|
+ params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification, |
397 |
|
|
+ "reply_markup": reply_markup} |
398 |
|
|
+ if self.reply_to_message_id: |
399 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
400 |
|
|
+ files = {"photo": open(path, 'rb')} |
401 |
|
|
+ if self.debug: |
402 |
|
|
+ print_message("Trying to /sendPhoto:") |
403 |
|
|
+ print_message(url) |
404 |
|
|
+ print_message(params) |
405 |
|
|
+ print_message("files: " + str(files)) |
406 |
|
|
+ answer = requests.post(url, params=params, files=files, proxies=self.proxies) |
407 |
|
|
+ self.result = answer.json() |
408 |
|
|
+ self.ok_update() |
409 |
|
|
+ return self.result |
410 |
|
|
+ |
411 |
|
|
+ def send_txt(self, to, text, text_name=None): |
412 |
|
|
+ path = self.tmp_dir + "/" + "zbxtg_txt_" |
413 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendDocument" |
414 |
|
|
+ text = "\n".join(text) |
415 |
|
|
+ if not text_name: |
416 |
|
|
+ path += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10)) |
417 |
|
|
+ else: |
418 |
|
|
+ path += text_name |
419 |
|
|
+ path += ".txt" |
420 |
|
|
+ file_write(path, text) |
421 |
|
|
+ params = {"chat_id": to, "caption": path.split("/")[-1], "disable_notification": self.disable_notification} |
422 |
|
|
+ if self.reply_to_message_id: |
423 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
424 |
|
|
+ files = {"document": open(path, 'rb')} |
425 |
|
|
+ if self.debug: |
426 |
|
|
+ print_message("Trying to /sendDocument:") |
427 |
|
|
+ print_message(url) |
428 |
|
|
+ print_message(params) |
429 |
|
|
+ print_message("files: " + str(files)) |
430 |
|
|
+ answer = requests.post(url, params=params, files=files, proxies=self.proxies) |
431 |
|
|
+ self.result = answer.json() |
432 |
|
|
+ self.ok_update() |
433 |
|
|
+ return self.result |
434 |
|
|
+ |
435 |
|
|
+ def get_uid(self, name): |
436 |
|
|
+ uid = 0 |
437 |
|
|
+ if self.debug: |
438 |
|
|
+ print_message("Getting uid from /getUpdates...") |
439 |
|
|
+ updates = self.get_updates() |
440 |
|
|
+ for m in updates["result"]: |
441 |
|
|
+ if "message" in m: |
442 |
|
|
+ chat = m["message"]["chat"] |
443 |
|
|
+ elif "edited_message" in m: |
444 |
|
|
+ chat = m["edited_message"]["chat"] |
445 |
|
|
+ else: |
446 |
|
|
+ continue |
447 |
|
|
+ if chat["type"] == self.type == "private": |
448 |
|
|
+ if "username" in chat: |
449 |
|
|
+ if chat["username"] == name: |
450 |
|
|
+ uid = chat["id"] |
451 |
|
|
+ if (chat["type"] == "group" or chat["type"] == "supergroup") and self.type == "group": |
452 |
|
|
+ if "title" in chat: |
453 |
|
|
+ if sys.version_info[0] < 3: |
454 |
|
|
+ if chat["title"] == name.decode("utf-8"): |
455 |
|
|
+ uid = chat["id"] |
456 |
|
|
+ else: |
457 |
|
|
+ if chat["title"] == name: |
458 |
|
|
+ uid = chat["id"] |
459 |
|
|
+ return uid |
460 |
|
|
+ |
461 |
|
|
+ def error_need_to_contact(self, to): |
462 |
|
|
+ if self.type == "private": |
463 |
|
|
+ print_message("User '{0}' needs to send some text bot in private".format(to)) |
464 |
|
|
+ if self.type == "group": |
465 |
|
|
+ print_message("You need start a conversation with your bot first in '{0}' group chat, type '/start@{1}'" |
466 |
|
|
+ .format(to, self.get_me()["result"]["username"])) |
467 |
|
|
+ |
468 |
|
|
+ def update_cache_uid(self, name, uid, message="Add new string to cache file"): |
469 |
|
|
+ cache_string = "{0};{1};{2}\n".format(name, self.type, str(uid).rstrip()) |
470 |
|
|
+ # FIXME |
471 |
|
|
+ if self.debug: |
472 |
|
|
+ print_message("{0}: {1}".format(message, cache_string)) |
473 |
|
|
+ with open(self.tmp_uids, "a") as cache_file_uids: |
474 |
|
|
+ cache_file_uids.write(cache_string) |
475 |
|
|
+ return True |
476 |
|
|
+ |
477 |
|
|
+ def get_uid_from_cache(self, name): |
478 |
|
|
+ if self.debug: |
479 |
|
|
+ print_message("Trying to read cached uid for {0}, {1}, from {2}".format(name, self.type, self.tmp_uids)) |
480 |
|
|
+ uid = 0 |
481 |
|
|
+ if os.path.isfile(self.tmp_uids): |
482 |
|
|
+ with open(self.tmp_uids, 'r') as cache_file_uids: |
483 |
|
|
+ cache_uids_old = cache_file_uids.readlines() |
484 |
|
|
+ for u in cache_uids_old: |
485 |
|
|
+ u_splitted = u.split(";") |
486 |
|
|
+ if name == u_splitted[0] and self.type == u_splitted[1]: |
487 |
|
|
+ uid = u_splitted[2] |
488 |
|
|
+ return uid |
489 |
|
|
+ |
490 |
|
|
+ def send_location(self, to, coordinates): |
491 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendLocation" |
492 |
|
|
+ params = {"chat_id": to, "disable_notification": self.disable_notification, |
493 |
|
|
+ "latitude": coordinates["latitude"], "longitude": coordinates["longitude"]} |
494 |
|
|
+ if self.reply_to_message_id: |
495 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
496 |
|
|
+ if self.debug: |
497 |
|
|
+ print_message("Trying to /sendLocation:") |
498 |
|
|
+ print_message(url) |
499 |
|
|
+ print_message("post params: " + str(params)) |
500 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
501 |
|
|
+ self.result = answer.json() |
502 |
|
|
+ self.ok_update() |
503 |
|
|
+ return self.result |
504 |
|
|
+ |
505 |
|
|
+ def answer_callback_query(self, callback_query_id, text=None): |
506 |
|
|
+ url = self.tg_url_bot_general + self.key + "/answerCallbackQuery" |
507 |
|
|
+ if not text: |
508 |
|
|
+ params = {"callback_query_id": callback_query_id} |
509 |
|
|
+ else: |
510 |
|
|
+ params = {"callback_query_id": callback_query_id, "text": text} |
511 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
512 |
|
|
+ self.result = answer.json() |
513 |
|
|
+ self.ok_update() |
514 |
|
|
+ return self.result |
515 |
|
|
+ |
516 |
|
|
+ def ok_update(self): |
517 |
|
|
+ self.ok = self.result["ok"] |
518 |
|
|
+ if self.ok: |
519 |
|
|
+ self.error = None |
520 |
|
|
+ else: |
521 |
|
|
+ self.error = self.result["description"] |
522 |
|
|
+ print_message(self.error) |
523 |
|
|
+ return True |
524 |
|
|
+ |
525 |
|
|
+ |
526 |
|
|
+def markdown_fix(message, offset, emoji=False): |
527 |
|
|
+ offset = int(offset) |
528 |
|
|
+ if emoji: # https://github.com/ableev/Zabbix-in-Telegram/issues/152 |
529 |
|
|
+ offset -= 2 |
530 |
|
|
+ message = "\n".join(message) |
531 |
|
|
+ message = message[:offset] + message[offset+1:] |
532 |
|
|
+ message = message.split("\n") |
533 |
|
|
+ return message |
534 |
|
|
+ |
535 |
|
|
+ |
536 |
|
|
+class ZabbixWeb: |
537 |
|
|
+ def __init__(self, server, username, password): |
538 |
|
|
+ self.debug = False |
539 |
|
|
+ self.server = server |
540 |
|
|
+ self.username = username |
541 |
|
|
+ self.password = password |
542 |
|
|
+ self.proxies = {} |
543 |
|
|
+ self.verify = True |
544 |
|
|
+ self.cookie = None |
545 |
|
|
+ self.basic_auth_user = None |
546 |
|
|
+ self.basic_auth_pass = None |
547 |
|
|
+ self.tmp_dir = None |
548 |
|
|
+ |
549 |
|
|
+ def login(self): |
550 |
|
|
+ if not self.verify: |
551 |
|
|
+ requests.packages.urllib3.disable_warnings() |
552 |
|
|
+ |
553 |
|
|
+ data_api = {"name": self.username, "password": self.password, "enter": "Sign in"} |
554 |
|
|
+ answer = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify, |
555 |
|
|
+ auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) |
556 |
|
|
+ cookie = answer.cookies |
557 |
|
|
+ if len(answer.history) > 1 and answer.history[0].status_code == 302: |
558 |
|
|
+ print_message("probably the server in your config file has not full URL (for example " |
559 |
|
|
+ "'{0}' instead of '{1}')".format(self.server, self.server + "/zabbix")) |
560 |
|
|
+ if not cookie: |
561 |
|
|
+ print_message("authorization has failed, url: {0}".format(self.server + "/")) |
562 |
|
|
+ cookie = None |
563 |
|
|
+ |
564 |
|
|
+ self.cookie = cookie |
565 |
|
|
+ |
566 |
|
|
+ def graph_get(self, itemid, period, title, width, height, version=3): |
567 |
|
|
+ file_img = self.tmp_dir + "/{0}.png".format("".join(itemid)) |
568 |
|
|
+ |
569 |
|
|
+ title = requests.utils.quote(title) |
570 |
|
|
+ |
571 |
|
|
+ colors = { |
572 |
|
|
+ 0: "00CC00", |
573 |
|
|
+ 1: "CC0000", |
574 |
|
|
+ 2: "0000CC", |
575 |
|
|
+ 3: "CCCC00", |
576 |
|
|
+ 4: "00CCCC", |
577 |
|
|
+ 5: "CC00CC", |
578 |
|
|
+ } |
579 |
|
|
+ |
580 |
|
|
+ drawtype = 5 |
581 |
|
|
+ if len(itemid) > 1: |
582 |
|
|
+ drawtype = 2 |
583 |
|
|
+ |
584 |
|
|
+ zbx_img_url_itemids = [] |
585 |
|
|
+ for i in range(0, len(itemid)): |
586 |
|
|
+ itemid_url = "&items[{0}][itemid]={1}&items[{0}][sortorder]={0}&" \ |
587 |
|
|
+ "items[{0}][drawtype]={3}&items[{0}][color]={2}".format(i, itemid[i], colors[i], drawtype) |
588 |
|
|
+ zbx_img_url_itemids.append(itemid_url) |
589 |
|
|
+ |
590 |
|
|
+ zbx_img_url = self.server + "/chart3.php?" |
591 |
|
|
+ if version < 4: |
592 |
|
|
+ zbx_img_url += "period={0}".format(period) |
593 |
|
|
+ else: |
594 |
|
|
+ zbx_img_url += "from=now-{0}&to=now".format(period) |
595 |
|
|
+ zbx_img_url += "&name={0}&width={1}&height={2}&graphtype=0&legend=1".format(title, width, height) |
596 |
|
|
+ zbx_img_url += "".join(zbx_img_url_itemids) |
597 |
|
|
+ |
598 |
|
|
+ if self.debug: |
599 |
|
|
+ print_message(zbx_img_url) |
600 |
|
|
+ answer = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify, |
601 |
|
|
+ auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) |
602 |
|
|
+ status_code = answer.status_code |
603 |
|
|
+ if status_code == 404: |
604 |
|
|
+ print_message("can't get image from '{0}'".format(zbx_img_url)) |
605 |
|
|
+ return False |
606 |
|
|
+ res_img = answer.content |
607 |
|
|
+ file_bwrite(file_img, res_img) |
608 |
|
|
+ return file_img |
609 |
|
|
+ |
610 |
|
|
+ def api_test(self): |
611 |
|
|
+ headers = {'Content-type': 'application/json'} |
612 |
|
|
+ api_data = json.dumps({"jsonrpc": "2.0", "method": "user.login", "params": |
613 |
|
|
+ {"user": self.username, "password": self.password}, "id": 1}) |
614 |
|
|
+ api_url = self.server + "/api_jsonrpc.php" |
615 |
|
|
+ api = requests.post(api_url, data=api_data, proxies=self.proxies, headers=headers) |
616 |
|
|
+ return api.text |
617 |
|
|
+ |
618 |
|
|
+ |
619 |
|
|
+def print_message(message): |
620 |
|
|
+ message = str(message) + "\n" |
621 |
|
|
+ filename = sys.argv[0].split("/")[-1] |
622 |
|
|
+ sys.stderr.write(filename + ": " + message) |
623 |
|
|
+ |
624 |
|
|
+ |
625 |
|
|
+def list_cut(elements, symbols_limit): |
626 |
|
|
+ symbols_count = symbols_count_now = 0 |
627 |
|
|
+ elements_new = [] |
628 |
|
|
+ element_last_list = [] |
629 |
|
|
+ for e in elements: |
630 |
|
|
+ symbols_count_now = symbols_count + len(e) |
631 |
|
|
+ if symbols_count_now > symbols_limit: |
632 |
|
|
+ limit_idx = symbols_limit - symbols_count |
633 |
|
|
+ e_list = list(e) |
634 |
|
|
+ for idx, ee in enumerate(e_list): |
635 |
|
|
+ if idx < limit_idx: |
636 |
|
|
+ element_last_list.append(ee) |
637 |
|
|
+ else: |
638 |
|
|
+ break |
639 |
|
|
+ break |
640 |
|
|
+ else: |
641 |
|
|
+ symbols_count = symbols_count_now + 1 |
642 |
|
|
+ elements_new.append(e) |
643 |
|
|
+ if symbols_count_now < symbols_limit: |
644 |
|
|
+ return elements, False |
645 |
|
|
+ else: |
646 |
|
|
+ element_last = "".join(element_last_list) |
647 |
|
|
+ elements_new.append(element_last) |
648 |
|
|
+ return elements_new, True |
649 |
|
|
+ |
650 |
|
|
+ |
651 |
|
|
+class Maps: |
652 |
|
|
+ # https://developers.google.com/maps/documentation/geocoding/intro |
653 |
|
|
+ def __init__(self): |
654 |
|
|
+ self.key = None |
655 |
|
|
+ self.proxies = {} |
656 |
|
|
+ |
657 |
|
|
+ def get_coordinates_by_address(self, address): |
658 |
|
|
+ coordinates = {"latitude": 0, "longitude": 0} |
659 |
|
|
+ url_api = "https://maps.googleapis.com/maps/api/geocode/json?key={0}&address={1}".format(self.key, address) |
660 |
|
|
+ url = url_api |
661 |
|
|
+ answer = requests.get(url, proxies=self.proxies) |
662 |
|
|
+ result = answer.json() |
663 |
|
|
+ try: |
664 |
|
|
+ coordinates_dict = result["results"][0]["geometry"]["location"] |
665 |
|
|
+ except: |
666 |
|
|
+ if "error_message" in result: |
667 |
|
|
+ print_message("[" + result["status"] + "]: " + result["error_message"]) |
668 |
|
|
+ return coordinates |
669 |
|
|
+ coordinates = {"latitude": coordinates_dict["lat"], "longitude": coordinates_dict["lng"]} |
670 |
|
|
+ return coordinates |
671 |
|
|
+ |
672 |
|
|
+ |
673 |
|
|
+def file_write(filename, text): |
674 |
|
|
+ with open(filename, "w") as fd: |
675 |
|
|
+ fd.write(str(text)) |
676 |
|
|
+ return True |
677 |
|
|
+ |
678 |
|
|
+ |
679 |
|
|
+def file_bwrite(filename, data): |
680 |
|
|
+ with open(filename, "wb") as fd: |
681 |
|
|
+ fd.write(data) |
682 |
|
|
+ return True |
683 |
|
|
+ |
684 |
|
|
+ |
685 |
|
|
+def file_read(filename): |
686 |
|
|
+ with open(filename, "r") as fd: |
687 |
|
|
+ text = fd.readlines() |
688 |
|
|
+ return text |
689 |
|
|
+ |
690 |
|
|
+ |
691 |
|
|
+def file_append(filename, text): |
692 |
|
|
+ with open(filename, "a") as fd: |
693 |
|
|
+ fd.write(str(text)) |
694 |
|
|
+ return True |
695 |
|
|
+ |
696 |
|
|
+ |
697 |
|
|
+def external_image_get(url, tmp_dir, timeout=6): |
698 |
|
|
+ image_hash = hashlib.md5() |
699 |
|
|
+ image_hash.update(url.encode()) |
700 |
|
|
+ file_img = tmp_dir + "/external_{0}.png".format(image_hash.hexdigest()) |
701 |
|
|
+ try: |
702 |
|
|
+ answer = requests.get(url, timeout=timeout, allow_redirects=True) |
703 |
|
|
+ except requests.exceptions.ReadTimeout as ex: |
704 |
|
|
+ print_message("Can't get external image from '{0}': timeout".format(url)) |
705 |
|
|
+ return False |
706 |
|
|
+ status_code = answer.status_code |
707 |
|
|
+ if status_code == 404: |
708 |
|
|
+ print_message("Can't get external image from '{0}': HTTP 404 error".format(url)) |
709 |
|
|
+ return False |
710 |
|
|
+ answer_image = answer.content |
711 |
|
|
+ file_bwrite(file_img, answer_image) |
712 |
|
|
+ return file_img |
713 |
|
|
+ |
714 |
|
|
+ |
715 |
|
|
+def age2sec(age_str): |
716 |
|
|
+ age_sec = 0 |
717 |
|
|
+ age_regex = "([0-9]+d)?\s?([0-9]+h)?\s?([0-9]+m)?" |
718 |
|
|
+ age_pattern = re.compile(age_regex) |
719 |
|
|
+ intervals = age_pattern.match(age_str).groups() |
720 |
|
|
+ for i in intervals: |
721 |
|
|
+ if i: |
722 |
|
|
+ metric = i[-1] |
723 |
|
|
+ if metric == "d": |
724 |
|
|
+ age_sec += int(i[0:-1])*86400 |
725 |
|
|
+ if metric == "h": |
726 |
|
|
+ age_sec += int(i[0:-1])*3600 |
727 |
|
|
+ if metric == "m": |
728 |
|
|
+ age_sec += int(i[0:-1])*60 |
729 |
|
|
+ return age_sec |
730 |
|
|
+ |
731 |
|
|
+ |
732 |
|
|
+def main(): |
733 |
|
|
+ |
734 |
|
|
+ tmp_dir = zbxtg_settings.zbx_tg_tmp_dir |
735 |
|
|
+ if tmp_dir == "/tmp/" + zbxtg_settings.zbx_tg_prefix: |
736 |
|
|
+ print_message("WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!") |
737 |
|
|
+ print_message("https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings") |
738 |
|
|
+ |
739 |
|
|
+ tmp_cookie = tmp_dir + "/cookie.py.txt" |
740 |
|
|
+ tmp_uids = tmp_dir + "/uids.txt" |
741 |
|
|
+ tmp_need_update = False # do we need to update cache file with uids or not |
742 |
|
|
+ |
743 |
|
|
+ rnd = random.randint(0, 999) |
744 |
|
|
+ ts = time.time() |
745 |
|
|
+ hash_ts = str(ts) + "." + str(rnd) |
746 |
|
|
+ |
747 |
|
|
+ log_file = "/dev/null" |
748 |
|
|
+ |
749 |
|
|
+ args = sys.argv |
750 |
|
|
+ |
751 |
|
|
+ settings = { |
752 |
|
|
+ "zbxtg_itemid": "0", # itemid for graph |
753 |
|
|
+ "zbxtg_title": None, # title for graph |
754 |
|
|
+ "zbxtg_image_period": None, |
755 |
|
|
+ "zbxtg_image_age": "3600", |
756 |
|
|
+ "zbxtg_image_width": "900", |
757 |
|
|
+ "zbxtg_image_height": "200", |
758 |
|
|
+ "tg_method_image": False, # if True - default send images, False - send text |
759 |
|
|
+ "tg_chat": False, # send message to chat or in private |
760 |
|
|
+ "tg_group": False, # send message to chat or in private |
761 |
|
|
+ "is_debug": False, |
762 |
|
|
+ "is_channel": False, |
763 |
|
|
+ "disable_web_page_preview": False, |
764 |
|
|
+ "location": None, # address |
765 |
|
|
+ "lat": 0, # latitude |
766 |
|
|
+ "lon": 0, # longitude |
767 |
|
|
+ "is_single_message": False, |
768 |
|
|
+ "markdown": False, |
769 |
|
|
+ "html": False, |
770 |
|
|
+ "signature": None, |
771 |
|
|
+ "signature_disable": False, |
772 |
|
|
+ "graph_buttons": False, |
773 |
|
|
+ "extimg": None, |
774 |
|
|
+ "to": None, |
775 |
|
|
+ "to_group": None, |
776 |
|
|
+ "forked": False, |
777 |
|
|
+ } |
778 |
|
|
+ |
779 |
|
|
+ url_github = "https://github.com/ableev/Zabbix-in-Telegram" |
780 |
|
|
+ url_wiki_base = "https://github.com/ableev/Zabbix-in-Telegram/wiki" |
781 |
|
|
+ url_tg_group = "https://t.me/ZbxTg" |
782 |
|
|
+ url_tg_channel = "https://t.me/Zabbix_in_Telegram" |
783 |
|
|
+ |
784 |
|
|
+ settings_description = { |
785 |
|
|
+ "itemid": {"name": "zbxtg_itemid", "type": "list", |
786 |
|
|
+ "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs"}, |
787 |
|
|
+ "title": {"name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs"}, |
788 |
|
|
+ "graphs_period": {"name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs"}, |
789 |
|
|
+ "graphs_age": {"name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs"}, |
790 |
|
|
+ "graphs_width": {"name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs"}, |
791 |
|
|
+ "graphs_height": {"name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs"}, |
792 |
|
|
+ "graphs": {"name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs"}, |
793 |
|
|
+ "chat": {"name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'", |
794 |
|
|
+ "url": "How-to-send-message-to-the-group-chat"}, |
795 |
|
|
+ "group": {"name": "tg_group", "type": "bool", "help": "sends message to a group", |
796 |
|
|
+ "url": "How-to-send-message-to-the-group-chat"}, |
797 |
|
|
+ "debug": {"name": "is_debug", "type": "bool", "help": "enables 'debug'", |
798 |
|
|
+ "url": "How-to-test-script-in-command-line"}, |
799 |
|
|
+ "channel": {"name": "is_channel", "type": "bool", "help": "sends message to a channel", |
800 |
|
|
+ "url": "Channel-support"}, |
801 |
|
|
+ "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool", |
802 |
|
|
+ "help": "disable web page preview", "url": "Disable-web-page-preview"}, |
803 |
|
|
+ "location": {"name": "location", "type": "str", "help": "address of location", "url": "Location"}, |
804 |
|
|
+ "lat": {"name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location"}, |
805 |
|
|
+ "lon": {"name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location"}, |
806 |
|
|
+ "single_message": {"name": "is_single_message", "type": "bool", "help": "do not split message and graph", |
807 |
|
|
+ "url": "Why-am-I-getting-two-messages-instead-of-one"}, |
808 |
|
|
+ "markdown": {"name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, |
809 |
|
|
+ "html": {"name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, |
810 |
|
|
+ "signature": {"name": "signature", "type": "str", |
811 |
|
|
+ "help": "bot's signature", "url": "Bot-signature"}, |
812 |
|
|
+ "signature_disable": {"name": "signature_disable", "type": "bool", |
813 |
|
|
+ "help": "enables/disables bot's signature", "url": "Bot-signature"}, |
814 |
|
|
+ "graph_buttons": {"name": "graph_buttons", "type": "bool", |
815 |
|
|
+ "help": "activates buttons under graph, could be using in ZbxTgDaemon", |
816 |
|
|
+ "url": "Interactive-bot"}, |
817 |
|
|
+ "external_image": {"name": "extimg", "type": "str", |
818 |
|
|
+ "help": "should be url; attaches external image from different source", |
819 |
|
|
+ "url": "External-image-as-graph"}, |
820 |
|
|
+ "to": {"name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", |
821 |
|
|
+ "url": "Custom-to-and-to_group"}, |
822 |
|
|
+ "to_group": {"name": "to_group", "type": "str", |
823 |
|
|
+ "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group"}, |
824 |
|
|
+ "forked": {"name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": ""}, |
825 |
|
|
+ } |
826 |
|
|
+ |
827 |
|
|
+ if len(args) < 4: |
828 |
|
|
+ do_not_exit = False |
829 |
|
|
+ if "--features" in args: |
830 |
|
|
+ print(("List of available settings, see {0}/Settings\n---".format(url_wiki_base))) |
831 |
|
|
+ for sett, proprt in list(settings_description.items()): |
832 |
|
|
+ print(("{0}: {1}\ndoc: {2}/{3}\n--".format(sett, proprt["help"], url_wiki_base, proprt["url"]))) |
833 |
|
|
+ |
834 |
|
|
+ elif "--show-settings" in args: |
835 |
|
|
+ do_not_exit = True |
836 |
|
|
+ print_message("Settings: " + str(json.dumps(settings, indent=2))) |
837 |
|
|
+ |
838 |
|
|
+ else: |
839 |
|
|
+ print(("Hi. You should provide at least three arguments.\n" |
840 |
|
|
+ "zbxtg.py [TO] [SUBJECT] [BODY]\n\n" |
841 |
|
|
+ "1. Read main page and/or wiki: {0} + {1}\n" |
842 |
|
|
+ "2. Public Telegram group (discussion): {2}\n" |
843 |
|
|
+ "3. Public Telegram channel: {3}\n" |
844 |
|
|
+ "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev" |
845 |
|
|
+ .format(url_github, url_wiki_base, url_tg_group, url_tg_channel))) |
846 |
|
|
+ if not do_not_exit: |
847 |
|
|
+ sys.exit(0) |
848 |
|
|
+ |
849 |
|
|
+ |
850 |
|
|
+ zbx_to = args[1] |
851 |
|
|
+ zbx_subject = args[2] |
852 |
|
|
+ zbx_body = args[3] |
853 |
|
|
+ |
854 |
|
|
+ tg = TelegramAPI(key=zbxtg_settings.tg_key) |
855 |
|
|
+ |
856 |
|
|
+ tg.tmp_dir = tmp_dir |
857 |
|
|
+ tg.tmp_uids = tmp_uids |
858 |
|
|
+ |
859 |
|
|
+ if zbxtg_settings.proxy_to_tg: |
860 |
|
|
+ proxy_to_tg = zbxtg_settings.proxy_to_tg |
861 |
|
|
+ if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"): |
862 |
|
|
+ proxy_to_tg = "https://" + proxy_to_tg |
863 |
|
|
+ tg.proxies = { |
864 |
|
|
+ "https": "{0}".format(proxy_to_tg), |
865 |
|
|
+ } |
866 |
|
|
+ |
867 |
|
|
+ zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user, |
868 |
|
|
+ password=zbxtg_settings.zbx_api_pass) |
869 |
|
|
+ |
870 |
|
|
+ zbx.tmp_dir = tmp_dir |
871 |
|
|
+ |
872 |
|
|
+ # workaround for Zabbix 4.x |
873 |
|
|
+ zbx_version = 3 |
874 |
|
|
+ |
875 |
|
|
+ try: |
876 |
|
|
+ zbx_version = zbxtg_settings.zbx_server_version |
877 |
|
|
+ except: |
878 |
|
|
+ pass |
879 |
|
|
+ |
880 |
|
|
+ if zbxtg_settings.proxy_to_zbx: |
881 |
|
|
+ zbx.proxies = { |
882 |
|
|
+ "http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx), |
883 |
|
|
+ "https": "https://{0}/".format(zbxtg_settings.proxy_to_zbx) |
884 |
|
|
+ } |
885 |
|
|
+ |
886 |
|
|
+ # https://github.com/ableev/Zabbix-in-Telegram/issues/55 |
887 |
|
|
+ try: |
888 |
|
|
+ if zbxtg_settings.zbx_basic_auth: |
889 |
|
|
+ zbx.basic_auth_user = zbxtg_settings.zbx_basic_auth_user |
890 |
|
|
+ zbx.basic_auth_pass = zbxtg_settings.zbx_basic_auth_pass |
891 |
|
|
+ except: |
892 |
|
|
+ pass |
893 |
|
|
+ |
894 |
|
|
+ try: |
895 |
|
|
+ zbx_api_verify = zbxtg_settings.zbx_api_verify |
896 |
|
|
+ zbx.verify = zbx_api_verify |
897 |
|
|
+ except: |
898 |
|
|
+ pass |
899 |
|
|
+ |
900 |
|
|
+ map = Maps() |
901 |
|
|
+ # api key to resolve address to coordinates via google api |
902 |
|
|
+ try: |
903 |
|
|
+ if zbxtg_settings.google_maps_api_key: |
904 |
|
|
+ map.key = zbxtg_settings.google_maps_api_key |
905 |
|
|
+ if zbxtg_settings.proxy_to_tg: |
906 |
|
|
+ map.proxies = { |
907 |
|
|
+ "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg), |
908 |
|
|
+ "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg) |
909 |
|
|
+ } |
910 |
|
|
+ except: |
911 |
|
|
+ pass |
912 |
|
|
+ |
913 |
|
|
+ zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines() |
914 |
|
|
+ zbxtg_body_text = [] |
915 |
|
|
+ |
916 |
|
|
+ for line in zbxtg_body: |
917 |
|
|
+ if line.find(zbxtg_settings.zbx_tg_prefix) > -1: |
918 |
|
|
+ setting = re.split("[\s:=]+", line, maxsplit=1) |
919 |
|
|
+ key = setting[0].replace(zbxtg_settings.zbx_tg_prefix + ";", "") |
920 |
|
|
+ if key not in settings_description: |
921 |
|
|
+ if "--debug" in args: |
922 |
|
|
+ print_message("[ERROR] There is no '{0}' method, use --features to get help".format(key)) |
923 |
|
|
+ continue |
924 |
|
|
+ if settings_description[key]["type"] == "list": |
925 |
|
|
+ value = setting[1].split(",") |
926 |
|
|
+ elif len(setting) > 1 and len(setting[1]) > 0: |
927 |
|
|
+ value = setting[1] |
928 |
|
|
+ elif settings_description[key]["type"] == "bool": |
929 |
|
|
+ value = True |
930 |
|
|
+ else: |
931 |
|
|
+ value = settings[settings_description[key]["name"]] |
932 |
|
|
+ if key in settings_description: |
933 |
|
|
+ settings[settings_description[key]["name"]] = value |
934 |
|
|
+ else: |
935 |
|
|
+ zbxtg_body_text.append(line) |
936 |
|
|
+ |
937 |
|
|
+ tg_method_image = bool(settings["tg_method_image"]) |
938 |
|
|
+ tg_chat = bool(settings["tg_chat"]) |
939 |
|
|
+ tg_group = bool(settings["tg_group"]) |
940 |
|
|
+ is_debug = bool(settings["is_debug"]) |
941 |
|
|
+ is_channel = bool(settings["is_channel"]) |
942 |
|
|
+ disable_web_page_preview = bool(settings["disable_web_page_preview"]) |
943 |
|
|
+ is_single_message = bool(settings["is_single_message"]) |
944 |
|
|
+ |
945 |
|
|
+ # experimental way to send message to the group https://github.com/ableev/Zabbix-in-Telegram/issues/15 |
946 |
|
|
+ if args[0].split("/")[-1] == "zbxtg_group.py" or "--group" in args or tg_chat or tg_group: |
947 |
|
|
+ tg_chat = True |
948 |
|
|
+ tg_group = True |
949 |
|
|
+ tg.type = "group" |
950 |
|
|
+ |
951 |
|
|
+ if "--debug" in args or is_debug: |
952 |
|
|
+ is_debug = True |
953 |
|
|
+ tg.debug = True |
954 |
|
|
+ zbx.debug = True |
955 |
|
|
+ print_message(tg.get_me()) |
956 |
|
|
+ print_message("Cache file with uids: " + tg.tmp_uids) |
957 |
|
|
+ log_file = tmp_dir + ".debug." + hash_ts + ".log" |
958 |
|
|
+ #print_message(log_file) |
959 |
|
|
+ |
960 |
|
|
+ if "--markdown" in args or settings["markdown"]: |
961 |
|
|
+ tg.markdown = True |
962 |
|
|
+ |
963 |
|
|
+ if "--html" in args or settings["html"]: |
964 |
|
|
+ tg.html = True |
965 |
|
|
+ |
966 |
|
|
+ if "--channel" in args or is_channel: |
967 |
|
|
+ tg.type = "channel" |
968 |
|
|
+ |
969 |
|
|
+ if "--disable_web_page_preview" in args or disable_web_page_preview: |
970 |
|
|
+ if is_debug: |
971 |
|
|
+ print_message("'disable_web_page_preview' option has been enabled") |
972 |
|
|
+ tg.disable_web_page_preview = True |
973 |
|
|
+ |
974 |
|
|
+ if "--graph_buttons" in args or settings["graph_buttons"]: |
975 |
|
|
+ tg.image_buttons = True |
976 |
|
|
+ |
977 |
|
|
+ if "--forked" in args: |
978 |
|
|
+ settings["forked"] = True |
979 |
|
|
+ |
980 |
|
|
+ if "--tg-key" in args: |
981 |
|
|
+ tg.key = args[args.index("--tg-key") + 1] |
982 |
|
|
+ |
983 |
|
|
+ location_coordinates = {"latitude": None, "longitude": None} |
984 |
|
|
+ if settings["lat"] > 0 and settings["lat"] > 0: |
985 |
|
|
+ location_coordinates = {"latitude": settings["lat"], "longitude": settings["lon"]} |
986 |
|
|
+ tg.location = location_coordinates |
987 |
|
|
+ else: |
988 |
|
|
+ if settings["location"]: |
989 |
|
|
+ location_coordinates = map.get_coordinates_by_address(settings["location"]) |
990 |
|
|
+ if location_coordinates: |
991 |
|
|
+ settings["lat"] = location_coordinates["latitude"] |
992 |
|
|
+ settings["lon"] = location_coordinates["longitude"] |
993 |
|
|
+ tg.location = location_coordinates |
994 |
|
|
+ |
995 |
|
|
+ if not os.path.isdir(tmp_dir): |
996 |
|
|
+ if is_debug: |
997 |
|
|
+ print_message("Tmp dir doesn't exist, creating new one...") |
998 |
|
|
+ try: |
999 |
|
|
+ os.makedirs(tmp_dir) |
1000 |
|
|
+ open(tg.tmp_uids, "a").close() |
1001 |
|
|
+ os.chmod(tmp_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
1002 |
|
|
+ os.chmod(tg.tmp_uids, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
1003 |
|
|
+ except: |
1004 |
|
|
+ tmp_dir = "/tmp" |
1005 |
|
|
+ if is_debug: |
1006 |
|
|
+ print_message("Using {0} as a temporary dir".format(tmp_dir)) |
1007 |
|
|
+ |
1008 |
|
|
+ done_all_work_in_the_fork = False |
1009 |
|
|
+ # issue75 |
1010 |
|
|
+ |
1011 |
|
|
+ to_types = ["to", "to_group", "to_channel"] |
1012 |
|
|
+ to_types_to_telegram = {"to": "private", "to_group": "group", "to_channel": "channel"} |
1013 |
|
|
+ multiple_to = {} |
1014 |
|
|
+ for i in to_types: |
1015 |
|
|
+ multiple_to[i]=[] |
1016 |
|
|
+ |
1017 |
|
|
+ for t in to_types: |
1018 |
|
|
+ try: |
1019 |
|
|
+ if settings[t] and not settings["forked"]: |
1020 |
|
|
+ # zbx_to = settings["to"] |
1021 |
|
|
+ multiple_to[t] = re.split(",", settings[t]) |
1022 |
|
|
+ except KeyError: |
1023 |
|
|
+ pass |
1024 |
|
|
+ |
1025 |
|
|
+ # example: |
1026 |
|
|
+ # {'to_channel': [], 'to': ['usr1', 'usr2', 'usr3'], 'to_group': []} |
1027 |
|
|
+ |
1028 |
|
|
+ if (sum([len(v) for k, v in list(multiple_to.items())])) == 1: |
1029 |
|
|
+ # if we have only one recipient, we don't need fork to send message, just re-write "to" vaiable |
1030 |
|
|
+ tmp_max = 0 |
1031 |
|
|
+ for t in to_types: |
1032 |
|
|
+ if len(multiple_to[t]) > tmp_max: |
1033 |
|
|
+ tmp_max = len(multiple_to[t]) |
1034 |
|
|
+ tg.type = to_types_to_telegram[t] |
1035 |
|
|
+ zbx_to = multiple_to[t][0] |
1036 |
|
|
+ else: |
1037 |
|
|
+ for t in to_types: |
1038 |
|
|
+ for i in multiple_to[t]: |
1039 |
|
|
+ args_new = list(args) |
1040 |
|
|
+ args_new[1] = i |
1041 |
|
|
+ if t == "to_group": |
1042 |
|
|
+ args_new.append("--group") |
1043 |
|
|
+ args_new.append("--forked") |
1044 |
|
|
+ args_new.insert(0, sys.executable) |
1045 |
|
|
+ if is_debug: |
1046 |
|
|
+ print_message("Fork for custom recipient ({1}), new args: {0}".format(args_new, |
1047 |
|
|
+ to_types_to_telegram[t])) |
1048 |
|
|
+ subprocess.call(args_new) |
1049 |
|
|
+ done_all_work_in_the_fork = True |
1050 |
|
|
+ |
1051 |
|
|
+ if done_all_work_in_the_fork: |
1052 |
|
|
+ sys.exit(0) |
1053 |
|
|
+ |
1054 |
|
|
+ uid = None |
1055 |
|
|
+ |
1056 |
|
|
+ if tg.type == "channel": |
1057 |
|
|
+ uid = zbx_to |
1058 |
|
|
+ if tg.type == "private": |
1059 |
|
|
+ zbx_to = zbx_to.replace("@", "") |
1060 |
|
|
+ |
1061 |
|
|
+ if zbx_to.isdigit(): |
1062 |
|
|
+ uid = zbx_to |
1063 |
|
|
+ |
1064 |
|
|
+ if not uid: |
1065 |
|
|
+ uid = tg.get_uid_from_cache(zbx_to) |
1066 |
|
|
+ |
1067 |
|
|
+ if not uid: |
1068 |
|
|
+ uid = tg.get_uid(zbx_to) |
1069 |
|
|
+ if uid: |
1070 |
|
|
+ tmp_need_update = True |
1071 |
|
|
+ if not uid: |
1072 |
|
|
+ tg.error_need_to_contact(zbx_to) |
1073 |
|
|
+ sys.exit(1) |
1074 |
|
|
+ |
1075 |
|
|
+ if tmp_need_update: |
1076 |
|
|
+ tg.update_cache_uid(zbx_to, str(uid).rstrip()) |
1077 |
|
|
+ |
1078 |
|
|
+ if is_debug: |
1079 |
|
|
+ print_message("Telegram uid of {0} '{1}': {2}".format(tg.type, zbx_to, uid)) |
1080 |
|
|
+ |
1081 |
|
|
+ # add signature, turned off by default, you can turn it on in config |
1082 |
|
|
+ try: |
1083 |
|
|
+ if "--signature" in args or settings["signature"] or zbxtg_settings.zbx_tg_signature\ |
1084 |
|
|
+ and not "--signature_disable" in args and not settings["signature_disable"]: |
1085 |
|
|
+ if "--signature" in args: |
1086 |
|
|
+ settings["signature"] = args[args.index("--signature") + 1] |
1087 |
|
|
+ if not settings["signature"]: |
1088 |
|
|
+ settings["signature"] = zbxtg_settings.zbx_server |
1089 |
|
|
+ zbxtg_body_text.append("--") |
1090 |
|
|
+ zbxtg_body_text.append(settings["signature"]) |
1091 |
|
|
+ except: |
1092 |
|
|
+ pass |
1093 |
|
|
+ |
1094 |
|
|
+ # replace text with emojis |
1095 |
|
|
+ internal_using_emoji = False # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152 |
1096 |
|
|
+ if hasattr(zbxtg_settings, "emoji_map"): |
1097 |
|
|
+ zbxtg_body_text_emoji_support = [] |
1098 |
|
|
+ for l in zbxtg_body_text: |
1099 |
|
|
+ l_new = l |
1100 |
|
|
+ for k, v in list(zbxtg_settings.emoji_map.items()): |
1101 |
|
|
+ l_new = l_new.replace("{{" + k + "}}", v) |
1102 |
|
|
+ zbxtg_body_text_emoji_support.append(l_new) |
1103 |
|
|
+ if len("".join(zbxtg_body_text)) - len("".join(zbxtg_body_text_emoji_support)): |
1104 |
|
|
+ internal_using_emoji = True |
1105 |
|
|
+ zbxtg_body_text = zbxtg_body_text_emoji_support |
1106 |
|
|
+ |
1107 |
|
|
+ if not is_single_message: |
1108 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
1109 |
|
|
+ if not tg.ok: |
1110 |
|
|
+ # first case â if group has been migrated to a supergroup, we need to update chat_id of that group |
1111 |
|
|
+ if tg.error.find("migrated") > -1 and tg.error.find("supergroup") > -1: |
1112 |
|
|
+ migrate_to_chat_id = tg.result["parameters"]["migrate_to_chat_id"] |
1113 |
|
|
+ tg.update_cache_uid(zbx_to, migrate_to_chat_id, message="Group chat is migrated to supergroup, " |
1114 |
|
|
+ "updating cache file") |
1115 |
|
|
+ uid = migrate_to_chat_id |
1116 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
1117 |
|
|
+ |
1118 |
|
|
+ # another case if markdown is enabled and we got parse error, try to remove "bad" symbols from message |
1119 |
|
|
+ if tg.markdown and tg.error.find("Can't find end of the entity starting at byte offset") > -1: |
1120 |
|
|
+ markdown_warning = "Original message has been fixed due to {0}. " \ |
1121 |
|
|
+ "Please, fix the markdown, it's slowing down messages sending."\ |
1122 |
|
|
+ .format(url_wiki_base + "/" + settings_description["markdown"]["url"]) |
1123 |
|
|
+ markdown_fix_attempts = 0 |
1124 |
|
|
+ while not tg.ok and markdown_fix_attempts != 3: |
1125 |
|
|
+ offset = re.search("Can't find end of the entity starting at byte offset ([0-9]+)", tg.error).group(1) |
1126 |
|
|
+ zbxtg_body_text = markdown_fix(zbxtg_body_text, offset, emoji=internal_using_emoji) + \ |
1127 |
|
|
+ ["\n"] + [markdown_warning] |
1128 |
|
|
+ tg.disable_web_page_preview = True |
1129 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
1130 |
|
|
+ markdown_fix_attempts += 1 |
1131 |
|
|
+ if tg.ok: |
1132 |
|
|
+ print_message(markdown_warning) |
1133 |
|
|
+ |
1134 |
|
|
+ if is_debug: |
1135 |
|
|
+ print((tg.result)) |
1136 |
|
|
+ |
1137 |
|
|
+ if settings["zbxtg_image_age"]: |
1138 |
|
|
+ age_sec = age2sec(settings["zbxtg_image_age"]) |
1139 |
|
|
+ if age_sec > 0 and age_sec > 3600: |
1140 |
|
|
+ settings["zbxtg_image_period"] = age_sec |
1141 |
|
|
+ |
1142 |
|
|
+ message_id = 0 |
1143 |
|
|
+ if tg_method_image: |
1144 |
|
|
+ zbx.login() |
1145 |
|
|
+ if not zbx.cookie: |
1146 |
|
|
+ text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\ |
1147 |
|
|
+ "unable to send graphs check manually" |
1148 |
|
|
+ tg.send_message(uid, [text_warn]) |
1149 |
|
|
+ print_message(text_warn) |
1150 |
|
|
+ else: |
1151 |
|
|
+ if not settings["extimg"]: |
1152 |
|
|
+ zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"], |
1153 |
|
|
+ settings["zbxtg_title"], settings["zbxtg_image_width"], |
1154 |
|
|
+ settings["zbxtg_image_height"], version=zbx_version) |
1155 |
|
|
+ else: |
1156 |
|
|
+ zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir) |
1157 |
|
|
+ zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200) |
1158 |
|
|
+ if tg.ok: |
1159 |
|
|
+ message_id = tg.result["result"]["message_id"] |
1160 |
|
|
+ tg.reply_to_message_id = message_id |
1161 |
|
|
+ if not zbxtg_file_img: |
1162 |
|
|
+ text_warn = "Can't get graph image, check script manually, see logs, or disable graphs" |
1163 |
|
|
+ tg.send_message(uid, [text_warn]) |
1164 |
|
|
+ print_message(text_warn) |
1165 |
|
|
+ else: |
1166 |
|
|
+ if not is_single_message: |
1167 |
|
|
+ zbxtg_body_text = "" |
1168 |
|
|
+ else: |
1169 |
|
|
+ if is_modified: |
1170 |
|
|
+ text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\ |
1171 |
|
|
+ "the message has been cut to 200 symbols, "\ |
1172 |
|
|
+ "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\ |
1173 |
|
|
+ "#issuecomment-166895044" |
1174 |
|
|
+ print_message(text_warn) |
1175 |
|
|
+ if not is_single_message: |
1176 |
|
|
+ tg.disable_notification = True |
1177 |
|
|
+ tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img) |
1178 |
|
|
+ if tg.ok: |
1179 |
|
|
+ settings["zbxtg_body_text"] = zbxtg_body_text |
1180 |
|
|
+ os.remove(zbxtg_file_img) |
1181 |
|
|
+ else: |
1182 |
|
|
+ if tg.error.find("PHOTO_INVALID_DIMENSIONS") > -1: |
1183 |
|
|
+ if not tg.disable_web_page_preview: |
1184 |
|
|
+ tg.disable_web_page_preview = True |
1185 |
|
|
+ text_warn = "Zabbix user couldn't get graph (probably has no rights to get data from host), " \ |
1186 |
|
|
+ "check script manually, see {0}".format(url_wiki_base + "/" + |
1187 |
|
|
+ settings_description["graphs"]["url"]) |
1188 |
|
|
+ tg.send_message(uid, [text_warn]) |
1189 |
|
|
+ print_message(text_warn) |
1190 |
|
|
+ if tg.location and location_coordinates["latitude"] and location_coordinates["longitude"]: |
1191 |
|
|
+ tg.reply_to_message_id = message_id |
1192 |
|
|
+ tg.disable_notification = True |
1193 |
|
|
+ tg.send_location(to=uid, coordinates=location_coordinates) |
1194 |
|
|
+ |
1195 |
|
|
+ if "--show-settings" in args: |
1196 |
|
|
+ print_message("Settings: " + str(json.dumps(settings, indent=2))) |
1197 |
|
|
+ |
1198 |
|
|
+if __name__ == "__main__": |
1199 |
|
|
+ main() |
1200 |
|
|
diff -Nur --no-dereference smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg.py smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg.py |
1201 |
|
|
--- smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg.py 1969-12-31 19:00:00.000000000 -0500 |
1202 |
|
|
+++ smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg.py 2021-11-08 22:00:51.689000000 -0500 |
1203 |
|
|
@@ -0,0 +1,939 @@ |
1204 |
|
|
+#!/usr/bin/env python |
1205 |
|
|
+# coding: utf-8 |
1206 |
|
|
+ |
1207 |
|
|
+import sys |
1208 |
|
|
+import os |
1209 |
|
|
+import time |
1210 |
|
|
+import random |
1211 |
|
|
+import string |
1212 |
|
|
+import requests |
1213 |
|
|
+import json |
1214 |
|
|
+import re |
1215 |
|
|
+import stat |
1216 |
|
|
+import hashlib |
1217 |
|
|
+import subprocess |
1218 |
|
|
+#import sqlite3 |
1219 |
|
|
+from os.path import dirname |
1220 |
|
|
+import zbxtg_settings |
1221 |
|
|
+ |
1222 |
|
|
+ |
1223 |
|
|
+class Cache: |
1224 |
|
|
+ def __init__(self, database): |
1225 |
|
|
+ self.database = database |
1226 |
|
|
+ |
1227 |
|
|
+ def create_db(self, database): |
1228 |
|
|
+ pass |
1229 |
|
|
+ |
1230 |
|
|
+ |
1231 |
|
|
+class TelegramAPI: |
1232 |
|
|
+ tg_url_bot_general = "https://api.telegram.org/bot" |
1233 |
|
|
+ |
1234 |
|
|
+ def http_get(self, url): |
1235 |
|
|
+ answer = requests.get(url, proxies=self.proxies) |
1236 |
|
|
+ self.result = answer.json() |
1237 |
|
|
+ self.ok_update() |
1238 |
|
|
+ return self.result |
1239 |
|
|
+ |
1240 |
|
|
+ def __init__(self, key): |
1241 |
|
|
+ self.debug = False |
1242 |
|
|
+ self.key = key |
1243 |
|
|
+ self.proxies = {} |
1244 |
|
|
+ self.type = "private" # 'private' for private chats or 'group' for group chats |
1245 |
|
|
+ self.markdown = False |
1246 |
|
|
+ self.html = False |
1247 |
|
|
+ self.disable_web_page_preview = False |
1248 |
|
|
+ self.disable_notification = False |
1249 |
|
|
+ self.reply_to_message_id = 0 |
1250 |
|
|
+ self.tmp_dir = None |
1251 |
|
|
+ self.tmp_uids = None |
1252 |
|
|
+ self.location = {"latitude": None, "longitude": None} |
1253 |
|
|
+ self.update_offset = 0 |
1254 |
|
|
+ self.image_buttons = False |
1255 |
|
|
+ self.result = None |
1256 |
|
|
+ self.ok = None |
1257 |
|
|
+ self.error = None |
1258 |
|
|
+ self.get_updates_from_file = False |
1259 |
|
|
+ |
1260 |
|
|
+ def get_me(self): |
1261 |
|
|
+ url = self.tg_url_bot_general + self.key + "/getMe" |
1262 |
|
|
+ me = self.http_get(url) |
1263 |
|
|
+ return me |
1264 |
|
|
+ |
1265 |
|
|
+ def get_updates(self): |
1266 |
|
|
+ url = self.tg_url_bot_general + self.key + "/getUpdates" |
1267 |
|
|
+ params = {"offset": self.update_offset} |
1268 |
|
|
+ if self.debug: |
1269 |
|
|
+ print_message(url) |
1270 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
1271 |
|
|
+ self.result = answer.json() |
1272 |
|
|
+ if self.get_updates_from_file: |
1273 |
|
|
+ print_message("Getting updated from file getUpdates.txt") |
1274 |
|
|
+ self.result = json.loads("".join(file_read("getUpdates.txt"))) |
1275 |
|
|
+ if self.debug: |
1276 |
|
|
+ print_message("Content of /getUpdates:") |
1277 |
|
|
+ print_message(json.dumps(self.result)) |
1278 |
|
|
+ self.ok_update() |
1279 |
|
|
+ return self.result |
1280 |
|
|
+ |
1281 |
|
|
+ def send_message(self, to, message): |
1282 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendMessage" |
1283 |
|
|
+ message = "\n".join(message) |
1284 |
|
|
+ params = {"chat_id": to, "text": message, "disable_web_page_preview": self.disable_web_page_preview, |
1285 |
|
|
+ "disable_notification": self.disable_notification} |
1286 |
|
|
+ if self.reply_to_message_id: |
1287 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
1288 |
|
|
+ if self.markdown or self.html: |
1289 |
|
|
+ parse_mode = "HTML" |
1290 |
|
|
+ if self.markdown: |
1291 |
|
|
+ parse_mode = "Markdown" |
1292 |
|
|
+ params["parse_mode"] = parse_mode |
1293 |
|
|
+ if self.debug: |
1294 |
|
|
+ print_message("Trying to /sendMessage:") |
1295 |
|
|
+ print_message(url) |
1296 |
|
|
+ print_message("post params: " + str(params)) |
1297 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
1298 |
|
|
+ if answer.status_code == 414: |
1299 |
|
|
+ self.result = {"ok": False, "description": "414 URI Too Long"} |
1300 |
|
|
+ else: |
1301 |
|
|
+ self.result = answer.json() |
1302 |
|
|
+ self.ok_update() |
1303 |
|
|
+ return self.result |
1304 |
|
|
+ |
1305 |
|
|
+ def update_message(self, to, message_id, message): |
1306 |
|
|
+ url = self.tg_url_bot_general + self.key + "/editMessageText" |
1307 |
|
|
+ message = "\n".join(message) |
1308 |
|
|
+ params = {"chat_id": to, "message_id": message_id, "text": message, |
1309 |
|
|
+ "disable_web_page_preview": self.disable_web_page_preview, |
1310 |
|
|
+ "disable_notification": self.disable_notification} |
1311 |
|
|
+ if self.markdown or self.html: |
1312 |
|
|
+ parse_mode = "HTML" |
1313 |
|
|
+ if self.markdown: |
1314 |
|
|
+ parse_mode = "Markdown" |
1315 |
|
|
+ params["parse_mode"] = parse_mode |
1316 |
|
|
+ if self.debug: |
1317 |
|
|
+ print_message("Trying to /editMessageText:") |
1318 |
|
|
+ print_message(url) |
1319 |
|
|
+ print_message("post params: " + str(params)) |
1320 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
1321 |
|
|
+ self.result = answer.json() |
1322 |
|
|
+ self.ok_update() |
1323 |
|
|
+ return self.result |
1324 |
|
|
+ |
1325 |
|
|
+ def send_photo(self, to, message, path): |
1326 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendPhoto" |
1327 |
|
|
+ message = "\n".join(message) |
1328 |
|
|
+ if self.image_buttons: |
1329 |
|
|
+ reply_markup = json.dumps({"inline_keyboard": [[ |
1330 |
|
|
+ {"text": "R", "callback_data": "graph_refresh"}, |
1331 |
|
|
+ {"text": "1h", "callback_data": "graph_period_3600"}, |
1332 |
|
|
+ {"text": "3h", "callback_data": "graph_period_10800"}, |
1333 |
|
|
+ {"text": "6h", "callback_data": "graph_period_21600"}, |
1334 |
|
|
+ {"text": "12h", "callback_data": "graph_period_43200"}, |
1335 |
|
|
+ {"text": "24h", "callback_data": "graph_period_86400"}, |
1336 |
|
|
+ ], ]}) |
1337 |
|
|
+ else: |
1338 |
|
|
+ reply_markup = json.dumps({}) |
1339 |
|
|
+ params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification, |
1340 |
|
|
+ "reply_markup": reply_markup} |
1341 |
|
|
+ if self.reply_to_message_id: |
1342 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
1343 |
|
|
+ files = {"photo": open(path, 'rb')} |
1344 |
|
|
+ if self.debug: |
1345 |
|
|
+ print_message("Trying to /sendPhoto:") |
1346 |
|
|
+ print_message(url) |
1347 |
|
|
+ print_message(params) |
1348 |
|
|
+ print_message("files: " + str(files)) |
1349 |
|
|
+ answer = requests.post(url, params=params, files=files, proxies=self.proxies) |
1350 |
|
|
+ self.result = answer.json() |
1351 |
|
|
+ self.ok_update() |
1352 |
|
|
+ return self.result |
1353 |
|
|
+ |
1354 |
|
|
+ def send_txt(self, to, text, text_name=None): |
1355 |
|
|
+ path = self.tmp_dir + "/" + "zbxtg_txt_" |
1356 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendDocument" |
1357 |
|
|
+ text = "\n".join(text) |
1358 |
|
|
+ if not text_name: |
1359 |
|
|
+ path += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10)) |
1360 |
|
|
+ else: |
1361 |
|
|
+ path += text_name |
1362 |
|
|
+ path += ".txt" |
1363 |
|
|
+ file_write(path, text) |
1364 |
|
|
+ params = {"chat_id": to, "caption": path.split("/")[-1], "disable_notification": self.disable_notification} |
1365 |
|
|
+ if self.reply_to_message_id: |
1366 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
1367 |
|
|
+ files = {"document": open(path, 'rb')} |
1368 |
|
|
+ if self.debug: |
1369 |
|
|
+ print_message("Trying to /sendDocument:") |
1370 |
|
|
+ print_message(url) |
1371 |
|
|
+ print_message(params) |
1372 |
|
|
+ print_message("files: " + str(files)) |
1373 |
|
|
+ answer = requests.post(url, params=params, files=files, proxies=self.proxies) |
1374 |
|
|
+ self.result = answer.json() |
1375 |
|
|
+ self.ok_update() |
1376 |
|
|
+ return self.result |
1377 |
|
|
+ |
1378 |
|
|
+ def get_uid(self, name): |
1379 |
|
|
+ uid = 0 |
1380 |
|
|
+ if self.debug: |
1381 |
|
|
+ print_message("Getting uid from /getUpdates...") |
1382 |
|
|
+ updates = self.get_updates() |
1383 |
|
|
+ for m in updates["result"]: |
1384 |
|
|
+ if "message" in m: |
1385 |
|
|
+ chat = m["message"]["chat"] |
1386 |
|
|
+ elif "edited_message" in m: |
1387 |
|
|
+ chat = m["edited_message"]["chat"] |
1388 |
|
|
+ else: |
1389 |
|
|
+ continue |
1390 |
|
|
+ if chat["type"] == self.type == "private": |
1391 |
|
|
+ if "username" in chat: |
1392 |
|
|
+ if chat["username"] == name: |
1393 |
|
|
+ uid = chat["id"] |
1394 |
|
|
+ if (chat["type"] == "group" or chat["type"] == "supergroup") and self.type == "group": |
1395 |
|
|
+ if "title" in chat: |
1396 |
|
|
+ if sys.version_info[0] < 3: |
1397 |
|
|
+ if chat["title"] == name.decode("utf-8"): |
1398 |
|
|
+ uid = chat["id"] |
1399 |
|
|
+ else: |
1400 |
|
|
+ if chat["title"] == name: |
1401 |
|
|
+ uid = chat["id"] |
1402 |
|
|
+ return uid |
1403 |
|
|
+ |
1404 |
|
|
+ def error_need_to_contact(self, to): |
1405 |
|
|
+ if self.type == "private": |
1406 |
|
|
+ print_message("User '{0}' needs to send some text bot in private".format(to)) |
1407 |
|
|
+ if self.type == "group": |
1408 |
|
|
+ print_message("You need start a conversation with your bot first in '{0}' group chat, type '/start@{1}'" |
1409 |
|
|
+ .format(to, self.get_me()["result"]["username"])) |
1410 |
|
|
+ |
1411 |
|
|
+ def update_cache_uid(self, name, uid, message="Add new string to cache file"): |
1412 |
|
|
+ cache_string = "{0};{1};{2}\n".format(name, self.type, str(uid).rstrip()) |
1413 |
|
|
+ # FIXME |
1414 |
|
|
+ if self.debug: |
1415 |
|
|
+ print_message("{0}: {1}".format(message, cache_string)) |
1416 |
|
|
+ with open(self.tmp_uids, "a") as cache_file_uids: |
1417 |
|
|
+ cache_file_uids.write(cache_string) |
1418 |
|
|
+ return True |
1419 |
|
|
+ |
1420 |
|
|
+ def get_uid_from_cache(self, name): |
1421 |
|
|
+ if self.debug: |
1422 |
|
|
+ print_message("Trying to read cached uid for {0}, {1}, from {2}".format(name, self.type, self.tmp_uids)) |
1423 |
|
|
+ uid = 0 |
1424 |
|
|
+ if os.path.isfile(self.tmp_uids): |
1425 |
|
|
+ with open(self.tmp_uids, 'r') as cache_file_uids: |
1426 |
|
|
+ cache_uids_old = cache_file_uids.readlines() |
1427 |
|
|
+ for u in cache_uids_old: |
1428 |
|
|
+ u_splitted = u.split(";") |
1429 |
|
|
+ if name == u_splitted[0] and self.type == u_splitted[1]: |
1430 |
|
|
+ uid = u_splitted[2] |
1431 |
|
|
+ return uid |
1432 |
|
|
+ |
1433 |
|
|
+ def send_location(self, to, coordinates): |
1434 |
|
|
+ url = self.tg_url_bot_general + self.key + "/sendLocation" |
1435 |
|
|
+ params = {"chat_id": to, "disable_notification": self.disable_notification, |
1436 |
|
|
+ "latitude": coordinates["latitude"], "longitude": coordinates["longitude"]} |
1437 |
|
|
+ if self.reply_to_message_id: |
1438 |
|
|
+ params["reply_to_message_id"] = self.reply_to_message_id |
1439 |
|
|
+ if self.debug: |
1440 |
|
|
+ print_message("Trying to /sendLocation:") |
1441 |
|
|
+ print_message(url) |
1442 |
|
|
+ print_message("post params: " + str(params)) |
1443 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
1444 |
|
|
+ self.result = answer.json() |
1445 |
|
|
+ self.ok_update() |
1446 |
|
|
+ return self.result |
1447 |
|
|
+ |
1448 |
|
|
+ def answer_callback_query(self, callback_query_id, text=None): |
1449 |
|
|
+ url = self.tg_url_bot_general + self.key + "/answerCallbackQuery" |
1450 |
|
|
+ if not text: |
1451 |
|
|
+ params = {"callback_query_id": callback_query_id} |
1452 |
|
|
+ else: |
1453 |
|
|
+ params = {"callback_query_id": callback_query_id, "text": text} |
1454 |
|
|
+ answer = requests.post(url, params=params, proxies=self.proxies) |
1455 |
|
|
+ self.result = answer.json() |
1456 |
|
|
+ self.ok_update() |
1457 |
|
|
+ return self.result |
1458 |
|
|
+ |
1459 |
|
|
+ def ok_update(self): |
1460 |
|
|
+ self.ok = self.result["ok"] |
1461 |
|
|
+ if self.ok: |
1462 |
|
|
+ self.error = None |
1463 |
|
|
+ else: |
1464 |
|
|
+ self.error = self.result["description"] |
1465 |
|
|
+ print_message(self.error) |
1466 |
|
|
+ return True |
1467 |
|
|
+ |
1468 |
|
|
+ |
1469 |
|
|
+def markdown_fix(message, offset, emoji=False): |
1470 |
|
|
+ offset = int(offset) |
1471 |
|
|
+ if emoji: # https://github.com/ableev/Zabbix-in-Telegram/issues/152 |
1472 |
|
|
+ offset -= 2 |
1473 |
|
|
+ message = "\n".join(message) |
1474 |
|
|
+ message = message[:offset] + message[offset+1:] |
1475 |
|
|
+ message = message.split("\n") |
1476 |
|
|
+ return message |
1477 |
|
|
+ |
1478 |
|
|
+ |
1479 |
|
|
+class ZabbixWeb: |
1480 |
|
|
+ def __init__(self, server, username, password): |
1481 |
|
|
+ self.debug = False |
1482 |
|
|
+ self.server = server |
1483 |
|
|
+ self.username = username |
1484 |
|
|
+ self.password = password |
1485 |
|
|
+ self.proxies = {} |
1486 |
|
|
+ self.verify = True |
1487 |
|
|
+ self.cookie = None |
1488 |
|
|
+ self.basic_auth_user = None |
1489 |
|
|
+ self.basic_auth_pass = None |
1490 |
|
|
+ self.tmp_dir = None |
1491 |
|
|
+ |
1492 |
|
|
+ def login(self): |
1493 |
|
|
+ if not self.verify: |
1494 |
|
|
+ requests.packages.urllib3.disable_warnings() |
1495 |
|
|
+ |
1496 |
|
|
+ data_api = {"name": self.username, "password": self.password, "enter": "Sign in"} |
1497 |
|
|
+ answer = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify, |
1498 |
|
|
+ auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) |
1499 |
|
|
+ cookie = answer.cookies |
1500 |
|
|
+ if len(answer.history) > 1 and answer.history[0].status_code == 302: |
1501 |
|
|
+ print_message("probably the server in your config file has not full URL (for example " |
1502 |
|
|
+ "'{0}' instead of '{1}')".format(self.server, self.server + "/zabbix")) |
1503 |
|
|
+ if not cookie: |
1504 |
|
|
+ print_message("authorization has failed, url: {0}".format(self.server + "/")) |
1505 |
|
|
+ cookie = None |
1506 |
|
|
+ |
1507 |
|
|
+ self.cookie = cookie |
1508 |
|
|
+ |
1509 |
|
|
+ def graph_get(self, itemid, period, title, width, height, version=3): |
1510 |
|
|
+ file_img = self.tmp_dir + "/{0}.png".format("".join(itemid)) |
1511 |
|
|
+ |
1512 |
|
|
+ title = requests.utils.quote(title) |
1513 |
|
|
+ |
1514 |
|
|
+ colors = { |
1515 |
|
|
+ 0: "00CC00", |
1516 |
|
|
+ 1: "CC0000", |
1517 |
|
|
+ 2: "0000CC", |
1518 |
|
|
+ 3: "CCCC00", |
1519 |
|
|
+ 4: "00CCCC", |
1520 |
|
|
+ 5: "CC00CC", |
1521 |
|
|
+ } |
1522 |
|
|
+ |
1523 |
|
|
+ drawtype = 5 |
1524 |
|
|
+ if len(itemid) > 1: |
1525 |
|
|
+ drawtype = 2 |
1526 |
|
|
+ |
1527 |
|
|
+ zbx_img_url_itemids = [] |
1528 |
|
|
+ for i in range(0, len(itemid)): |
1529 |
|
|
+ itemid_url = "&items[{0}][itemid]={1}&items[{0}][sortorder]={0}&" \ |
1530 |
|
|
+ "items[{0}][drawtype]={3}&items[{0}][color]={2}".format(i, itemid[i], colors[i], drawtype) |
1531 |
|
|
+ zbx_img_url_itemids.append(itemid_url) |
1532 |
|
|
+ |
1533 |
|
|
+ zbx_img_url = self.server + "/chart3.php?" |
1534 |
|
|
+ if version < 4: |
1535 |
|
|
+ zbx_img_url += "period={0}".format(period) |
1536 |
|
|
+ else: |
1537 |
|
|
+ zbx_img_url += "from=now-{0}&to=now".format(period) |
1538 |
|
|
+ zbx_img_url += "&name={0}&width={1}&height={2}&graphtype=0&legend=1".format(title, width, height) |
1539 |
|
|
+ zbx_img_url += "".join(zbx_img_url_itemids) |
1540 |
|
|
+ |
1541 |
|
|
+ if self.debug: |
1542 |
|
|
+ print_message(zbx_img_url) |
1543 |
|
|
+ answer = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify, |
1544 |
|
|
+ auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) |
1545 |
|
|
+ status_code = answer.status_code |
1546 |
|
|
+ if status_code == 404: |
1547 |
|
|
+ print_message("can't get image from '{0}'".format(zbx_img_url)) |
1548 |
|
|
+ return False |
1549 |
|
|
+ res_img = answer.content |
1550 |
|
|
+ file_bwrite(file_img, res_img) |
1551 |
|
|
+ return file_img |
1552 |
|
|
+ |
1553 |
|
|
+ def api_test(self): |
1554 |
|
|
+ headers = {'Content-type': 'application/json'} |
1555 |
|
|
+ api_data = json.dumps({"jsonrpc": "2.0", "method": "user.login", "params": |
1556 |
|
|
+ {"user": self.username, "password": self.password}, "id": 1}) |
1557 |
|
|
+ api_url = self.server + "/api_jsonrpc.php" |
1558 |
|
|
+ api = requests.post(api_url, data=api_data, proxies=self.proxies, headers=headers) |
1559 |
|
|
+ return api.text |
1560 |
|
|
+ |
1561 |
|
|
+ |
1562 |
|
|
+def print_message(message): |
1563 |
|
|
+ message = str(message) + "\n" |
1564 |
|
|
+ filename = sys.argv[0].split("/")[-1] |
1565 |
|
|
+ sys.stderr.write(filename + ": " + message) |
1566 |
|
|
+ |
1567 |
|
|
+ |
1568 |
|
|
+def list_cut(elements, symbols_limit): |
1569 |
|
|
+ symbols_count = symbols_count_now = 0 |
1570 |
|
|
+ elements_new = [] |
1571 |
|
|
+ element_last_list = [] |
1572 |
|
|
+ for e in elements: |
1573 |
|
|
+ symbols_count_now = symbols_count + len(e) |
1574 |
|
|
+ if symbols_count_now > symbols_limit: |
1575 |
|
|
+ limit_idx = symbols_limit - symbols_count |
1576 |
|
|
+ e_list = list(e) |
1577 |
|
|
+ for idx, ee in enumerate(e_list): |
1578 |
|
|
+ if idx < limit_idx: |
1579 |
|
|
+ element_last_list.append(ee) |
1580 |
|
|
+ else: |
1581 |
|
|
+ break |
1582 |
|
|
+ break |
1583 |
|
|
+ else: |
1584 |
|
|
+ symbols_count = symbols_count_now + 1 |
1585 |
|
|
+ elements_new.append(e) |
1586 |
|
|
+ if symbols_count_now < symbols_limit: |
1587 |
|
|
+ return elements, False |
1588 |
|
|
+ else: |
1589 |
|
|
+ element_last = "".join(element_last_list) |
1590 |
|
|
+ elements_new.append(element_last) |
1591 |
|
|
+ return elements_new, True |
1592 |
|
|
+ |
1593 |
|
|
+ |
1594 |
|
|
+class Maps: |
1595 |
|
|
+ # https://developers.google.com/maps/documentation/geocoding/intro |
1596 |
|
|
+ def __init__(self): |
1597 |
|
|
+ self.key = None |
1598 |
|
|
+ self.proxies = {} |
1599 |
|
|
+ |
1600 |
|
|
+ def get_coordinates_by_address(self, address): |
1601 |
|
|
+ coordinates = {"latitude": 0, "longitude": 0} |
1602 |
|
|
+ url_api = "https://maps.googleapis.com/maps/api/geocode/json?key={0}&address={1}".format(self.key, address) |
1603 |
|
|
+ url = url_api |
1604 |
|
|
+ answer = requests.get(url, proxies=self.proxies) |
1605 |
|
|
+ result = answer.json() |
1606 |
|
|
+ try: |
1607 |
|
|
+ coordinates_dict = result["results"][0]["geometry"]["location"] |
1608 |
|
|
+ except: |
1609 |
|
|
+ if "error_message" in result: |
1610 |
|
|
+ print_message("[" + result["status"] + "]: " + result["error_message"]) |
1611 |
|
|
+ return coordinates |
1612 |
|
|
+ coordinates = {"latitude": coordinates_dict["lat"], "longitude": coordinates_dict["lng"]} |
1613 |
|
|
+ return coordinates |
1614 |
|
|
+ |
1615 |
|
|
+ |
1616 |
|
|
+def file_write(filename, text): |
1617 |
|
|
+ with open(filename, "w") as fd: |
1618 |
|
|
+ fd.write(str(text)) |
1619 |
|
|
+ return True |
1620 |
|
|
+ |
1621 |
|
|
+ |
1622 |
|
|
+def file_bwrite(filename, data): |
1623 |
|
|
+ with open(filename, "wb") as fd: |
1624 |
|
|
+ fd.write(data) |
1625 |
|
|
+ return True |
1626 |
|
|
+ |
1627 |
|
|
+ |
1628 |
|
|
+def file_read(filename): |
1629 |
|
|
+ with open(filename, "r") as fd: |
1630 |
|
|
+ text = fd.readlines() |
1631 |
|
|
+ return text |
1632 |
|
|
+ |
1633 |
|
|
+ |
1634 |
|
|
+def file_append(filename, text): |
1635 |
|
|
+ with open(filename, "a") as fd: |
1636 |
|
|
+ fd.write(str(text)) |
1637 |
|
|
+ return True |
1638 |
|
|
+ |
1639 |
|
|
+ |
1640 |
|
|
+def external_image_get(url, tmp_dir, timeout=6): |
1641 |
|
|
+ image_hash = hashlib.md5() |
1642 |
|
|
+ image_hash.update(url.encode()) |
1643 |
|
|
+ file_img = tmp_dir + "/external_{0}.png".format(image_hash.hexdigest()) |
1644 |
|
|
+ try: |
1645 |
|
|
+ answer = requests.get(url, timeout=timeout, allow_redirects=True) |
1646 |
|
|
+ except requests.exceptions.ReadTimeout as ex: |
1647 |
|
|
+ print_message("Can't get external image from '{0}': timeout".format(url)) |
1648 |
|
|
+ return False |
1649 |
|
|
+ status_code = answer.status_code |
1650 |
|
|
+ if status_code == 404: |
1651 |
|
|
+ print_message("Can't get external image from '{0}': HTTP 404 error".format(url)) |
1652 |
|
|
+ return False |
1653 |
|
|
+ answer_image = answer.content |
1654 |
|
|
+ file_bwrite(file_img, answer_image) |
1655 |
|
|
+ return file_img |
1656 |
|
|
+ |
1657 |
|
|
+ |
1658 |
|
|
+def age2sec(age_str): |
1659 |
|
|
+ age_sec = 0 |
1660 |
|
|
+ age_regex = "([0-9]+d)?\s?([0-9]+h)?\s?([0-9]+m)?" |
1661 |
|
|
+ age_pattern = re.compile(age_regex) |
1662 |
|
|
+ intervals = age_pattern.match(age_str).groups() |
1663 |
|
|
+ for i in intervals: |
1664 |
|
|
+ if i: |
1665 |
|
|
+ metric = i[-1] |
1666 |
|
|
+ if metric == "d": |
1667 |
|
|
+ age_sec += int(i[0:-1])*86400 |
1668 |
|
|
+ if metric == "h": |
1669 |
|
|
+ age_sec += int(i[0:-1])*3600 |
1670 |
|
|
+ if metric == "m": |
1671 |
|
|
+ age_sec += int(i[0:-1])*60 |
1672 |
|
|
+ return age_sec |
1673 |
|
|
+ |
1674 |
|
|
+ |
1675 |
|
|
+def main(): |
1676 |
|
|
+ |
1677 |
|
|
+ tmp_dir = zbxtg_settings.zbx_tg_tmp_dir |
1678 |
|
|
+ if tmp_dir == "/tmp/" + zbxtg_settings.zbx_tg_prefix: |
1679 |
|
|
+ print_message("WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!") |
1680 |
|
|
+ print_message("https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings") |
1681 |
|
|
+ |
1682 |
|
|
+ tmp_cookie = tmp_dir + "/cookie.py.txt" |
1683 |
|
|
+ tmp_uids = tmp_dir + "/uids.txt" |
1684 |
|
|
+ tmp_need_update = False # do we need to update cache file with uids or not |
1685 |
|
|
+ |
1686 |
|
|
+ rnd = random.randint(0, 999) |
1687 |
|
|
+ ts = time.time() |
1688 |
|
|
+ hash_ts = str(ts) + "." + str(rnd) |
1689 |
|
|
+ |
1690 |
|
|
+ log_file = "/dev/null" |
1691 |
|
|
+ |
1692 |
|
|
+ args = sys.argv |
1693 |
|
|
+ |
1694 |
|
|
+ settings = { |
1695 |
|
|
+ "zbxtg_itemid": "0", # itemid for graph |
1696 |
|
|
+ "zbxtg_title": None, # title for graph |
1697 |
|
|
+ "zbxtg_image_period": None, |
1698 |
|
|
+ "zbxtg_image_age": "3600", |
1699 |
|
|
+ "zbxtg_image_width": "900", |
1700 |
|
|
+ "zbxtg_image_height": "200", |
1701 |
|
|
+ "tg_method_image": False, # if True - default send images, False - send text |
1702 |
|
|
+ "tg_chat": False, # send message to chat or in private |
1703 |
|
|
+ "tg_group": False, # send message to chat or in private |
1704 |
|
|
+ "is_debug": False, |
1705 |
|
|
+ "is_channel": False, |
1706 |
|
|
+ "disable_web_page_preview": False, |
1707 |
|
|
+ "location": None, # address |
1708 |
|
|
+ "lat": 0, # latitude |
1709 |
|
|
+ "lon": 0, # longitude |
1710 |
|
|
+ "is_single_message": False, |
1711 |
|
|
+ "markdown": False, |
1712 |
|
|
+ "html": False, |
1713 |
|
|
+ "signature": None, |
1714 |
|
|
+ "signature_disable": False, |
1715 |
|
|
+ "graph_buttons": False, |
1716 |
|
|
+ "extimg": None, |
1717 |
|
|
+ "to": None, |
1718 |
|
|
+ "to_group": None, |
1719 |
|
|
+ "forked": False, |
1720 |
|
|
+ } |
1721 |
|
|
+ |
1722 |
|
|
+ url_github = "https://github.com/ableev/Zabbix-in-Telegram" |
1723 |
|
|
+ url_wiki_base = "https://github.com/ableev/Zabbix-in-Telegram/wiki" |
1724 |
|
|
+ url_tg_group = "https://t.me/ZbxTg" |
1725 |
|
|
+ url_tg_channel = "https://t.me/Zabbix_in_Telegram" |
1726 |
|
|
+ |
1727 |
|
|
+ settings_description = { |
1728 |
|
|
+ "itemid": {"name": "zbxtg_itemid", "type": "list", |
1729 |
|
|
+ "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs"}, |
1730 |
|
|
+ "title": {"name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs"}, |
1731 |
|
|
+ "graphs_period": {"name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs"}, |
1732 |
|
|
+ "graphs_age": {"name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs"}, |
1733 |
|
|
+ "graphs_width": {"name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs"}, |
1734 |
|
|
+ "graphs_height": {"name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs"}, |
1735 |
|
|
+ "graphs": {"name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs"}, |
1736 |
|
|
+ "chat": {"name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'", |
1737 |
|
|
+ "url": "How-to-send-message-to-the-group-chat"}, |
1738 |
|
|
+ "group": {"name": "tg_group", "type": "bool", "help": "sends message to a group", |
1739 |
|
|
+ "url": "How-to-send-message-to-the-group-chat"}, |
1740 |
|
|
+ "debug": {"name": "is_debug", "type": "bool", "help": "enables 'debug'", |
1741 |
|
|
+ "url": "How-to-test-script-in-command-line"}, |
1742 |
|
|
+ "channel": {"name": "is_channel", "type": "bool", "help": "sends message to a channel", |
1743 |
|
|
+ "url": "Channel-support"}, |
1744 |
|
|
+ "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool", |
1745 |
|
|
+ "help": "disable web page preview", "url": "Disable-web-page-preview"}, |
1746 |
|
|
+ "location": {"name": "location", "type": "str", "help": "address of location", "url": "Location"}, |
1747 |
|
|
+ "lat": {"name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location"}, |
1748 |
|
|
+ "lon": {"name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location"}, |
1749 |
|
|
+ "single_message": {"name": "is_single_message", "type": "bool", "help": "do not split message and graph", |
1750 |
|
|
+ "url": "Why-am-I-getting-two-messages-instead-of-one"}, |
1751 |
|
|
+ "markdown": {"name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, |
1752 |
|
|
+ "html": {"name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, |
1753 |
|
|
+ "signature": {"name": "signature", "type": "str", |
1754 |
|
|
+ "help": "bot's signature", "url": "Bot-signature"}, |
1755 |
|
|
+ "signature_disable": {"name": "signature_disable", "type": "bool", |
1756 |
|
|
+ "help": "enables/disables bot's signature", "url": "Bot-signature"}, |
1757 |
|
|
+ "graph_buttons": {"name": "graph_buttons", "type": "bool", |
1758 |
|
|
+ "help": "activates buttons under graph, could be using in ZbxTgDaemon", |
1759 |
|
|
+ "url": "Interactive-bot"}, |
1760 |
|
|
+ "external_image": {"name": "extimg", "type": "str", |
1761 |
|
|
+ "help": "should be url; attaches external image from different source", |
1762 |
|
|
+ "url": "External-image-as-graph"}, |
1763 |
|
|
+ "to": {"name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", |
1764 |
|
|
+ "url": "Custom-to-and-to_group"}, |
1765 |
|
|
+ "to_group": {"name": "to_group", "type": "str", |
1766 |
|
|
+ "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group"}, |
1767 |
|
|
+ "forked": {"name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": ""}, |
1768 |
|
|
+ } |
1769 |
|
|
+ |
1770 |
|
|
+ if len(args) < 4: |
1771 |
|
|
+ do_not_exit = False |
1772 |
|
|
+ if "--features" in args: |
1773 |
|
|
+ print(("List of available settings, see {0}/Settings\n---".format(url_wiki_base))) |
1774 |
|
|
+ for sett, proprt in list(settings_description.items()): |
1775 |
|
|
+ print(("{0}: {1}\ndoc: {2}/{3}\n--".format(sett, proprt["help"], url_wiki_base, proprt["url"]))) |
1776 |
|
|
+ |
1777 |
|
|
+ elif "--show-settings" in args: |
1778 |
|
|
+ do_not_exit = True |
1779 |
|
|
+ print_message("Settings: " + str(json.dumps(settings, indent=2))) |
1780 |
|
|
+ |
1781 |
|
|
+ else: |
1782 |
|
|
+ print(("Hi. You should provide at least three arguments.\n" |
1783 |
|
|
+ "zbxtg.py [TO] [SUBJECT] [BODY]\n\n" |
1784 |
|
|
+ "1. Read main page and/or wiki: {0} + {1}\n" |
1785 |
|
|
+ "2. Public Telegram group (discussion): {2}\n" |
1786 |
|
|
+ "3. Public Telegram channel: {3}\n" |
1787 |
|
|
+ "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev" |
1788 |
|
|
+ .format(url_github, url_wiki_base, url_tg_group, url_tg_channel))) |
1789 |
|
|
+ if not do_not_exit: |
1790 |
|
|
+ sys.exit(0) |
1791 |
|
|
+ |
1792 |
|
|
+ |
1793 |
|
|
+ zbx_to = args[1] |
1794 |
|
|
+ zbx_subject = args[2] |
1795 |
|
|
+ zbx_body = args[3] |
1796 |
|
|
+ |
1797 |
|
|
+ tg = TelegramAPI(key=zbxtg_settings.tg_key) |
1798 |
|
|
+ |
1799 |
|
|
+ tg.tmp_dir = tmp_dir |
1800 |
|
|
+ tg.tmp_uids = tmp_uids |
1801 |
|
|
+ |
1802 |
|
|
+ if zbxtg_settings.proxy_to_tg: |
1803 |
|
|
+ proxy_to_tg = zbxtg_settings.proxy_to_tg |
1804 |
|
|
+ if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"): |
1805 |
|
|
+ proxy_to_tg = "https://" + proxy_to_tg |
1806 |
|
|
+ tg.proxies = { |
1807 |
|
|
+ "https": "{0}".format(proxy_to_tg), |
1808 |
|
|
+ } |
1809 |
|
|
+ |
1810 |
|
|
+ zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user, |
1811 |
|
|
+ password=zbxtg_settings.zbx_api_pass) |
1812 |
|
|
+ |
1813 |
|
|
+ zbx.tmp_dir = tmp_dir |
1814 |
|
|
+ |
1815 |
|
|
+ # workaround for Zabbix 4.x |
1816 |
|
|
+ zbx_version = 3 |
1817 |
|
|
+ |
1818 |
|
|
+ try: |
1819 |
|
|
+ zbx_version = zbxtg_settings.zbx_server_version |
1820 |
|
|
+ except: |
1821 |
|
|
+ pass |
1822 |
|
|
+ |
1823 |
|
|
+ if zbxtg_settings.proxy_to_zbx: |
1824 |
|
|
+ zbx.proxies = { |
1825 |
|
|
+ "http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx), |
1826 |
|
|
+ "https": "https://{0}/".format(zbxtg_settings.proxy_to_zbx) |
1827 |
|
|
+ } |
1828 |
|
|
+ |
1829 |
|
|
+ # https://github.com/ableev/Zabbix-in-Telegram/issues/55 |
1830 |
|
|
+ try: |
1831 |
|
|
+ if zbxtg_settings.zbx_basic_auth: |
1832 |
|
|
+ zbx.basic_auth_user = zbxtg_settings.zbx_basic_auth_user |
1833 |
|
|
+ zbx.basic_auth_pass = zbxtg_settings.zbx_basic_auth_pass |
1834 |
|
|
+ except: |
1835 |
|
|
+ pass |
1836 |
|
|
+ |
1837 |
|
|
+ try: |
1838 |
|
|
+ zbx_api_verify = zbxtg_settings.zbx_api_verify |
1839 |
|
|
+ zbx.verify = zbx_api_verify |
1840 |
|
|
+ except: |
1841 |
|
|
+ pass |
1842 |
|
|
+ |
1843 |
|
|
+ map = Maps() |
1844 |
|
|
+ # api key to resolve address to coordinates via google api |
1845 |
|
|
+ try: |
1846 |
|
|
+ if zbxtg_settings.google_maps_api_key: |
1847 |
|
|
+ map.key = zbxtg_settings.google_maps_api_key |
1848 |
|
|
+ if zbxtg_settings.proxy_to_tg: |
1849 |
|
|
+ map.proxies = { |
1850 |
|
|
+ "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg), |
1851 |
|
|
+ "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg) |
1852 |
|
|
+ } |
1853 |
|
|
+ except: |
1854 |
|
|
+ pass |
1855 |
|
|
+ |
1856 |
|
|
+ zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines() |
1857 |
|
|
+ zbxtg_body_text = [] |
1858 |
|
|
+ |
1859 |
|
|
+ for line in zbxtg_body: |
1860 |
|
|
+ if line.find(zbxtg_settings.zbx_tg_prefix) > -1: |
1861 |
|
|
+ setting = re.split("[\s:=]+", line, maxsplit=1) |
1862 |
|
|
+ key = setting[0].replace(zbxtg_settings.zbx_tg_prefix + ";", "") |
1863 |
|
|
+ if key not in settings_description: |
1864 |
|
|
+ if "--debug" in args: |
1865 |
|
|
+ print_message("[ERROR] There is no '{0}' method, use --features to get help".format(key)) |
1866 |
|
|
+ continue |
1867 |
|
|
+ if settings_description[key]["type"] == "list": |
1868 |
|
|
+ value = setting[1].split(",") |
1869 |
|
|
+ elif len(setting) > 1 and len(setting[1]) > 0: |
1870 |
|
|
+ value = setting[1] |
1871 |
|
|
+ elif settings_description[key]["type"] == "bool": |
1872 |
|
|
+ value = True |
1873 |
|
|
+ else: |
1874 |
|
|
+ value = settings[settings_description[key]["name"]] |
1875 |
|
|
+ if key in settings_description: |
1876 |
|
|
+ settings[settings_description[key]["name"]] = value |
1877 |
|
|
+ else: |
1878 |
|
|
+ zbxtg_body_text.append(line) |
1879 |
|
|
+ |
1880 |
|
|
+ tg_method_image = bool(settings["tg_method_image"]) |
1881 |
|
|
+ tg_chat = bool(settings["tg_chat"]) |
1882 |
|
|
+ tg_group = bool(settings["tg_group"]) |
1883 |
|
|
+ is_debug = bool(settings["is_debug"]) |
1884 |
|
|
+ is_channel = bool(settings["is_channel"]) |
1885 |
|
|
+ disable_web_page_preview = bool(settings["disable_web_page_preview"]) |
1886 |
|
|
+ is_single_message = bool(settings["is_single_message"]) |
1887 |
|
|
+ |
1888 |
|
|
+ # experimental way to send message to the group https://github.com/ableev/Zabbix-in-Telegram/issues/15 |
1889 |
|
|
+ if args[0].split("/")[-1] == "zbxtg_group.py" or "--group" in args or tg_chat or tg_group: |
1890 |
|
|
+ tg_chat = True |
1891 |
|
|
+ tg_group = True |
1892 |
|
|
+ tg.type = "group" |
1893 |
|
|
+ |
1894 |
|
|
+ if "--debug" in args or is_debug: |
1895 |
|
|
+ is_debug = True |
1896 |
|
|
+ tg.debug = True |
1897 |
|
|
+ zbx.debug = True |
1898 |
|
|
+ print_message(tg.get_me()) |
1899 |
|
|
+ print_message("Cache file with uids: " + tg.tmp_uids) |
1900 |
|
|
+ log_file = tmp_dir + ".debug." + hash_ts + ".log" |
1901 |
|
|
+ #print_message(log_file) |
1902 |
|
|
+ |
1903 |
|
|
+ if "--markdown" in args or settings["markdown"]: |
1904 |
|
|
+ tg.markdown = True |
1905 |
|
|
+ |
1906 |
|
|
+ if "--html" in args or settings["html"]: |
1907 |
|
|
+ tg.html = True |
1908 |
|
|
+ |
1909 |
|
|
+ if "--channel" in args or is_channel: |
1910 |
|
|
+ tg.type = "channel" |
1911 |
|
|
+ |
1912 |
|
|
+ if "--disable_web_page_preview" in args or disable_web_page_preview: |
1913 |
|
|
+ if is_debug: |
1914 |
|
|
+ print_message("'disable_web_page_preview' option has been enabled") |
1915 |
|
|
+ tg.disable_web_page_preview = True |
1916 |
|
|
+ |
1917 |
|
|
+ if "--graph_buttons" in args or settings["graph_buttons"]: |
1918 |
|
|
+ tg.image_buttons = True |
1919 |
|
|
+ |
1920 |
|
|
+ if "--forked" in args: |
1921 |
|
|
+ settings["forked"] = True |
1922 |
|
|
+ |
1923 |
|
|
+ if "--tg-key" in args: |
1924 |
|
|
+ tg.key = args[args.index("--tg-key") + 1] |
1925 |
|
|
+ |
1926 |
|
|
+ location_coordinates = {"latitude": None, "longitude": None} |
1927 |
|
|
+ if settings["lat"] > 0 and settings["lat"] > 0: |
1928 |
|
|
+ location_coordinates = {"latitude": settings["lat"], "longitude": settings["lon"]} |
1929 |
|
|
+ tg.location = location_coordinates |
1930 |
|
|
+ else: |
1931 |
|
|
+ if settings["location"]: |
1932 |
|
|
+ location_coordinates = map.get_coordinates_by_address(settings["location"]) |
1933 |
|
|
+ if location_coordinates: |
1934 |
|
|
+ settings["lat"] = location_coordinates["latitude"] |
1935 |
|
|
+ settings["lon"] = location_coordinates["longitude"] |
1936 |
|
|
+ tg.location = location_coordinates |
1937 |
|
|
+ |
1938 |
|
|
+ if not os.path.isdir(tmp_dir): |
1939 |
|
|
+ if is_debug: |
1940 |
|
|
+ print_message("Tmp dir doesn't exist, creating new one...") |
1941 |
|
|
+ try: |
1942 |
|
|
+ os.makedirs(tmp_dir) |
1943 |
|
|
+ open(tg.tmp_uids, "a").close() |
1944 |
|
|
+ os.chmod(tmp_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
1945 |
|
|
+ os.chmod(tg.tmp_uids, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
1946 |
|
|
+ except: |
1947 |
|
|
+ tmp_dir = "/tmp" |
1948 |
|
|
+ if is_debug: |
1949 |
|
|
+ print_message("Using {0} as a temporary dir".format(tmp_dir)) |
1950 |
|
|
+ |
1951 |
|
|
+ done_all_work_in_the_fork = False |
1952 |
|
|
+ # issue75 |
1953 |
|
|
+ |
1954 |
|
|
+ to_types = ["to", "to_group", "to_channel"] |
1955 |
|
|
+ to_types_to_telegram = {"to": "private", "to_group": "group", "to_channel": "channel"} |
1956 |
|
|
+ multiple_to = {} |
1957 |
|
|
+ for i in to_types: |
1958 |
|
|
+ multiple_to[i]=[] |
1959 |
|
|
+ |
1960 |
|
|
+ for t in to_types: |
1961 |
|
|
+ try: |
1962 |
|
|
+ if settings[t] and not settings["forked"]: |
1963 |
|
|
+ # zbx_to = settings["to"] |
1964 |
|
|
+ multiple_to[t] = re.split(",", settings[t]) |
1965 |
|
|
+ except KeyError: |
1966 |
|
|
+ pass |
1967 |
|
|
+ |
1968 |
|
|
+ # example: |
1969 |
|
|
+ # {'to_channel': [], 'to': ['usr1', 'usr2', 'usr3'], 'to_group': []} |
1970 |
|
|
+ |
1971 |
|
|
+ if (sum([len(v) for k, v in list(multiple_to.items())])) == 1: |
1972 |
|
|
+ # if we have only one recipient, we don't need fork to send message, just re-write "to" vaiable |
1973 |
|
|
+ tmp_max = 0 |
1974 |
|
|
+ for t in to_types: |
1975 |
|
|
+ if len(multiple_to[t]) > tmp_max: |
1976 |
|
|
+ tmp_max = len(multiple_to[t]) |
1977 |
|
|
+ tg.type = to_types_to_telegram[t] |
1978 |
|
|
+ zbx_to = multiple_to[t][0] |
1979 |
|
|
+ else: |
1980 |
|
|
+ for t in to_types: |
1981 |
|
|
+ for i in multiple_to[t]: |
1982 |
|
|
+ args_new = list(args) |
1983 |
|
|
+ args_new[1] = i |
1984 |
|
|
+ if t == "to_group": |
1985 |
|
|
+ args_new.append("--group") |
1986 |
|
|
+ args_new.append("--forked") |
1987 |
|
|
+ args_new.insert(0, sys.executable) |
1988 |
|
|
+ if is_debug: |
1989 |
|
|
+ print_message("Fork for custom recipient ({1}), new args: {0}".format(args_new, |
1990 |
|
|
+ to_types_to_telegram[t])) |
1991 |
|
|
+ subprocess.call(args_new) |
1992 |
|
|
+ done_all_work_in_the_fork = True |
1993 |
|
|
+ |
1994 |
|
|
+ if done_all_work_in_the_fork: |
1995 |
|
|
+ sys.exit(0) |
1996 |
|
|
+ |
1997 |
|
|
+ uid = None |
1998 |
|
|
+ |
1999 |
|
|
+ if tg.type == "channel": |
2000 |
|
|
+ uid = zbx_to |
2001 |
|
|
+ if tg.type == "private": |
2002 |
|
|
+ zbx_to = zbx_to.replace("@", "") |
2003 |
|
|
+ |
2004 |
|
|
+ if zbx_to.isdigit(): |
2005 |
|
|
+ uid = zbx_to |
2006 |
|
|
+ |
2007 |
|
|
+ if not uid: |
2008 |
|
|
+ uid = tg.get_uid_from_cache(zbx_to) |
2009 |
|
|
+ |
2010 |
|
|
+ if not uid: |
2011 |
|
|
+ uid = tg.get_uid(zbx_to) |
2012 |
|
|
+ if uid: |
2013 |
|
|
+ tmp_need_update = True |
2014 |
|
|
+ if not uid: |
2015 |
|
|
+ tg.error_need_to_contact(zbx_to) |
2016 |
|
|
+ sys.exit(1) |
2017 |
|
|
+ |
2018 |
|
|
+ if tmp_need_update: |
2019 |
|
|
+ tg.update_cache_uid(zbx_to, str(uid).rstrip()) |
2020 |
|
|
+ |
2021 |
|
|
+ if is_debug: |
2022 |
|
|
+ print_message("Telegram uid of {0} '{1}': {2}".format(tg.type, zbx_to, uid)) |
2023 |
|
|
+ |
2024 |
|
|
+ # add signature, turned off by default, you can turn it on in config |
2025 |
|
|
+ try: |
2026 |
|
|
+ if "--signature" in args or settings["signature"] or zbxtg_settings.zbx_tg_signature\ |
2027 |
|
|
+ and not "--signature_disable" in args and not settings["signature_disable"]: |
2028 |
|
|
+ if "--signature" in args: |
2029 |
|
|
+ settings["signature"] = args[args.index("--signature") + 1] |
2030 |
|
|
+ if not settings["signature"]: |
2031 |
|
|
+ settings["signature"] = zbxtg_settings.zbx_server |
2032 |
|
|
+ zbxtg_body_text.append("--") |
2033 |
|
|
+ zbxtg_body_text.append(settings["signature"]) |
2034 |
|
|
+ except: |
2035 |
|
|
+ pass |
2036 |
|
|
+ |
2037 |
|
|
+ # replace text with emojis |
2038 |
|
|
+ internal_using_emoji = False # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152 |
2039 |
|
|
+ if hasattr(zbxtg_settings, "emoji_map"): |
2040 |
|
|
+ zbxtg_body_text_emoji_support = [] |
2041 |
|
|
+ for l in zbxtg_body_text: |
2042 |
|
|
+ l_new = l |
2043 |
|
|
+ for k, v in list(zbxtg_settings.emoji_map.items()): |
2044 |
|
|
+ l_new = l_new.replace("{{" + k + "}}", v) |
2045 |
|
|
+ zbxtg_body_text_emoji_support.append(l_new) |
2046 |
|
|
+ if len("".join(zbxtg_body_text)) - len("".join(zbxtg_body_text_emoji_support)): |
2047 |
|
|
+ internal_using_emoji = True |
2048 |
|
|
+ zbxtg_body_text = zbxtg_body_text_emoji_support |
2049 |
|
|
+ |
2050 |
|
|
+ if not is_single_message: |
2051 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
2052 |
|
|
+ if not tg.ok: |
2053 |
|
|
+ # first case â if group has been migrated to a supergroup, we need to update chat_id of that group |
2054 |
|
|
+ if tg.error.find("migrated") > -1 and tg.error.find("supergroup") > -1: |
2055 |
|
|
+ migrate_to_chat_id = tg.result["parameters"]["migrate_to_chat_id"] |
2056 |
|
|
+ tg.update_cache_uid(zbx_to, migrate_to_chat_id, message="Group chat is migrated to supergroup, " |
2057 |
|
|
+ "updating cache file") |
2058 |
|
|
+ uid = migrate_to_chat_id |
2059 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
2060 |
|
|
+ |
2061 |
|
|
+ # another case if markdown is enabled and we got parse error, try to remove "bad" symbols from message |
2062 |
|
|
+ if tg.markdown and tg.error.find("Can't find end of the entity starting at byte offset") > -1: |
2063 |
|
|
+ markdown_warning = "Original message has been fixed due to {0}. " \ |
2064 |
|
|
+ "Please, fix the markdown, it's slowing down messages sending."\ |
2065 |
|
|
+ .format(url_wiki_base + "/" + settings_description["markdown"]["url"]) |
2066 |
|
|
+ markdown_fix_attempts = 0 |
2067 |
|
|
+ while not tg.ok and markdown_fix_attempts != 3: |
2068 |
|
|
+ offset = re.search("Can't find end of the entity starting at byte offset ([0-9]+)", tg.error).group(1) |
2069 |
|
|
+ zbxtg_body_text = markdown_fix(zbxtg_body_text, offset, emoji=internal_using_emoji) + \ |
2070 |
|
|
+ ["\n"] + [markdown_warning] |
2071 |
|
|
+ tg.disable_web_page_preview = True |
2072 |
|
|
+ tg.send_message(uid, zbxtg_body_text) |
2073 |
|
|
+ markdown_fix_attempts += 1 |
2074 |
|
|
+ if tg.ok: |
2075 |
|
|
+ print_message(markdown_warning) |
2076 |
|
|
+ |
2077 |
|
|
+ if is_debug: |
2078 |
|
|
+ print((tg.result)) |
2079 |
|
|
+ |
2080 |
|
|
+ if settings["zbxtg_image_age"]: |
2081 |
|
|
+ age_sec = age2sec(settings["zbxtg_image_age"]) |
2082 |
|
|
+ if age_sec > 0 and age_sec > 3600: |
2083 |
|
|
+ settings["zbxtg_image_period"] = age_sec |
2084 |
|
|
+ |
2085 |
|
|
+ message_id = 0 |
2086 |
|
|
+ if tg_method_image: |
2087 |
|
|
+ zbx.login() |
2088 |
|
|
+ if not zbx.cookie: |
2089 |
|
|
+ text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\ |
2090 |
|
|
+ "unable to send graphs check manually" |
2091 |
|
|
+ tg.send_message(uid, [text_warn]) |
2092 |
|
|
+ print_message(text_warn) |
2093 |
|
|
+ else: |
2094 |
|
|
+ if not settings["extimg"]: |
2095 |
|
|
+ zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"], |
2096 |
|
|
+ settings["zbxtg_title"], settings["zbxtg_image_width"], |
2097 |
|
|
+ settings["zbxtg_image_height"], version=zbx_version) |
2098 |
|
|
+ else: |
2099 |
|
|
+ zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir) |
2100 |
|
|
+ zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200) |
2101 |
|
|
+ if tg.ok: |
2102 |
|
|
+ message_id = tg.result["result"]["message_id"] |
2103 |
|
|
+ tg.reply_to_message_id = message_id |
2104 |
|
|
+ if not zbxtg_file_img: |
2105 |
|
|
+ text_warn = "Can't get graph image, check script manually, see logs, or disable graphs" |
2106 |
|
|
+ tg.send_message(uid, [text_warn]) |
2107 |
|
|
+ print_message(text_warn) |
2108 |
|
|
+ else: |
2109 |
|
|
+ if not is_single_message: |
2110 |
|
|
+ zbxtg_body_text = "" |
2111 |
|
|
+ else: |
2112 |
|
|
+ if is_modified: |
2113 |
|
|
+ text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\ |
2114 |
|
|
+ "the message has been cut to 200 symbols, "\ |
2115 |
|
|
+ "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\ |
2116 |
|
|
+ "#issuecomment-166895044" |
2117 |
|
|
+ print_message(text_warn) |
2118 |
|
|
+ if not is_single_message: |
2119 |
|
|
+ tg.disable_notification = True |
2120 |
|
|
+ tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img) |
2121 |
|
|
+ if tg.ok: |
2122 |
|
|
+ settings["zbxtg_body_text"] = zbxtg_body_text |
2123 |
|
|
+ os.remove(zbxtg_file_img) |
2124 |
|
|
+ else: |
2125 |
|
|
+ if tg.error.find("PHOTO_INVALID_DIMENSIONS") > -1: |
2126 |
|
|
+ if not tg.disable_web_page_preview: |
2127 |
|
|
+ tg.disable_web_page_preview = True |
2128 |
|
|
+ text_warn = "Zabbix user couldn't get graph (probably has no rights to get data from host), " \ |
2129 |
|
|
+ "check script manually, see {0}".format(url_wiki_base + "/" + |
2130 |
|
|
+ settings_description["graphs"]["url"]) |
2131 |
|
|
+ tg.send_message(uid, [text_warn]) |
2132 |
|
|
+ print_message(text_warn) |
2133 |
|
|
+ if tg.location and location_coordinates["latitude"] and location_coordinates["longitude"]: |
2134 |
|
|
+ tg.reply_to_message_id = message_id |
2135 |
|
|
+ tg.disable_notification = True |
2136 |
|
|
+ tg.send_location(to=uid, coordinates=location_coordinates) |
2137 |
|
|
+ |
2138 |
|
|
+ if "--show-settings" in args: |
2139 |
|
|
+ print_message("Settings: " + str(json.dumps(settings, indent=2))) |
2140 |
|
|
+ |
2141 |
|
|
+if __name__ == "__main__": |
2142 |
|
|
+ main() |
2143 |
|
|
diff -Nur --no-dereference smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg_settings.example.py smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg_settings.example.py |
2144 |
|
|
--- smeserver-zabbix-server-0.1.old/root/var/lib/zabbix/bin/zbxtg_settings.example.py 1969-12-31 19:00:00.000000000 -0500 |
2145 |
|
|
+++ smeserver-zabbix-server-0.1/root/var/lib/zabbix/bin/zbxtg_settings.example.py 2021-11-08 22:03:56.386000000 -0500 |
2146 |
|
|
@@ -0,0 +1,68 @@ |
2147 |
|
|
+# -*- coding: utf-8 -*- |
2148 |
|
|
+ |
2149 |
|
|
+tg_key = "XYZ" # telegram bot api key |
2150 |
|
|
+ |
2151 |
|
|
+zbx_tg_prefix = "zbxtg" # variable for separating text from script info |
2152 |
|
|
+zbx_tg_tmp_dir = "/var/tmp/" + zbx_tg_prefix # directory for saving caches, uids, cookies, etc. |
2153 |
|
|
+zbx_tg_signature = False |
2154 |
|
|
+ |
2155 |
|
|
+zbx_tg_update_messages = True |
2156 |
|
|
+zbx_tg_matches = { |
2157 |
|
|
+ "problem": "PROBLEM: ", |
2158 |
|
|
+ "ok": "OK: " |
2159 |
|
|
+} |
2160 |
|
|
+ |
2161 |
|
|
+zbx_server = "http://127.0.0.1/zabbix/" # zabbix server full url |
2162 |
|
|
+zbx_api_user = "api" |
2163 |
|
|
+zbx_api_pass = "api" |
2164 |
|
|
+zbx_api_verify = True # True - do not ignore self signed certificates, False - ignore |
2165 |
|
|
+ |
2166 |
|
|
+#zbx_server_version = 2 # for Zabbix 2.x version |
2167 |
|
|
+zbx_server_version = 3 # for Zabbix 3.x version, by default, not everyone updated to 4.x yet |
2168 |
|
|
+#zbx_server_version = 4 # for Zabbix 4.x version, default will be changed in the future with this |
2169 |
|
|
+ |
2170 |
|
|
+zbx_basic_auth = False |
2171 |
|
|
+zbx_basic_auth_user = "zabbix" |
2172 |
|
|
+zbx_basic_auth_pass = "zabbix" |
2173 |
|
|
+ |
2174 |
|
|
+proxy_to_zbx = None |
2175 |
|
|
+proxy_to_tg = None |
2176 |
|
|
+ |
2177 |
|
|
+# proxy_to_zbx = "http://proxy.local:3128" |
2178 |
|
|
+# proxy_to_tg = "https://proxy.local:3128" |
2179 |
|
|
+ |
2180 |
|
|
+# proxy_to_tg = "socks5://user1:password2@hostname:port" # socks5 with username and password |
2181 |
|
|
+# proxy_to_tg = "socks5://hostname:port" # socks5 without username and password |
2182 |
|
|
+# proxy_to_tg = "socks5h://hostname:port" # hostname resolution on SOCKS proxy. |
2183 |
|
|
+ # This helps when internet provider alter DNS queries. |
2184 |
|
|
+ # Found here: https://stackoverflow.com/a/43266186/957508 |
2185 |
|
|
+ |
2186 |
|
|
+google_maps_api_key = None # get your key, see https://developers.google.com/maps/documentation/geocoding/intro |
2187 |
|
|
+ |
2188 |
|
|
+zbx_tg_daemon_enabled = False |
2189 |
|
|
+zbx_tg_daemon_enabled_ids = [6931850, ] |
2190 |
|
|
+zbx_tg_daemon_enabled_users = ["ableev", ] |
2191 |
|
|
+zbx_tg_daemon_enabled_chats = ["Zabbix in Telegram Script", ] |
2192 |
|
|
+ |
2193 |
|
|
+zbx_db_host = "localhost" |
2194 |
|
|
+zbx_db_database = "zabbix" |
2195 |
|
|
+zbx_db_user = "zbxtg" |
2196 |
|
|
+zbx_db_password = "zbxtg" |
2197 |
|
|
+ |
2198 |
|
|
+ |
2199 |
|
|
+emoji_map = { |
2200 |
|
|
+ "Disaster": "đĨ", |
2201 |
|
|
+ "High": "đ", |
2202 |
|
|
+ "Average": "â", |
2203 |
|
|
+ "Warning": "â ī¸", |
2204 |
|
|
+ "Information": "âšī¸", |
2205 |
|
|
+ "Not classified": "đ", |
2206 |
|
|
+ "OK": "â
", |
2207 |
|
|
+ "PROBLEM": "â", |
2208 |
|
|
+ "info": "âšī¸", |
2209 |
|
|
+ "WARNING": "â ī¸", |
2210 |
|
|
+ "DISASTER": "â", |
2211 |
|
|
+ "bomb": "đŖ", |
2212 |
|
|
+ "fire": "đĨ", |
2213 |
|
|
+ "hankey": "đŠ", |
2214 |
|
|
+} |