Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37371251
en ru br
Репозитории ALT

Группа :: Мониторинг
Пакет: zabbix-in-telegram

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: zabbix-in-telegram-20200425-alt1.patch
Скачать


 .gear/rules                          |   3 +
 .gear/tags/list                      |   1 +
 .gear/zabbix-in-telegram.spec        |  65 +++
 LICENSE.txt                          |   2 +-
 README.md                            |  51 +--
 ZbxTgDaemon.py                       | 174 ++++++++
 bash-old-version/tg_vars.cfg.example |  11 +
 bash-old-version/zbxtg.sh            | 142 +++++++
 requirements.txt                     |   3 +
 tg_vars.cfg.example                  |  10 -
 zbxtg.py                             | 747 +++++++++++++++++++++++++++++------
 zbxtg.sh                             | 111 ------
 zbxtg_settings.example.py            |  57 ++-
 13 files changed, 1094 insertions(+), 283 deletions(-)
diff --git a/.gear/rules b/.gear/rules
new file mode 100644
index 0000000..2319eee
--- /dev/null
+++ b/.gear/rules
@@ -0,0 +1,3 @@
+tar: upstream:.
+diff: upstream:. .
+spec: .gear/zabbix-in-telegram.spec
diff --git a/.gear/tags/list b/.gear/tags/list
new file mode 100644
index 0000000..71c721b
--- /dev/null
+++ b/.gear/tags/list
@@ -0,0 +1 @@
+c23673b50fa2f10c40ee53ad5cad6f90212a071a upstream
diff --git a/.gear/zabbix-in-telegram.spec b/.gear/zabbix-in-telegram.spec
new file mode 100644
index 0000000..90270c3
--- /dev/null
+++ b/.gear/zabbix-in-telegram.spec
@@ -0,0 +1,65 @@
+%define z_dir %_sysconfdir/zabbix/alertscripts
+
+Name: zabbix-in-telegram
+Version: 20200425
+Release: alt1
+
+Summary: Zabbix Notifications with graphs in Telegram
+
+License: MIT
+Group: Monitoring
+URL: https://github.com/ableev/Zabbix-in-Telegram
+
+BuildArch: noarch
+
+# Source-git: https://github.com/ableev/Zabbix-in-Telegram.git
+Source: %name-%version.tar
+Patch: %name-%version-%release.patch
+
+BuildRequires(pre): rpm-build-python3
+BuildRequires(pre): rpm-build-intro
+
+Requires: zabbix-server-common > 3.0.0
+
+%add_python3_req_skip zbxtg_settings
+
+# generated by 'epm restore --dry-run' from zabbix-in-telegram/requirements.txt
+%py3_use socks >= 1.6.8
+%py3_use requests >= 2.20.0
+%py3_use requests-oauthlib >= 0.6.2
+
+%description
+Zabbix Notifications with graphs in Telegram.
+
+%prep
+%setup
+sed -i 's|#!/usr/bin/env python|#!/usr/bin/env python3|' \
+    $(find ./ -name '*.py')
+
+%build
+
+%install
+mkdir -p %buildroot%z_dir
+install -p -m 755 zbxtg.py %buildroot%z_dir/zbxtg.py
+install -p -m 644 zbxtg_settings.example.py %buildroot%z_dir/zbxtg_settings.py
+
+%files
+%z_dir/zbxtg.py
+%config(noreplace) %z_dir/zbxtg_settings.py
+%doc README.md LICENSE.txt
+
+
+%changelog
+* Sat Jul 03 2021 Vitaly Lipatov <lav@altlinux.ru> 20200425-alt1
+- update to 4ca3585b4b568060370f17a864a8cbceb14438ca
+- add requires
+
+* Fri Nov 15 2019 Andrey Bychkov <mrdrew@altlinux.org> 20160607-alt3
+- python2 -> python3
+
+* Tue Jun  7 2016 Terechkov Evgenii <evg@altlinux.org> 20160607-alt2
+- c23673b
+
+* Tue Jun  7 2016 Terechkov Evgenii <evg@altlinux.org> 20160607-alt1
+- Initial build for ALT Linux Sisyphus (77e163b)
+
diff --git a/LICENSE.txt b/LICENSE.txt
index 10a8634..d3023c7 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2016 Ilya Ableev
+Copyright (c) 2019 Ilya Ableev
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 0147ae7..193d3b2 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,39 @@
 # Zabbix-in-Telegram
 Zabbix Notifications with graphs in Telegram
 
-Join us in our **Telegram group** via this link: https://telegram.me/ZbxTg
+Join us in our **Telegram group** via this link: https://t.me/ZbxTg
 
-Subscribe to our channel: https://telegram.me/Zabbix_in_Telegram
+Subscribe to our channel: https://t.me/Zabbix_in_Telegram
 
 Rate on [share.zabbix.com](https://share.zabbix.com): https://share.zabbix.com/cat-notifications/zabbix-in-telegram
 
 ### Features
 - [x] Graphs based on latest data are sent directly to your messenger
-- [x] You can send messages both in private and group chats
-- [x] Channels support
+- [x] You can send messages both in private and group/supergroup chats
+- [x] Channels support (only public, but you can do it for private as well with dirty hack)
 - [x] Saves chatid as a temporary file
 - [x] Simple markdown and HTML are supported
-- [x] Emoji in messages
+- [x] Emoji (you can use emoji instead of severity, see [the wiki article](https://github.com/ableev/Zabbix-in-Telegram/wiki/Trigger-severity-as-Emoji)) (zabbix doesn't support utf8mb4 encoding yet)
+- [x] Location map
 
 ### TODOs
-- Simple zabbix's management via bot's commands
+- Simple zabbix's management via bot's commands Б─⌠ in dev state
 - Ability to send complex graph or part of screen
 
 
 ### Configuration / Installation
 
-**First of all**: You need to install the `requests` module for python, this is required for operation! </br>
-                  To do so, enter `pip install requests` in your commandline!
+**READ WIKI IF YOU HAVE PROBLEM WITH SOMETHING**: https://github.com/ableev/Zabbix-in-Telegram/wiki
 
- * Put `zbxtg.py` in your `AlertScriptsPath` directory, the path is set inside your zabbix_server.conf
- * Put `zbxtg_group.py` in the same location if you want to send messages to the group chat
- * Create `zbxtg_settings.py` with your settings and save them in the same directory as the script, see example for layout
-  * Create a bot in Telegram and get API key
-  * Create readonly user in Zabbix (for getting graphs images from zabbix)
-  * Set proxy host:port in `zbxtg_settings.py` if you need an internet proxy
+**First of all**: You need to install the appropriate modules for python, this is required for operation! </br>
+                  To do so, enter `pip install -r requirements.txt` in your commandline!
+
+ * Put `zbxtg.py` in your `AlertScriptsPath` directory, the path is set inside your `zabbix_server.conf`
+ * Put `zbxtg_group.py` in the same location if you want to send messages to the group chat (if you are using Zabbix 2.x version)
+ * Create `zbxtg_settings.py` (copy it from `zbxtg_settings.example.py`) with your settings and save them in the same directory as the script, see example for layout
+  * Create a bot in Telegram and get API key: https://core.telegram.org/bots#creating-a-new-bot
+  * Create readonly user in Zabbix web interface (for getting graphs from zabbix)
+  * Set proxy host:port in `zbxtg_settings.py` if you need an internet proxy (socks5 supported as well, the wiki will help you)
  * Add new media for Telegram in Zabbix web interface with these settings:
  
 <img src="https://i.imgur.com/Ytrbe4S.png" width="400px">
@@ -41,7 +44,7 @@ Rate on [share.zabbix.com](https://share.zabbix.com): https://share.zabbix.com/c
 
  * **Note that Zabbix 3.0 has different settings for that step, see it there**: https://github.com/ableev/Zabbix-in-Telegram/wiki/Working-with-Zabbix-3.0
  * Send a message to your bot via Telegram, e.g. "/start"
-  * If you are in group chat, just mention your bot, e.g. `@ZbxTgDevBot ping`
+  * If you are in a group chat, start a conversation with your bot: `/start@ZbxTgDevBot`
  * Create a new action like this:
 ```
 Last value: {ITEM.LASTVALUE1} ({TIME})
@@ -55,8 +58,9 @@ zbxtg;title:{HOST.HOST} - {TRIGGER.NAME}
 
  * Add the appropriate Media Type to your user
   * The username is **CASE-SENSITIVE**
+  * If you don't have a username, you can use your chatid directly (and you need to google how to get it)
   * Group chats don't have URLs, so you need to put group's name in media type
-  * Messages for channels should be sent as for private chats
+  * Messages for channels should be sent as for private chats (simply add bot to your channel first and use channel's username as if it was a real user)
 
   * Private:
 
@@ -73,9 +77,12 @@ zbxtg;graphs_period=10800 -- set graphs period (default - 3600 seconds)
 zbxtg;graphs_width=700 -- set graphs width (default - 900px)
 zbxtg;graphs_height=300 -- set graphs height (default - 300px)
 zbxtg;itemid:{ITEM.ID1} -- define itemid (from trigger) for attach
-zbxtg;title:{HOST.HOST} - {TRIGGER.NAME} -- graph title
+zbxtg;itemid:{ITEM.ID1},{ITEM.ID2},{ITEM.ID3} -- same, but if you want to send two or more graphs, use complex trigger
+zbxtg;title:{HOST.HOST} - {TRIGGER.NAME} -- graph's title
 zbxtg;debug -- enables debug mode, some logs and images will be saved in the tmp dir (temporary doesn't affect python version)
 zbxtg;channel -- enables sending to channels
+zbxtg;to:username1,username2,username3 -- now you don't need to create dedicated profiles and add media for them, use this option in action to send messages to those user(s)
+zbxtg;to_group:Group Name One,Group Name Two -- the same but for groups
 ```
 
 You can use markdown or html formatting in your action: https://core.telegram.org/bots/api#markdown-style + https://core.telegram.org/bots/api#html-style. 
@@ -83,10 +90,10 @@ You can use markdown or html formatting in your action: https://core.telegram.or
 #### Debug
 
 * You can use the following command to send a message from your command line: </br>
-`./zbxtg.py "<username>" "<message_subject>" "<message_body>" --debug`
- * For `<username>` substitute your Telegram username, NOT that of your bot (case-sensitive)
- * For `<message_subject>` and `<message_body>` just substitute something like "test" "test" (for Telegram it's doesn't matter between subject and body
- * You can omit the `"`, these are optional
+`./zbxtg.py "@username" "first part of a message" "second part of a message" --debug`
+ * For `@username` substitute your Telegram username, **NOT that of your bot** (case-sensitive) OR chatid
+ * For `first part of a message` and `second part of a message` just substitute something like "test" "test" (for Telegram it's doesn't matter between subject and body)
+ * You can skip the `"` if it's one word for every parameter, these are optional
 
 ---
 
@@ -99,5 +106,5 @@ You can use markdown or html formatting in your action: https://core.telegram.or
 If you see this error, it means that you rich the limit of caption with 200 symbols in it (Telegram API's limitaion).
 Such captions will be automatically cut to 200 symbols.
 
-#### Zabbix 3.0 
+#### Zabbix 3.0 and higher (3.2, 3.4, 4.0, 4.2, 4.4)
 https://github.com/ableev/Zabbix-in-Telegram/wiki/Working-with-Zabbix-3.0
diff --git a/ZbxTgDaemon.py b/ZbxTgDaemon.py
new file mode 100644
index 0000000..d18a4d0
--- /dev/null
+++ b/ZbxTgDaemon.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import sys
+import os
+import hashlib
+import re
+import time
+from os.path import dirname
+import zbxtg_settings
+import zbxtg
+from pyzabbix import ZabbixAPI, ZabbixAPIException
+
+
+class zabbixApi():
+    def __init__(self, server, user, password):
+        self.api = ZabbixAPI(server)
+        self.user = user
+        self.password = password
+
+    def login(self):
+        self.api.login(self.user, self.password)
+
+    def triggers_active(self):
+        return self.api.trigger.get(output="extend", monitored=True, filter={"value": 1}, sortfield="priority", sortorder="DESC",
+                                    selectHosts="extend")
+
+
+
+def print_message(string):
+    string = str(string) + "\n"
+    filename = sys.argv[0].split("/")[-1]
+    sys.stderr.write(filename + ": " + string)
+
+
+def file_write(filename, text):
+    with open(filename, "w") as fd:
+        fd.write(str(text))
+    return True
+
+
+def file_read(filename):
+    with open(filename, 'r') as fd:
+        text = fd.readlines()
+    return text
+
+
+def main():
+    TelegramAPI = zbxtg.TelegramAPI
+    ZabbixWeb = zbxtg.ZabbixWeb
+    tmp_dir = zbxtg_settings.zbx_tg_tmp_dir
+
+    if not zbxtg_settings.zbx_tg_daemon_enabled:
+        print("You should enable daemon by adding 'zbx_tg_remote_control' in the configuration file")
+        sys.exit(1)
+
+    tmp_uids = tmp_dir + "/uids.txt"
+    tmp_ts = {
+        "message_id": tmp_dir + "/daemon_message_id.txt",
+        "update_offset": tmp_dir + "/update_offset.txt",
+    }
+
+    for i, v in tmp_ts.iteritems():
+        if not os.path.exists(v):
+            print_message("{0} doesn't exist, creating new one...".format(v))
+            file_write(v, "0")
+            print_message("{0} successfully created".format(v))
+
+    message_id_last = file_read(tmp_ts["message_id"])[0].strip()
+    if message_id_last:
+        message_id_last = int(message_id_last)
+
+    update_id = file_read(tmp_ts["update_offset"])
+
+    tg = TelegramAPI(key=zbxtg_settings.tg_key)
+    if zbxtg_settings.proxy_to_tg:
+        proxy_to_tg = zbxtg_settings.proxy_to_tg
+        if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"):
+            proxy_to_tg = "https://" + proxy_to_tg
+        tg.proxies = {
+            "https": "{0}".format(zbxtg_settings.proxy_to_tg),
+        }
+    zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user,
+                    password=zbxtg_settings.zbx_api_pass)
+    if zbxtg_settings.proxy_to_zbx:
+        zbx.proxies = {"http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx)}
+
+    try:
+        zbx_api_verify = zbxtg_settings.zbx_api_verify
+        zbx.verify = zbx_api_verify
+    except:
+        pass
+
+    zbxapi = zabbixApi(zbxtg_settings.zbx_server, zbxtg_settings.zbx_api_user, zbxtg_settings.zbx_api_pass)
+    zbxapi.login()
+
+    print(tg.get_me())
+
+    #hosts = zbxdb.db_query("SELECT hostid, host FROM hosts")
+
+    commands = [
+        "/triggers",
+        "/help",
+        # "/graph",
+        # "/history",
+        # "/screen"
+    ]
+
+    def md5(fname):
+        hash_md5 = hashlib.md5()
+        with open(fname, "rb") as f:
+            for chunk in iter(lambda: f.read(4096), b""):
+                hash_md5.update(chunk)
+        return hash_md5.hexdigest()
+
+    md5sum = md5("ZbxTgDaemon.py")
+    print md5sum
+
+    try:
+        while True:
+            time.sleep(1)
+            md5sum_new = md5("ZbxTgDaemon.py")
+            if md5sum != md5sum_new:
+                sys.exit(1)
+            tg.update_offset = update_id
+            updates = tg.get_updates()
+            if not updates["result"]:
+                continue
+            for m in updates["result"]:
+                if "message" not in m:
+                    continue
+                update_id_last = m["update_id"]
+                tg.update_offset = update_id_last
+                if m["message"]["from"]["id"] not in zbxtg_settings.zbx_tg_daemon_enabled_ids:
+                    file_write(tmp_ts["update_offset"], update_id_last)
+                    continue
+                    print("Fuck this shit, I'm not going to answer to someone not from the whitelist")
+                else:
+                    if not "text" in m["message"]:
+                        continue
+                    text = m["message"]["text"]
+                    to = m["message"]["from"]["id"]
+                    reply_text = list()
+                    if m["message"]["message_id"] > message_id_last:
+                        if re.search(r"^/(start|help)", text):
+                            reply_text.append("Hey, this is ZbxTgDaemon bot.")
+                            reply_text.append("https://github.com/ableev/Zabbix-in-Telegram")
+                            reply_text.append("If you need help, you can ask it in @ZbxTg group\n")
+                            reply_text.append("Available commands:")
+                            reply_text.append("\n".join(commands))
+                            tg.disable_web_page_preview = True
+                        if re.search(r"^/triggers", text):
+                            triggers = zbxapi.triggers_active()
+                            if triggers:
+                                for t in triggers:
+                                    reply_text.append("Severity: {0}, Host: {1}, Trigger: {2}".format(
+                                        t["priority"], t["hosts"][0]["host"].encode('utf-8'), t["description"].encode('utf-8')
+                                    ))
+                            else:
+                                reply_text.append("There are no triggers, have a nice day!")
+                        if not reply_text:
+                            reply_text = ["I don't know what to do about it"]
+                        if tg.send_message(to, reply_text):
+                            with open(tmp_ts["message_id"], "w") as message_id_file:
+                                message_id_file.write(str(m["message"]["message_id"]))
+                            message_id_last = m["message"]["message_id"]
+                            tg.disable_web_page_preview = False
+                file_write(tmp_ts["update_offset"], update_id_last)
+    except KeyboardInterrupt:
+        print("Exiting...")
+
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/bash-old-version/tg_vars.cfg.example b/bash-old-version/tg_vars.cfg.example
new file mode 100644
index 0000000..6494278
--- /dev/null
+++ b/bash-old-version/tg_vars.cfg.example
@@ -0,0 +1,11 @@
+TG_KEY="000:AAAAA_bbbb"
+
+ZBX_TG_PREFIX="zbxtg" # variable for separating text from script info
+ZBX_TG_SIGN="TRUE"
+
+ZBX_SERVER="http://zabbix.local" # zabbix server url
+ZBX_API_USER="api" # zabbix user; user must have at least read access to get graphs
+ZBX_API_PASS="apisecret"
+
+CURL="curl -s" # if you are using proxy server, it's time to add it right here
+# CURL="curl -x proxy.local:3128 -s"
diff --git a/bash-old-version/zbxtg.sh b/bash-old-version/zbxtg.sh
new file mode 100755
index 0000000..6afad26
--- /dev/null
+++ b/bash-old-version/zbxtg.sh
@@ -0,0 +1,142 @@
+#!/bin/bash
+
+. $(dirname "$0")/tg_vars.cfg
+
+CURL_TG="${CURL} https://api.telegram.org/bot${TG_KEY}"
+
+TMP_DIR="/tmp/${ZBX_TG_PREFIX}"
+[ ! -d "${TMP_DIR}" ] && (mkdir -p ${TMP_DIR} || TMP_DIR="/tmp")
+TMP_COOKIE="${TMP_DIR}/cookie.txt"
+TMP_UIDS="${TMP_DIR}/uids.txt"
+
+TS="`date +%s_%N`_$RANDOM"
+LOG="/dev/null"
+
+IS_DEBUG () {
+    if [ "${ISDEBUG}" == "TRUE" ]
+    then
+        return 0
+    else
+        return 1
+    fi
+}
+
+
+login() {
+    # grab cookie for downloading image
+    IS_DEBUG && echo "${CURL} --cookie-jar ${TMP_COOKIE} --request POST --data \"name=${ZBX_API_USER}&password=${ZBX_API_PASS}&enter=Sign%20in\" ${ZBX_SERVER}/" >>${LOG}
+    ${CURL} --cookie-jar ${TMP_COOKIE} --request POST --data "name=${ZBX_API_USER}&password=${ZBX_API_PASS}&enter=Sign%20in" ${ZBX_SERVER}/
+}
+
+get_image() {
+    URL=$1
+    URL=$(echo "${URL}" | sed -e 's/\ /%20/g')
+    IMG_NAME=$2
+    # downloads png graph and saves it to temporary path
+    IS_DEBUG && echo "${CURL} --cookie ${TMP_COOKIE} --globoff \"${URL}\" -o ${IMG_NAME}" >>${LOG}
+    ${CURL} --cookie ${TMP_COOKIE} --globoff "${URL}" -o ${IMG_NAME}
+}
+
+TO=$1
+SUBJECT=$2
+BODY=$3
+
+TG_GROUP=0 # send message to chat or to private chat to user
+TG_CHANNEL=0 # send message to channel
+METHOD="txt" # sendMessage (simple text) or sendPhoto (attached image)
+
+echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};graphs" && METHOD="image"
+echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};chat" && TG_GROUP=1
+echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};group" && TG_GROUP=1
+echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};debug" && ISDEBUG="TRUE"
+echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};channel" && TG_CHANNEL=1
+
+IS_DEBUG && LOG="${TMP_DIR}/debug.${TS}.log"
+IS_DEBUG && echo -e "TMP_DIR=${TMP_DIR}\nTMP_COOKIE=${TMP_COOKIE}\nTMP_UIDS=${TMP_UIDS}" >>${LOG}
+
+if [ "${TG_GROUP}" -eq 1 ]
+then
+    TG_CONTACT_TYPE="group"
+else
+    TG_CONTACT_TYPE="private"
+fi
+
+TG_CHAT_ID=$(cat ${TMP_UIDS} | awk -F ';' '{if ($1 == "'${TO}'" && $2 == "'${TG_CONTACT_TYPE}'") print $3}' | tail -1)
+
+if [ "${TG_CHANNEL}" -eq 1 ]
+then
+    TG_CHAT_ID="${TO}"
+fi
+
+if [ -z "${TG_CHAT_ID}" ]
+then
+    TG_UPDATES=$(${CURL_TG}/getUpdates | sed -e 's/},{/\n/')
+    for (( idx=${#TG_UPDATES[@]}-1 ; idx>=0 ; idx-- ))
+    do
+        UPDATE="${TG_UPDATES[idx]}"
+        echo "${UPDATE}"
+        if [ "${TG_GROUP}" -eq 1 ]
+        then
+            TG_CHAT_ID=$(echo "${UPDATE}" | sed -e 's/["}{]//g' | awk -F ',' '{if ($8 == "type:group" && $7 == "title:'${TO}'") {gsub("chat:id:", "", $6); print $6}}' | tail -1)
+            if [ "$(echo ${TG_CHAT_ID} | grep -Eq '\-[0-9]+' && echo 1 || echo 0)" -eq 1 ]
+            then
+                break
+            fi
+        else
+            TG_CHAT_ID=$(echo "${UPDATE}" | sed -e 's/["}{]//g' | awk -F ',' '{if ($10 == "type:private" && $5 == "username:'${TO}'") {gsub("chat:id:", "", $6); print $6}}' | tail -1)
+            if [ "$(echo ${TG_CHAT_ID} | grep -Eq '[0-9]+' && echo 1 || echo 0)" -eq 1 ]
+            then
+                break
+            fi
+        fi
+    done
+    echo "${TO};${TG_CONTACT_TYPE};${TG_CHAT_ID}" >>${TMP_UIDS}
+fi
+
+IS_DEBUG && echo "TG_CHAT_ID: ${TG_CHAT_ID}" >>${LOG}
+
+TG_TEXT=$(echo "${BODY}" | grep -vE "^${ZBX_TG_PREFIX};")
+if [ "${ZBX_TG_SIGN}" != "FALSE" ]
+then
+    TG_TEXT=$(echo ${TG_TEXT}; echo "--"; echo "${ZBX_SERVER}")
+fi
+
+case "${METHOD}" in
+
+    "txt")
+        TG_MESSAGE=$(echo -e "${SUBJECT}\n${TG_TEXT}")
+        IS_DEBUG && echo "${CURL_TG}/sendMessage -F \"chat_id=${TG_CHAT_ID}\" -F \"text=${TG_MESSAGE}\"" >>${LOG}
+        ANSWER=$(${CURL_TG}/sendMessage?chat_id=${TG_CHAT_ID} --form "text=${TG_MESSAGE}" 2>&1)
+        if [ "$(echo "${ANSWER}" | grep -Ec 'migrated.*supergroup')" -eq 1 ]
+        then
+            migrate_to_chat_id=$(echo "${ANSWER}" | sed -e 's/["}{]//g' | grep -Eo '\-[0-9]+$')
+            echo "${TO};${TG_CONTACT_TYPE};${migrate_to_chat_id}" >>${TMP_UIDS}
+            ANSWER=$(${CURL_TG}/sendMessage?chat_id=${migrate_to_chat_id} --form "text=${TG_MESSAGE}" 2>&1)
+        fi
+    ;;
+
+    "image")
+        PERIOD=3600 # default period
+        echo "${BODY}" | grep -q "^${ZBX_TG_PREFIX};graphs_period" && PERIOD=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};graphs_period=" '{if ($2 != "") print $2}' | tail -1 | grep -Eo '[0-9]+' || echo 3600)
+        ZBX_ITEMID=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};itemid:" '{if ($2 != "") print $2}' | tail -1 | grep -Eo '[0-9]+')
+        ZBX_TITLE=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};title:" '{if ($2 != "") print $2}' | tail -1)
+        URL="${ZBX_SERVER}/chart3.php?period=${PERIOD}&name=${ZBX_TITLE}&width=900&height=200&graphtype=0&legend=1&items[0][itemid]=${ZBX_ITEMID}&items[0][sortorder]=0&items[0][drawtype]=5&items[0][color]=00CC00"
+        IS_DEBUG && echo "Zabbix graph URL: ${URL}" >> ${LOG}
+        login
+        CACHE_IMAGE="${TMP_DIR}/graph.${ZBX_ITEMID}.png"
+        IS_DEBUG && echo "Image cached to ${CACHE_IMAGE} and wasn't deleted" >> ${LOG}
+        get_image "${URL}" ${CACHE_IMAGE}
+        TG_CAPTION_ORIG=$(echo -e "${SUBJECT}\n${TG_TEXT}")
+        TG_CAPTION=$(echo -e $(echo "${TG_CAPTION_ORIG}" | sed ':a;N;$!ba;s/\n/\\n/g' | awk '{print substr( $0, 0, 200 )}'))
+        if [ "${TG_CAPTION}" != "${TG_CAPTION_ORIG}" ]
+        then
+            echo "${ZBX_TG_PREFIX}: probably you will see MEDIA_CAPTION_TOO_LONG error, the message has been cut to 200 symbols, https://github.com/ableev/Zabbix-in-Telegram/issues/9#issuecomment-166895044"
+        fi
+        IS_DEBUG && echo "${CURL_TG}/sendPhoto?chat_id=${TG_CHAT_ID}\" --form \"caption=${TG_CAPTION}\" -F \"photo=@${CACHE_IMAGE}\"" >>${LOG}
+        ANSWER=$(${CURL_TG}/sendPhoto?chat_id=${TG_CHAT_ID} --form "caption=${TG_CAPTION}" -F "photo=@${CACHE_IMAGE}")
+        IS_DEBUG || rm ${CACHE_IMAGE}
+    ;;
+
+esac
+
+echo >>${LOG}
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..63b178b
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+PySocks==1.6.8
+requests==2.20.0
+requests-oauthlib==0.6.2
diff --git a/tg_vars.cfg.example b/tg_vars.cfg.example
deleted file mode 100644
index 938ab1d..0000000
--- a/tg_vars.cfg.example
+++ /dev/null
@@ -1,10 +0,0 @@
-TG_KEY="000:AAAAA_bbbb"
-
-ZBX_TG_PREFIX="zbxtg" # variable for separating text from script info
-
-ZBX_SERVER="http://zabbix.local" # zabbix server url
-ZBX_API_USER="api" # zabbix user; user must have at least read access to get graphs
-ZBX_API_PASS="apisecret"
-
-CURL="curl -s" # if you are using proxy server, it's time to add it right here
-# CURL="curl -x proxy.local:3128 -s"
diff --git a/zbxtg.py b/zbxtg.py
index 5099de9..3689ab5 100755
--- a/zbxtg.py
+++ b/zbxtg.py
@@ -5,22 +5,34 @@ import sys
 import os
 import time
 import random
+import string
 import requests
 import json
 import re
 import stat
+import hashlib
+import subprocess
+#import sqlite3
 from os.path import dirname
 import zbxtg_settings
 
 
-class TelegramAPI():
+class Cache:
+    def __init__(self, database):
+        self.database = database
+
+    def create_db(self, database):
+        pass
+
+
+class TelegramAPI:
     tg_url_bot_general = "https://api.telegram.org/bot"
 
     def http_get(self, url):
-        res = requests.get(url, proxies=self.proxies)
-        answer = res.text
-        answer_json = json.loads(answer.decode('utf8'))
-        return answer_json
+        answer = requests.get(url, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
 
     def __init__(self, key):
         self.debug = False
@@ -32,7 +44,15 @@ class TelegramAPI():
         self.disable_web_page_preview = False
         self.disable_notification = False
         self.reply_to_message_id = 0
+        self.tmp_dir = None
         self.tmp_uids = None
+        self.location = {"latitude": None, "longitude": None}
+        self.update_offset = 0
+        self.image_buttons = False
+        self.result = None
+        self.ok = None
+        self.error = None
+        self.get_updates_from_file = False
 
     def get_me(self):
         url = self.tg_url_bot_general + self.key + "/getMe"
@@ -41,17 +61,19 @@ class TelegramAPI():
 
     def get_updates(self):
         url = self.tg_url_bot_general + self.key + "/getUpdates"
+        params = {"offset": self.update_offset}
         if self.debug:
             print_message(url)
-        updates = self.http_get(url)
+        answer = requests.post(url, params=params, proxies=self.proxies)
+        self.result = answer.json()
+        if self.get_updates_from_file:
+            print_message("Getting updated from file getUpdates.txt")
+            self.result = json.loads("".join(file_read("getUpdates.txt")))
         if self.debug:
             print_message("Content of /getUpdates:")
-            print_message(updates)
-        if not updates["ok"]:
-            print_message(updates)
-            return updates
-        else:
-            return updates
+            print_message(json.dumps(self.result))
+        self.ok_update()
+        return self.result
 
     def send_message(self, to, message):
         url = self.tg_url_bot_general + self.key + "/sendMessage"
@@ -69,19 +91,50 @@ class TelegramAPI():
             print_message("Trying to /sendMessage:")
             print_message(url)
             print_message("post params: " + str(params))
-        res = requests.post(url, params=params, proxies=self.proxies)
-        answer = res.text
-        answer_json = json.loads(answer.decode('utf8'))
-        if not answer_json["ok"]:
-            print_message(answer_json)
-            return answer_json
+        answer = requests.post(url, params=params, proxies=self.proxies)
+        if answer.status_code == 414:
+            self.result = {"ok": False, "description": "414 URI Too Long"}
         else:
-            return answer_json
+            self.result = answer.json()
+        self.ok_update()
+        return self.result
+
+    def update_message(self, to, message_id, message):
+        url = self.tg_url_bot_general + self.key + "/editMessageText"
+        message = "\n".join(message)
+        params = {"chat_id": to, "message_id": message_id, "text": message,
+                  "disable_web_page_preview": self.disable_web_page_preview,
+                  "disable_notification": self.disable_notification}
+        if self.markdown or self.html:
+            parse_mode = "HTML"
+            if self.markdown:
+                parse_mode = "Markdown"
+            params["parse_mode"] = parse_mode
+        if self.debug:
+            print_message("Trying to /editMessageText:")
+            print_message(url)
+            print_message("post params: " + str(params))
+        answer = requests.post(url, params=params, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
 
     def send_photo(self, to, message, path):
         url = self.tg_url_bot_general + self.key + "/sendPhoto"
         message = "\n".join(message)
-        params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification}
+        if self.image_buttons:
+            reply_markup = json.dumps({"inline_keyboard": [[
+                {"text": "R", "callback_data": "graph_refresh"},
+                {"text": "1h", "callback_data": "graph_period_3600"},
+                {"text": "3h", "callback_data": "graph_period_10800"},
+                {"text": "6h", "callback_data": "graph_period_21600"},
+                {"text": "12h", "callback_data": "graph_period_43200"},
+                {"text": "24h", "callback_data": "graph_period_86400"},
+            ], ]})
+        else:
+            reply_markup = json.dumps({})
+        params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification,
+                  "reply_markup": reply_markup}
         if self.reply_to_message_id:
             params["reply_to_message_id"] = self.reply_to_message_id
         files = {"photo": open(path, 'rb')}
@@ -90,14 +143,34 @@ class TelegramAPI():
             print_message(url)
             print_message(params)
             print_message("files: " + str(files))
-        res = requests.post(url, params=params, files=files, proxies=self.proxies)
-        answer = res.text
-        answer_json = json.loads(answer.decode('utf8'))
-        if not answer_json["ok"]:
-            print_message(answer_json)
-            return answer_json
+        answer = requests.post(url, params=params, files=files, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
+
+    def send_txt(self, to, text, text_name=None):
+        path = self.tmp_dir + "/" + "zbxtg_txt_"
+        url = self.tg_url_bot_general + self.key + "/sendDocument"
+        text = "\n".join(text)
+        if not text_name:
+            path += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
         else:
-            return answer_json
+            path += text_name
+        path += ".txt"
+        file_write(path, text)
+        params = {"chat_id": to, "caption": path.split("/")[-1], "disable_notification": self.disable_notification}
+        if self.reply_to_message_id:
+            params["reply_to_message_id"] = self.reply_to_message_id
+        files = {"document": open(path, 'rb')}
+        if self.debug:
+            print_message("Trying to /sendDocument:")
+            print_message(url)
+            print_message(params)
+            print_message("files: " + str(files))
+        answer = requests.post(url, params=params, files=files, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
 
     def get_uid(self, name):
         uid = 0
@@ -109,21 +182,28 @@ class TelegramAPI():
                 chat = m["message"]["chat"]
             elif "edited_message" in m:
                 chat = m["edited_message"]["chat"]
+            else:
+                continue
             if chat["type"] == self.type == "private":
                 if "username" in chat:
                     if chat["username"] == name:
                         uid = chat["id"]
             if (chat["type"] == "group" or chat["type"] == "supergroup") and self.type == "group":
                 if "title" in chat:
-                    if chat["title"] == name.decode("utf-8"):
-                        uid = chat["id"]
+                    if sys.version_info[0] < 3:
+                        if chat["title"] == name.decode("utf-8"):
+                            uid = chat["id"]
+                    else:
+                        if chat["title"] == name:
+                            uid = chat["id"]
         return uid
 
     def error_need_to_contact(self, to):
         if self.type == "private":
             print_message("User '{0}' needs to send some text bot in private".format(to))
         if self.type == "group":
-            print_message("You need to mention your bot in '{0}' group chat (i.e. type @YourBot)".format(to))
+            print_message("You need start a conversation with your bot first in '{0}' group chat, type '/start@{1}'"
+                          .format(to, self.get_me()["result"]["username"]))
 
     def update_cache_uid(self, name, uid, message="Add new string to cache file"):
         cache_string = "{0};{1};{2}\n".format(name, self.type, str(uid).rstrip())
@@ -132,22 +212,68 @@ class TelegramAPI():
             print_message("{0}: {1}".format(message, cache_string))
         with open(self.tmp_uids, "a") as cache_file_uids:
             cache_file_uids.write(cache_string)
+        return True
 
     def get_uid_from_cache(self, name):
+        if self.debug:
+            print_message("Trying to read cached uid for {0}, {1}, from {2}".format(name, self.type, self.tmp_uids))
         uid = 0
         if os.path.isfile(self.tmp_uids):
             with open(self.tmp_uids, 'r') as cache_file_uids:
                 cache_uids_old = cache_file_uids.readlines()
-
             for u in cache_uids_old:
                 u_splitted = u.split(";")
                 if name == u_splitted[0] and self.type == u_splitted[1]:
                     uid = u_splitted[2]
-
         return uid
 
+    def send_location(self, to, coordinates):
+        url = self.tg_url_bot_general + self.key + "/sendLocation"
+        params = {"chat_id": to, "disable_notification": self.disable_notification,
+                  "latitude": coordinates["latitude"], "longitude": coordinates["longitude"]}
+        if self.reply_to_message_id:
+            params["reply_to_message_id"] = self.reply_to_message_id
+        if self.debug:
+            print_message("Trying to /sendLocation:")
+            print_message(url)
+            print_message("post params: " + str(params))
+        answer = requests.post(url, params=params, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
+
+    def answer_callback_query(self, callback_query_id, text=None):
+        url = self.tg_url_bot_general + self.key + "/answerCallbackQuery"
+        if not text:
+            params = {"callback_query_id": callback_query_id}
+        else:
+            params = {"callback_query_id": callback_query_id, "text": text}
+        answer = requests.post(url, params=params, proxies=self.proxies)
+        self.result = answer.json()
+        self.ok_update()
+        return self.result
+
+    def ok_update(self):
+        self.ok = self.result["ok"]
+        if self.ok:
+            self.error = None
+        else:
+            self.error = self.result["description"]
+            print_message(self.error)
+        return True
+
+
+def markdown_fix(message, offset, emoji=False):
+    offset = int(offset)
+    if emoji:  # https://github.com/ableev/Zabbix-in-Telegram/issues/152
+        offset -= 2
+    message = "\n".join(message)
+    message = message[:offset] + message[offset+1:]
+    message = message.split("\n")
+    return message
 
-class ZabbixAPI():
+
+class ZabbixWeb:
     def __init__(self, server, username, password):
         self.debug = False
         self.server = server
@@ -156,16 +282,19 @@ class ZabbixAPI():
         self.proxies = {}
         self.verify = True
         self.cookie = None
+        self.basic_auth_user = None
+        self.basic_auth_pass = None
+        self.tmp_dir = None
 
     def login(self):
-
         if not self.verify:
             requests.packages.urllib3.disable_warnings()
 
         data_api = {"name": self.username, "password": self.password, "enter": "Sign in"}
-        req_cookie = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify)
-        cookie = req_cookie.cookies
-        if len(req_cookie.history) > 1 and req_cookie.history[0].status_code == 302:
+        answer = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify,
+                               auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass))
+        cookie = answer.cookies
+        if len(answer.history) > 1 and answer.history[0].status_code == 302:
             print_message("probably the server in your config file has not full URL (for example "
                           "'{0}' instead of '{1}')".format(self.server, self.server + "/zabbix"))
         if not cookie:
@@ -174,26 +303,49 @@ class ZabbixAPI():
 
         self.cookie = cookie
 
-    def graph_get(self, itemid, period, title, width, height, tmp_dir):
-        file_img = tmp_dir + "/{0}.png".format(itemid)
+    def graph_get(self, itemid, period, title, width, height, version=3):
+        file_img = "{0}/{1}.png".format(self.tmp_dir,                                                   
+                                        "".join(random.choice(string.ascii_letters) for e in range(10)))
 
         title = requests.utils.quote(title)
 
-        zbx_img_url = self.server + "/chart3.php?period={1}&name={2}" \
-                                    "&width={3}&height={4}&graphtype=0&legend=1" \
-                                    "&items[0][itemid]={0}&items[0][sortorder]=0" \
-                                    "&items[0][drawtype]=5&items[0][color]=00CC00".format(itemid, period, title,
-                                                                                          width, height)
+        colors = {
+            0: "00CC00",
+            1: "CC0000",
+            2: "0000CC",
+            3: "CCCC00",
+            4: "00CCCC",
+            5: "CC00CC",
+        }
+
+        drawtype = 5
+        if len(itemid) > 1:
+            drawtype = 2
+
+        zbx_img_url_itemids = []
+        for i in range(0, len(itemid)):
+            itemid_url = "&items[{0}][itemid]={1}&items[{0}][sortorder]={0}&" \
+                         "items[{0}][drawtype]={3}&items[{0}][color]={2}".format(i, itemid[i], colors[i], drawtype)
+            zbx_img_url_itemids.append(itemid_url)
+
+        zbx_img_url = self.server + "/chart3.php?"
+        if version < 4:
+            zbx_img_url += "period={0}".format(period)
+        else:
+            zbx_img_url += "from=now-{0}&to=now".format(period)
+        zbx_img_url += "&name={0}&width={1}&height={2}&graphtype=0&legend=1".format(title, width, height)
+        zbx_img_url += "".join(zbx_img_url_itemids)
+
         if self.debug:
             print_message(zbx_img_url)
-        res = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify)
-        res_code = res.status_code
-        if res_code == 404:
+        answer = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify,
+                              auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass))
+        status_code = answer.status_code
+        if status_code == 404:
             print_message("can't get image from '{0}'".format(zbx_img_url))
             return False
-        res_img = res.content
-        with open(file_img, 'wb') as fp:
-            fp.write(res_img)
+        res_img = answer.content
+        file_bwrite(file_img, res_img)
         return file_img
 
     def api_test(self):
@@ -205,16 +357,15 @@ class ZabbixAPI():
         return api.text
 
 
-def print_message(string):
-    string = str(string) + "\n"
+def print_message(message):
+    message = str(message) + "\n"
     filename = sys.argv[0].split("/")[-1]
-    sys.stderr.write(filename + ": " + string)
+    sys.stderr.write(filename + ": " + message)
 
 
 def list_cut(elements, symbols_limit):
     symbols_count = symbols_count_now = 0
     elements_new = []
-    element_last = None
     element_last_list = []
     for e in elements:
         symbols_count_now = symbols_count + len(e)
@@ -238,9 +389,93 @@ def list_cut(elements, symbols_limit):
         return elements_new, True
 
 
+class Maps:
+    # https://developers.google.com/maps/documentation/geocoding/intro
+    def __init__(self):
+        self.key = None
+        self.proxies = {}
+
+    def get_coordinates_by_address(self, address):
+        coordinates = {"latitude": 0, "longitude": 0}
+        url_api = "https://maps.googleapis.com/maps/api/geocode/json?key={0}&address={1}".format(self.key, address)
+        url = url_api
+        answer = requests.get(url, proxies=self.proxies)
+        result = answer.json()
+        try:
+            coordinates_dict = result["results"][0]["geometry"]["location"]
+        except:
+            if "error_message" in result:
+                print_message("[" + result["status"] + "]: " + result["error_message"])
+            return coordinates
+        coordinates = {"latitude": coordinates_dict["lat"], "longitude": coordinates_dict["lng"]}
+        return coordinates
+
+
+def file_write(filename, text):
+    with open(filename, "w") as fd:
+        fd.write(str(text))
+    return True
+
+
+def file_bwrite(filename, data):
+    with open(filename, "wb") as fd:
+        fd.write(data)
+    return True
+
+
+def file_read(filename):
+    with open(filename, "r") as fd:
+        text = fd.readlines()
+    return text
+
+
+def file_append(filename, text):
+    with open(filename, "a") as fd:
+        fd.write(str(text))
+    return True
+
+
+def external_image_get(url, tmp_dir, timeout=6):
+    image_hash = hashlib.md5()
+    image_hash.update(url.encode())
+    file_img = tmp_dir + "/external_{0}.png".format(image_hash.hexdigest())
+    try:
+        answer = requests.get(url, timeout=timeout, allow_redirects=True)
+    except requests.exceptions.ReadTimeout as ex:
+        print_message("Can't get external image from '{0}': timeout".format(url))
+        return False
+    status_code = answer.status_code
+    if status_code == 404:
+        print_message("Can't get external image from '{0}': HTTP 404 error".format(url))
+        return False
+    answer_image = answer.content
+    file_bwrite(file_img, answer_image)
+    return file_img
+
+
+def age2sec(age_str):
+    age_sec = 0
+    age_regex = "([0-9]+d)?\s?([0-9]+h)?\s?([0-9]+m)?"
+    age_pattern = re.compile(age_regex)
+    intervals = age_pattern.match(age_str).groups()
+    for i in intervals:
+        if i:
+            metric = i[-1]
+            if metric == "d":
+                age_sec += int(i[0:-1])*86400
+            if metric == "h":
+                age_sec += int(i[0:-1])*3600
+            if metric == "m":
+                age_sec += int(i[0:-1])*60
+    return age_sec
+
+
 def main():
 
     tmp_dir = zbxtg_settings.zbx_tg_tmp_dir
+    if tmp_dir == "/tmp/" + zbxtg_settings.zbx_tg_prefix:
+        print_message("WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!")
+        print_message("https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings")
 
     tmp_cookie = tmp_dir + "/cookie.py.txt"
     tmp_uids = tmp_dir + "/uids.txt"
@@ -252,28 +487,150 @@ def main():
 
     log_file = "/dev/null"
 
-    zbx_to = sys.argv[1]
-    zbx_subject = sys.argv[2]
-    zbx_body = sys.argv[3]
+    args = sys.argv
+
+    settings = {
+        "zbxtg_itemid": "0",  # itemid for graph
+        "zbxtg_title": None,  # title for graph
+        "zbxtg_image_period": None,
+        "zbxtg_image_age": "3600",
+        "zbxtg_image_width": "900",
+        "zbxtg_image_height": "200",
+        "tg_method_image": False,  # if True - default send images, False - send text
+        "tg_chat": False,  # send message to chat or in private
+        "tg_group": False,  # send message to chat or in private
+        "is_debug": False,
+        "is_channel": False,
+        "disable_web_page_preview": False,
+        "location": None,  # address
+        "lat": 0,  # latitude
+        "lon": 0,  # longitude
+        "is_single_message": False,
+        "markdown": False,
+        "html": False,
+        "signature": None,
+        "signature_disable": False,
+        "graph_buttons": False,
+        "extimg": None,
+        "to": None,
+        "to_group": None,
+        "forked": False,
+    }
+
+    url_github = "https://github.com/ableev/Zabbix-in-Telegram"
+    url_wiki_base = "https://github.com/ableev/Zabbix-in-Telegram/wiki"
+    url_tg_group = "https://t.me/ZbxTg"
+    url_tg_channel = "https://t.me/Zabbix_in_Telegram"
+
+    settings_description = {
+        "itemid": {"name": "zbxtg_itemid", "type": "list",
+                   "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs"},
+        "title": {"name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs"},
+        "graphs_period": {"name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs"},
+        "graphs_age": {"name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs"},
+        "graphs_width": {"name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs"},
+        "graphs_height": {"name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs"},
+        "graphs": {"name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs"},
+        "chat": {"name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'",
+                 "url": "How-to-send-message-to-the-group-chat"},
+        "group": {"name": "tg_group", "type": "bool", "help": "sends message to a group",
+                  "url": "How-to-send-message-to-the-group-chat"},
+        "debug": {"name": "is_debug", "type": "bool", "help": "enables 'debug'",
+                  "url": "How-to-test-script-in-command-line"},
+        "channel": {"name": "is_channel", "type": "bool", "help": "sends message to a channel",
+                    "url": "Channel-support"},
+        "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool",
+                                     "help": "disable web page preview", "url": "Disable-web-page-preview"},
+        "location": {"name": "location", "type": "str", "help": "address of location", "url": "Location"},
+        "lat": {"name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location"},
+        "lon": {"name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location"},
+        "single_message": {"name": "is_single_message", "type": "bool", "help": "do not split message and graph",
+                           "url": "Why-am-I-getting-two-messages-instead-of-one"},
+        "markdown": {"name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"},
+        "html": {"name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"},
+        "signature": {"name": "signature", "type": "str",
+                      "help": "bot's signature", "url": "Bot-signature"},
+        "signature_disable": {"name": "signature_disable", "type": "bool",
+                              "help": "enables/disables bot's signature", "url": "Bot-signature"},
+        "graph_buttons": {"name": "graph_buttons", "type": "bool",
+                          "help": "activates buttons under graph, could be using in ZbxTgDaemon",
+                          "url": "Interactive-bot"},
+        "external_image": {"name": "extimg", "type": "str",
+                           "help": "should be url; attaches external image from different source",
+                           "url": "External-image-as-graph"},
+        "to": {"name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments",
+               "url": "Custom-to-and-to_group"},
+        "to_group": {"name": "to_group", "type": "str",
+                     "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group"},
+        "forked": {"name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": ""},
+    }
+
+    if len(args) < 4:
+        do_not_exit = False
+        if "--features" in args:
+            print(("List of available settings, see {0}/Settings\n---".format(url_wiki_base)))
+            for sett, proprt in list(settings_description.items()):
+                print(("{0}: {1}\ndoc: {2}/{3}\n--".format(sett, proprt["help"], url_wiki_base, proprt["url"])))
+
+        elif "--show-settings" in args:
+            do_not_exit = True
+            print_message("Settings: " + str(json.dumps(settings, indent=2)))
+
+        else:
+            print(("Hi. You should provide at least three arguments.\n"
+                   "zbxtg.py [TO] [SUBJECT] [BODY]\n\n"
+                  "1. Read main page and/or wiki: {0} + {1}\n"
+                  "2. Public Telegram group (discussion): {2}\n"
+                  "3. Public Telegram channel: {3}\n"
+                  "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev"
+                  .format(url_github, url_wiki_base, url_tg_group, url_tg_channel)))
+        if not do_not_exit:
+            sys.exit(0)
+
+
+    zbx_to = args[1]
+    zbx_subject = args[2]
+    zbx_body = args[3]
 
     tg = TelegramAPI(key=zbxtg_settings.tg_key)
 
+    tg.tmp_dir = tmp_dir
     tg.tmp_uids = tmp_uids
 
     if zbxtg_settings.proxy_to_tg:
+        proxy_to_tg = zbxtg_settings.proxy_to_tg
+        if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"):
+            proxy_to_tg = "https://" + proxy_to_tg
         tg.proxies = {
-            "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg),
-            "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg)
-            }
+            "https": "{0}".format(proxy_to_tg),
+        }
 
-    zbx = ZabbixAPI(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user,
+    zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user,
                     password=zbxtg_settings.zbx_api_pass)
 
+    zbx.tmp_dir = tmp_dir
+
+    # workaround for Zabbix 4.x
+    zbx_version = 3
+
+    try:
+        zbx_version = zbxtg_settings.zbx_server_version
+    except:
+        pass
+
     if zbxtg_settings.proxy_to_zbx:
         zbx.proxies = {
             "http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx),
             "https": "https://{0}/".format(zbxtg_settings.proxy_to_zbx)
-            }
+        }
+
+    # https://github.com/ableev/Zabbix-in-Telegram/issues/55
+    try:
+        if zbxtg_settings.zbx_basic_auth:
+            zbx.basic_auth_user = zbxtg_settings.zbx_basic_auth_user
+            zbx.basic_auth_pass = zbxtg_settings.zbx_basic_auth_pass
+    except:
+        pass
 
     try:
         zbx_api_verify = zbxtg_settings.zbx_api_verify
@@ -281,42 +638,38 @@ def main():
     except:
         pass
 
+    map = Maps()
+    # api key to resolve address to coordinates via google api
+    try:
+        if zbxtg_settings.google_maps_api_key:
+            map.key = zbxtg_settings.google_maps_api_key
+        if zbxtg_settings.proxy_to_tg:
+            map.proxies = {
+                "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg),
+                "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg)
+            }
+    except:
+        pass
+
     zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines()
     zbxtg_body_text = []
 
-    settings = {
-        "zbxtg_itemid": "0",  # itemid for graph
-        "zbxtg_title": None,  # title for graph
-        "zbxtg_image_period": "3600",
-        "zbxtg_image_width": "900",
-        "zbxtg_image_height": "200",
-        "tg_method_image": False,  # if True - default send images, False - send text
-        "tg_chat": False,  # send message to chat or in private
-        "is_debug": False,
-        "is_channel": False,
-        "disable_web_page_preview": False,
-    }
-    settings_description = {
-        "itemid": {"name": "zbxtg_itemid", "type": "int"},
-        "title": {"name": "zbxtg_title", "type": "str"},
-        "graphs_period": {"name": "zbxtg_image_period", "type": "int"},
-        "graphs_width": {"name": "zbxtg_image_width", "type": "int"},
-        "graphs_height": {"name": "zbxtg_image_height", "type": "int"},
-        "graphs": {"name": "tg_method_image", "type": "bool"},
-        "chat": {"name": "tg_chat", "type": "bool"},
-        "debug": {"name": "is_debug", "type": "bool"},
-        "channel": {"name": "is_channel", "type": "bool"},
-        "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool"},
-    }
-
     for line in zbxtg_body:
         if line.find(zbxtg_settings.zbx_tg_prefix) > -1:
-            setting = re.split("[\s\:\=]+", line, maxsplit=1)
+            setting = re.split("[\s:=]+", line, maxsplit=1)
             key = setting[0].replace(zbxtg_settings.zbx_tg_prefix + ";", "")
-            if len(setting) > 1 and len(setting[1]) > 0:
+            if key not in settings_description:
+                if "--debug" in args:
+                    print_message("[ERROR] There is no '{0}' method, use --features to get help".format(key))
+                continue
+            if settings_description[key]["type"] == "list":
+                value = setting[1].split(",")
+            elif len(setting) > 1 and len(setting[1]) > 0:
                 value = setting[1]
-            else:
+            elif settings_description[key]["type"] == "bool":
                 value = True
+            else:
+                value = settings[settings_description[key]["name"]]
             if key in settings_description:
                 settings[settings_description[key]["name"]] = value
         else:
@@ -324,16 +677,19 @@ def main():
 
     tg_method_image = bool(settings["tg_method_image"])
     tg_chat = bool(settings["tg_chat"])
+    tg_group = bool(settings["tg_group"])
     is_debug = bool(settings["is_debug"])
     is_channel = bool(settings["is_channel"])
     disable_web_page_preview = bool(settings["disable_web_page_preview"])
+    is_single_message = bool(settings["is_single_message"])
 
     # experimental way to send message to the group https://github.com/ableev/Zabbix-in-Telegram/issues/15
-    if sys.argv[0].split("/")[-1] == "zbxtg_group.py" or "--group" in sys.argv or tg_chat:
+    if args[0].split("/")[-1] == "zbxtg_group.py" or "--group" in args or tg_chat or tg_group:
         tg_chat = True
+        tg_group = True
         tg.type = "group"
 
-    if "--debug" in sys.argv or is_debug:
+    if "--debug" in args or is_debug:
         is_debug = True
         tg.debug = True
         zbx.debug = True
@@ -342,20 +698,41 @@ def main():
         log_file = tmp_dir + ".debug." + hash_ts + ".log"
         #print_message(log_file)
 
-    if "--markdown" in sys.argv:
+    if "--markdown" in args or settings["markdown"]:
         tg.markdown = True
 
-    if "--html" in sys.argv:
+    if "--html" in args or settings["html"]:
         tg.html = True
 
-    if "--channel" in sys.argv or is_channel:
+    if "--channel" in args or is_channel:
         tg.type = "channel"
 
-    if "--disable_web_page_preview" in sys.argv or disable_web_page_preview:
+    if "--disable_web_page_preview" in args or disable_web_page_preview:
         if is_debug:
             print_message("'disable_web_page_preview' option has been enabled")
         tg.disable_web_page_preview = True
 
+    if "--graph_buttons" in args or settings["graph_buttons"]:
+        tg.image_buttons = True
+
+    if "--forked" in args:
+        settings["forked"] = True
+
+    if "--tg-key" in args:
+        tg.key = args[args.index("--tg-key") + 1]
+
+    location_coordinates = {"latitude": None, "longitude": None}
+    if settings["lat"] > 0 and settings["lat"] > 0:
+        location_coordinates = {"latitude": settings["lat"], "longitude": settings["lon"]}
+        tg.location = location_coordinates
+    else:
+        if settings["location"]:
+            location_coordinates = map.get_coordinates_by_address(settings["location"])
+            if location_coordinates:
+                settings["lat"] = location_coordinates["latitude"]
+                settings["lon"] = location_coordinates["longitude"]
+                tg.location = location_coordinates
+
     if not os.path.isdir(tmp_dir):
         if is_debug:
             print_message("Tmp dir doesn't exist, creating new one...")
@@ -369,13 +746,62 @@ def main():
         if is_debug:
             print_message("Using {0} as a temporary dir".format(tmp_dir))
 
+    done_all_work_in_the_fork = False
+    # issue75
+
+    to_types = ["to", "to_group", "to_channel"]
+    to_types_to_telegram = {"to": "private", "to_group": "group", "to_channel": "channel"}
+    multiple_to = {}
+    for i in to_types:
+        multiple_to[i]=[]
+
+    for t in to_types:
+        try:
+            if settings[t] and not settings["forked"]:
+                # zbx_to = settings["to"]
+                multiple_to[t] = re.split(",", settings[t])
+        except KeyError:
+            pass
+
+    # example:
+    # {'to_channel': [], 'to': ['usr1', 'usr2', 'usr3'], 'to_group': []}
+
+    if (sum([len(v) for k, v in list(multiple_to.items())])) == 1:
+        # if we have only one recipient, we don't need fork to send message, just re-write "to" vaiable
+        tmp_max = 0
+        for t in to_types:
+            if len(multiple_to[t]) > tmp_max:
+                tmp_max = len(multiple_to[t])
+                tg.type = to_types_to_telegram[t]
+                zbx_to = multiple_to[t][0]
+    else:
+        for t in to_types:
+            for i in multiple_to[t]:
+                args_new = list(args)
+                args_new[1] = i
+                if t == "to_group":
+                    args_new.append("--group")
+                args_new.append("--forked")
+                args_new.insert(0, sys.executable)
+                if is_debug:
+                    print_message("Fork for custom recipient ({1}), new args: {0}".format(args_new,
+                                                                                          to_types_to_telegram[t]))
+                subprocess.call(args_new)
+                done_all_work_in_the_fork = True
+
+    if done_all_work_in_the_fork:
+        sys.exit(0)
+
     uid = None
 
     if tg.type == "channel":
         uid = zbx_to
-    else:
+    if tg.type == "private":
         zbx_to = zbx_to.replace("@", "")
 
+    if zbx_to.isdigit():
+        uid = zbx_to
+
     if not uid:
         uid = tg.get_uid_from_cache(zbx_to)
 
@@ -395,59 +821,120 @@ def main():
 
     # add signature, turned off by default, you can turn it on in config
     try:
-        if zbxtg_settings.zbx_tg_signature:
+        if "--signature" in args or settings["signature"] or zbxtg_settings.zbx_tg_signature\
+                and not "--signature_disable" in args and not settings["signature_disable"]:
+            if "--signature" in args:
+                settings["signature"] = args[args.index("--signature") + 1]
+            if not settings["signature"]:
+                settings["signature"] = zbxtg_settings.zbx_server
             zbxtg_body_text.append("--")
-            zbxtg_body_text.append(zbxtg_settings.zbx_server)
+            zbxtg_body_text.append(settings["signature"])
     except:
         pass
 
     # replace text with emojis
+    internal_using_emoji = False  # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152
     if hasattr(zbxtg_settings, "emoji_map"):
         zbxtg_body_text_emoji_support = []
         for l in zbxtg_body_text:
             l_new = l
-            for k, v in zbxtg_settings.emoji_map.iteritems():
+            for k, v in list(zbxtg_settings.emoji_map.items()):
                 l_new = l_new.replace("{{" + k + "}}", v)
             zbxtg_body_text_emoji_support.append(l_new)
+        if len("".join(zbxtg_body_text)) - len("".join(zbxtg_body_text_emoji_support)):
+            internal_using_emoji = True
         zbxtg_body_text = zbxtg_body_text_emoji_support
 
-    if not tg_method_image:
-        result = tg.send_message(uid, zbxtg_body_text)
-        if not result["ok"]:
-            if result["description"] == "[Error]: Bad Request: group chat is migrated to supergroup chat":
-                migrate_to_chat_id = result["parameters"]["migrate_to_chat_id"]
-                tg.update_cache_uid(zbx_to, uid, message="Group chat is migrated to supergroup, updating cache file")
+    if not is_single_message:
+        tg.send_message(uid, zbxtg_body_text)
+        if not tg.ok:
+            # first case Б─⌠ if group has been migrated to a supergroup, we need to update chat_id of that group
+            if tg.error.find("migrated") > -1 and tg.error.find("supergroup") > -1:
+                migrate_to_chat_id = tg.result["parameters"]["migrate_to_chat_id"]
+                tg.update_cache_uid(zbx_to, migrate_to_chat_id, message="Group chat is migrated to supergroup, "
+                                                                        "updating cache file")
                 uid = migrate_to_chat_id
-                result = tg.send_message(uid, zbxtg_body_text)
-    else:
+                tg.send_message(uid, zbxtg_body_text)
+
+            # another case if markdown is enabled and we got parse error, try to remove "bad" symbols from message
+            if tg.markdown and tg.error.find("Can't find end of the entity starting at byte offset") > -1:
+                markdown_warning = "Original message has been fixed due to {0}. " \
+                                   "Please, fix the markdown, it's slowing down messages sending."\
+                    .format(url_wiki_base + "/" + settings_description["markdown"]["url"])
+                markdown_fix_attempts = 0
+                while not tg.ok and markdown_fix_attempts != 3:
+                    offset = re.search("Can't find end of the entity starting at byte offset ([0-9]+)", tg.error).group(1)
+                    zbxtg_body_text = markdown_fix(zbxtg_body_text, offset, emoji=internal_using_emoji) + \
+                                      ["\n"] + [markdown_warning]
+                    tg.disable_web_page_preview = True
+                    tg.send_message(uid, zbxtg_body_text)
+                    markdown_fix_attempts += 1
+                if tg.ok:
+                    print_message(markdown_warning)
+
+    if is_debug:
+        print((tg.result))
+
+    if settings["zbxtg_image_age"]:
+        age_sec = age2sec(settings["zbxtg_image_age"])
+        if age_sec > 0 and age_sec > 3600:
+            settings["zbxtg_image_period"] = age_sec
+
+    message_id = 0
+    if tg_method_image:
         zbx.login()
         if not zbx.cookie:
-            print_message("Login to Zabbix web UI has failed, check manually...")
+            text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\
+                        "unable to send graphs check manually"
+            tg.send_message(uid, [text_warn])
+            print_message(text_warn)
         else:
-            zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"],
-                                           settings["zbxtg_title"], settings["zbxtg_image_width"],
-                                           settings["zbxtg_image_height"], tmp_dir)
-            #zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200)
-            result = tg.send_message(uid, zbxtg_body_text)
-            message_id = result["result"]["message_id"]
+            if not settings["extimg"]:
+                zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"],
+                                               settings["zbxtg_title"], settings["zbxtg_image_width"],
+                                               settings["zbxtg_image_height"], version=zbx_version)
+            else:
+                zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir)
+            zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200)
+            if tg.ok:
+                message_id = tg.result["result"]["message_id"]
             tg.reply_to_message_id = message_id
-            tg.disable_notification = True
             if not zbxtg_file_img:
-                tg.send_message(uid, ["Can't get graph image, check script manually, see logs, or disable graphs"])
-                print_message("Can't get image, check URL manually")
+                text_warn = "Can't get graph image, check script manually, see logs, or disable graphs"
+                tg.send_message(uid, [text_warn])
+                print_message(text_warn)
             else:
-
-                zbxtg_body_text = ""
-                """
-                if is_modified:
-                    print_message("probably you will see MEDIA_CAPTION_TOO_LONG error, "
-                                  "the message has been cut to 200 symbols, "
-                                  "https://github.com/ableev/Zabbix-in-Telegram/issues/9"
-                                  "#issuecomment-166895044")
-                """
-                if tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img):
+                if not is_single_message:
+                    zbxtg_body_text = ""
+                else:
+                    if is_modified:
+                        text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\
+                                    "the message has been cut to 200 symbols, "\
+                                    "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\
+                                    "#issuecomment-166895044"
+                        print_message(text_warn)
+                if not is_single_message:
+                    tg.disable_notification = True
+                tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img)
+                if tg.ok:
+                    settings["zbxtg_body_text"] = zbxtg_body_text
                     os.remove(zbxtg_file_img)
-
+                else:
+                    if tg.error.find("PHOTO_INVALID_DIMENSIONS") > -1:
+                        if not tg.disable_web_page_preview:
+                            tg.disable_web_page_preview = True
+                        text_warn = "Zabbix user couldn't get graph (probably has no rights to get data from host), " \
+                                    "check script manually, see {0}".format(url_wiki_base + "/" +
+                                                                            settings_description["graphs"]["url"])
+                        tg.send_message(uid, [text_warn])
+                        print_message(text_warn)
+    if tg.location and location_coordinates["latitude"] and location_coordinates["longitude"]:
+        tg.reply_to_message_id = message_id
+        tg.disable_notification = True
+        tg.send_location(to=uid, coordinates=location_coordinates)
+
+    if "--show-settings" in args:
+        print_message("Settings: " + str(json.dumps(settings, indent=2)))
 
 if __name__ == "__main__":
-    main()
\ No newline at end of file
+    main()
diff --git a/zbxtg.sh b/zbxtg.sh
deleted file mode 100755
index f9c9663..0000000
--- a/zbxtg.sh
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/bin/bash
-
-. $(dirname "$0")/tg_vars.cfg
-
-CURL_TG="${CURL} https://api.telegram.org/bot${TG_KEY}"
-
-TMP_DIR="/tmp/${ZBX_TG_PREFIX}"
-[ ! -d "${TMP_DIR}" ] && (mkdir -p ${TMP_DIR} || TMP_DIR="/tmp")
-TMP_COOKIE="${TMP_DIR}/cookie.txt"
-TMP_UIDS="${TMP_DIR}/uids.txt"
-
-TS="`date +%s_%N`_$RANDOM"
-LOG="/dev/null"
-
-IS_DEBUG () {
-    if [ "${ISDEBUG}" == "TRUE" ]
-    then
-        return 0
-    else
-        return 1
-    fi
-}
-
-
-login() {
-    # grab cookie for downloading image
-    IS_DEBUG && echo "${CURL} --cookie-jar ${TMP_COOKIE} --request POST --data \"name=${ZBX_API_USER}&password=${ZBX_API_PASS}&enter=Sign%20in\" ${ZBX_SERVER}/" >>${LOG}
-    ${CURL} --cookie-jar ${TMP_COOKIE} --request POST --data "name=${ZBX_API_USER}&password=${ZBX_API_PASS}&enter=Sign%20in" ${ZBX_SERVER}/
-}
-
-get_image() {
-    URL=$1
-    URL=$(echo "${URL}" | sed -e 's/\ /%20/g')
-    IMG_NAME=$2
-    # downloads png graph and saves it to temporary path
-    IS_DEBUG && echo "${CURL} --cookie ${TMP_COOKIE} --globoff \"${URL}\" -o ${IMG_NAME}" >>${LOG}
-    ${CURL} --cookie ${TMP_COOKIE} --globoff "${URL}" -o ${IMG_NAME}
-}
-
-TO=$1
-SUBJECT=$2
-BODY=$3
-
-TG_CHAT=0 # send message to chat or to private chat to user
-METHOD="txt" # sendMessage (simple text) or sendPhoto (attached image)
-
-echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};graphs" && METHOD="image"
-echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};chat" && TG_CHAT=1
-echo "${BODY}" | grep -q "${ZBX_TG_PREFIX};debug" && ISDEBUG="TRUE"
-
-IS_DEBUG && LOG="${TMP_DIR}/debug.${TS}.log"
-IS_DEBUG && echo -e "TMP_DIR=${TMP_DIR}\nTMP_COOKIE=${TMP_COOKIE}\nTMP_UIDS=${TMP_UIDS}" >>${LOG}
-
-if [ "${TG_CHAT}" -eq 1 ]
-then
-    TG_CONTACT_TYPE="chat"
-else
-    TG_CONTACT_TYPE="user"
-fi
-
-TG_CHAT_ID=$(cat ${TMP_UIDS} | awk -F ';' '{if ($1 == "'${TO}'" && $2 == "'${TG_CONTACT_TYPE}'") print $3}' | tail -1)
-
-if [ -z "${TG_CHAT_ID}" ]
-then
-    TG_UPDATES=$(${CURL_TG}/getUpdates)
-    if [ "${TG_CHAT}" -eq 1 ]
-    then
-        TG_CHAT_ID=$(echo "${TG_UPDATES}" | sed -e 's/["}{]//g' | awk -F ',' '{if ($8 == "type:group" && $7 == "title:'${TO}'") {gsub("chat:id:", "", $6); print $6}}' | tail -1)
-    else
-        TG_CHAT_ID=$(echo "${TG_UPDATES}" | sed -e 's/["}{]//g' | awk -F ',' '{if ($10 == "type:private" && $5 == "username:'${TO}'") {gsub("chat:id:", "", $6); print $6}}' | tail -1)
-    fi
-    echo "${TO};${TG_CONTACT_TYPE};${TG_CHAT_ID}" >>${TMP_UIDS}
-fi
-
-IS_DEBUG && echo "TG_CHAT_ID: ${TG_CHAT_ID}" >>${LOG}
-
-TG_TEXT=$(echo "${BODY}" | grep -vE "^${ZBX_TG_PREFIX};"; echo "--"; echo "${ZBX_SERVER}")
-
-case "${METHOD}" in
-
-    "txt")
-        TG_MESSAGE=$(echo -e "${SUBJECT}\n${TG_TEXT}")
-        IS_DEBUG && echo "${CURL_TG}/sendMessage -F \"chat_id=${TG_CHAT_ID}\" -F \"text=${TG_MESSAGE}\"" >>${LOG}
-        ${CURL_TG}/sendMessage -F "chat_id=${TG_CHAT_ID}" -F "text=${TG_MESSAGE}" >>${LOG} 2>&1
-    ;;
-
-    "image")
-        PERIOD=3600 # default period
-        echo "${BODY}" | grep -q "^${ZBX_TG_PREFIX};graphs_period" && PERIOD=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};graphs_period=" '{if ($2 != "") print $2}' | tail -1 | grep -Eo '[0-9]+' || echo 3600)
-        ZBX_ITEMID=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};itemid:" '{if ($2 != "") print $2}' | tail -1 | grep -Eo '[0-9]+')
-        ZBX_TITLE=$(echo "${BODY}" | awk -F "${ZBX_TG_PREFIX};title:" '{if ($2 != "") print $2}' | tail -1)
-        URL="${ZBX_SERVER}/chart3.php?period=${PERIOD}&name=${ZBX_TITLE}&width=900&height=200&graphtype=0&legend=1&items[0][itemid]=${ZBX_ITEMID}&items[0][sortorder]=0&items[0][drawtype]=5&items[0][color]=00CC00"
-        IS_DEBUG && echo "Zabbix graph URL: ${URL}" >> ${LOG}
-        login
-        CACHE_IMAGE="${TMP_DIR}/graph.${ZBX_ITEMID}.png"
-        IS_DEBUG && echo "Image cached to ${CACHE_IMAGE} and wasn't deleted" >> ${LOG}
-        get_image "${URL}" ${CACHE_IMAGE}
-        TG_CAPTION_ORIG=$(echo -e "${SUBJECT}\n${TG_TEXT}")
-        TG_CAPTION=$(echo -e $(echo "${TG_CAPTION_ORIG}" | sed ':a;N;$!ba;s/\n/\\n/g' | awk '{print substr( $0, 0, 200 )}'))
-        if [ "${TG_CAPTION}" != "${TG_CAPTION_ORIG}" ]
-        then
-            echo "${ZBX_TG_PREFIX}: probably you will see MEDIA_CAPTION_TOO_LONG error, the message has been cut to 200 symbols, https://github.com/ableev/Zabbix-in-Telegram/issues/9#issuecomment-166895044"
-        fi
-        IS_DEBUG && echo "${CURL_TG}/sendPhoto -F \"chat_id=${TG_CHAT_ID}\" -F \"caption=${TG_CAPTION}\" -F \"photo=@${CACHE_IMAGE}\"" >>${LOG}
-        ${CURL_TG}/sendPhoto -F "chat_id=${TG_CHAT_ID}" -F "caption=${TG_CAPTION}" -F "photo=@${CACHE_IMAGE}" >>${LOG} 2>&1
-        IS_DEBUG || rm ${CACHE_IMAGE}
-    ;;
-
-esac
-
-echo >>${LOG}
diff --git a/zbxtg_settings.example.py b/zbxtg_settings.example.py
index 4b313df..4b9e656 100644
--- a/zbxtg_settings.example.py
+++ b/zbxtg_settings.example.py
@@ -3,27 +3,66 @@
 tg_key = "XYZ"  # telegram bot api key
 
 zbx_tg_prefix = "zbxtg"  # variable for separating text from script info
-zbx_tg_tmp_dir = "/tmp/" + zbx_tg_prefix  # directory for saving caches, uids, cookies, etc.
+zbx_tg_tmp_dir = "/var/tmp/" + zbx_tg_prefix  # directory for saving caches, uids, cookies, etc.
 zbx_tg_signature = False
 
-zbx_server = "http://localhost"  # zabbix server full url
+zbx_tg_update_messages = True
+zbx_tg_matches = {
+    "problem": "PROBLEM: ",
+    "ok": "OK: "
+}
+
+zbx_server = "http://127.0.0.1/zabbix/"  # zabbix server full url
 zbx_api_user = "api"
 zbx_api_pass = "api"
 zbx_api_verify = True  # True - do not ignore self signed certificates, False - ignore
 
+#zbx_server_version = 2  # for Zabbix 2.x version
+zbx_server_version = 3  # for Zabbix 3.x version, by default, not everyone updated to 4.x yet
+#zbx_server_version = 4  # for Zabbix 4.x version, default will be changed in the future with this
+
+zbx_basic_auth = False
+zbx_basic_auth_user = "zabbix"
+zbx_basic_auth_pass = "zabbix"
+
 proxy_to_zbx = None
 proxy_to_tg = None
 
-#proxy_to_zbx = "proxy.local:3128"
-#proxy_to_tg = "proxy.local:3128"
+# proxy_to_zbx = "http://proxy.local:3128"
+# proxy_to_tg = "https://proxy.local:3128"
+
+# proxy_to_tg = "socks5://user1:password2@hostname:port" # socks5 with username and password
+# proxy_to_tg = "socks5://hostname:port" # socks5 without username and password
+# proxy_to_tg = "socks5h://hostname:port" # hostname resolution on SOCKS proxy. 
+                                          # This helps when internet provider alter DNS queries.
+                                          # Found here: https://stackoverflow.com/a/43266186/957508
+
+google_maps_api_key = None  # get your key, see https://developers.google.com/maps/documentation/geocoding/intro
+
+zbx_tg_daemon_enabled = False
+zbx_tg_daemon_enabled_ids = [6931850, ]
+zbx_tg_daemon_enabled_users = ["ableev", ]
+zbx_tg_daemon_enabled_chats = ["Zabbix in Telegram Script", ]
+
+zbx_db_host = "localhost"
+zbx_db_database = "zabbix"
+zbx_db_user = "zbxtg"
+zbx_db_password = "zbxtg"
+
 
 emoji_map = {
-    "ok": "Б°┘",
-    "problem": "Б²≈",
+    "Disaster": "П÷■╔",
+    "High": "П÷⌡▒",
+    "Average": "Б²≈",
+    "Warning": "Б ═О╦▐",
+    "Information": "Б└╧О╦▐",
+    "Not classified": "П÷■≤",
+    "OK": "Б°┘",
+    "PROBLEM": "Б²≈",
     "info": "Б└╧О╦▐",
-    "warning": "Б ═О╦▐",
-    "disaster": "Б²▄",
+    "WARNING": "Б ═О╦▐",
+    "DISASTER": "Б²▄",
     "bomb": "П÷▓ё",
     "fire": "П÷■╔",
     "hankey": "П÷▓╘",
-}
\ No newline at end of file
+}
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin