diff --git a/README.md b/README.md index 004d24a..9c0233f 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,63 @@ -# Server Manager Bot +# SERVER MANAGER BOT +Команды и возможности бота: -A Telegram Bot: +| Команда | Описание | +| ------ | ------ | +| `/stats` | дает обобщенную статистику о памяти \ диске \ процессах, а также общую нагрузку за определнный период времени | +| `/shell` | позволяет использовать бот в режиме Shell | +| `/memgraph` | строит график использования памяти за прошедший период | +| `/setmem` | возможность установить порог оперативной памяти (%) для мониторинга | +| `/setpoll` | интервал опроса в секундах (выше 10) | -* Commands - * `/stats` - gives summed statistics about memory \ disk \ processes (will improve) - * `/shell` - goes into the mode of executing shell commands & sends you the output - * `/memgraph` - plots a graph of memory usage for a past period and sends you a picture of the graph - * `/setmem` - set memory threshold (%) to monitor and notify if memory usage goes above it - * `/setpoll` - set polling interval in seconds (higher than 10) -* Monitors memory usage and if it reaches above the set threshold = sends you warning message +------------ +**Как это работает**: [Смотреть GIF](https://i.13.wf/2019/08/17/1566074720-2541.gif) +![Вывод изображения ](https://i.13.yt/2019/08/19/1566205847-6575.png) -Example summary: [Gif](http://i.imgur.com/AhCvy9W.gifv) +**Пример графика**: -![Bot](http://i.imgur.com/hXT0drx.png) +![Пример графика](https://i.13.wf/2019/08/19/1566205949-2775.jpg) +------------ -Example shell command output as a message from the bot: +## Установка -![Shell](https://i.imgur.com/PtvcaSD.png) +```sh +$ git https://github.com/vladios13/ServerStatsBot.git +$ cd ServerStatsBot +$ sudo pip3 install -r requirements.txt +``` +* Все ключи и токены сохраняйте в `tokens.py`. +* Получить токен для бота можно в [Bot Father](https://t.me/BotFather) +* В этот файл поместите строковую переменную `telegrambot` токен вашего бота. +* Пример: `telegrambot = "000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"` +Вы должны установить переменную. `adminchatid` в `tokens.py`. Вы так же можете указать несколько пользователей. +**Пример**: +* `adminchatid = [443355]` +* `adminchatid = [443355, 55667788, 99884433]` -Example graph sent by bot: [Gif](http://i.imgur.com/anX7rJR.gifv) - -![Graph](http://i.imgur.com/K8mG3aM.jpg?1) - -# Usage - -## Requirements - -* Python 3+ -* [Telepot](https://github.com/nickoala/telepot) -* [Psutil](https://github.com/giampaolo/psutil) - * Make sure to install it for Python 3+ - * In order to make sure that `pip` installs packages for the 3+ version: - * `curl -O https://bootstrap.pypa.io/get-pip.py` - * `sudo python3 get-pip.py` - * After that `pip install psutil` - * Also Stackoverflow question about that [here](http://stackoverflow.com/questions/11268501/how-to-use-pip-with-python-3-x-alongside-python-2-x) -* [matplotlib](http://matplotlib.org/) - * `sudo apt-get install python3-matplotlib` -* Bot key & `tokens.py` - * Hide all the keys and admin variables in `tokens.py`. Use it only for sensitive variables. Avoid creating functions not to clutter the namespaces through the import. - * Get a key from the [Bot Father](https://telegram.me/BotFather) - * Clone that repo - * In the folder with the cloned repo create a file `tokens.py` - * It's added to the `.gitignore` so you don't commit your own (and I don't commit mine:) - * In that file put a string variable `telegrambot` which equals your key - * For example: `telegrambot = "000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"` - -## Running the bot - -`python3 servstatsbot.py` +------------ -## Running the bot as "daemon" +## Запуск -* See included file in the repo: `servstatsbot.conf` - * Open it and edit the path as mentiond in the comments there -* Place that file in `/etc/init/` -* Start the "daemon" with: `start servstatsbot` - * You can start|stop|restart - * If bot crashes it'll be automatically restarted - * It will also start after reboot +Выполните: `python3 servstatsbot.py` -## Setting an admin +------------ -You have to set a variable `adminchatid` in `tokens.py` to be equal your chat_id or multiple chat_id (if more people will use your bot). -For example: +## Запуск в режиме "daemon" -* `adminchatid = [443355]` -* `adminchatid = [443355, 55667788, 99884433]` +* Вся нужная информация находится в: `servstatsbot.conf` +* Откройте его и отредактируйте путь, как указано в комментариях к нему. +* Поместите файл в папку `/etc/init/` +* Запустите как "daemon" : `start servstatsbot` + * Используйте `start|stop|restart` + * Если произойдет сбой, он будет автоматически перезапущен. + * Он также заработает после перезагрузки. +------------ +# Разработчики бота -I will reimplement this differently later. - - -# PLEASE CONTRIBUTE :) - I threw this code together within 10 minutes or so as a mockup to work on it later. But I think it's a nice bot idea and some of you guys might like this too. So please feel free to fork, pull, requests features! - Can give contributors access! - Would really love to see this bot grow some fat and brain:) - - -# Other bot development - -## Alfred +### Alfred - разрботчик [http://alfredthebot.com](http://alfredthebot.com) - - - GB +### vladios13 - локализация и доработка 🌚 +[Blog vladios13](http://blog.vladios13.com) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bc12e76 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +psutil +telepot +matplotlib \ No newline at end of file diff --git a/servstatsbot.py b/servstatsbot.py index 95c1ffc..c3226fa 100644 --- a/servstatsbot.py +++ b/servstatsbot.py @@ -1,3 +1,5 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- from tokens import * import matplotlib matplotlib.use("Agg") # has to be before any other matplotlibs imports to set a "headless" backend @@ -12,14 +14,14 @@ # import threading # import random import telepot -# from telepot.namedtuple import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardHide, ForceReply +from telepot.namedtuple import ReplyKeyboardMarkup, KeyboardButton # from telepot.namedtuple import InlineKeyboardMarkup, InlineKeyboardButton # from telepot.namedtuple import InlineQueryResultArticle, InlineQueryResultPhoto, InputTextMessageContent -memorythreshold = 85 # If memory usage more this % -poll = 300 # seconds +memorythreshold = 85 # Если потребляется больше памяти, то этот % равен: +poll = 300 # значения в секундах shellexecution = [] timelist = [] @@ -29,7 +31,8 @@ setpolling = [] graphstart = datetime.now() -stopmarkup = {'keyboard': [['Stop']]} +stopmarkup = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text="Stop Shell")]],resize_keyboard=True) +# stopmarkup = {'keyboard': [['Stop']]} hide_keyboard = {'hide_keyboard': True} def clearall(chat_id): @@ -44,9 +47,9 @@ def plotmemgraph(memlist, xaxis, tmperiod): # print(memlist) # print(xaxis) plt.xlabel(tmperiod) - plt.ylabel('% Used') - plt.title('Memory Usage Graph') - plt.text(0.1*len(xaxis), memorythreshold+2, 'Threshold: '+str(memorythreshold)+ ' %') + plt.ylabel('% использовано') + plt.title('График использования ОЗУ') + plt.text(0.1*len(xaxis), memorythreshold+2, 'Порог: '+str(memorythreshold)+ ' %') memthresholdarr = [] for xas in xaxis: memthresholdarr.append(memorythreshold) @@ -54,7 +57,7 @@ def plotmemgraph(memlist, xaxis, tmperiod): plt.axis([0, len(xaxis)-1, 0, 100]) plt.savefig('/tmp/graph.png') plt.close() - f = open('/tmp/graph.png', 'rb') # some file on local disk + f = open('/tmp/graph.png', 'rb') # файл изображние на локальном диске return f @@ -67,20 +70,30 @@ def __init__(self, *args, **kwargs): def on_chat_message(self, msg): content_type, chat_type, chat_id = telepot.glance(msg) # Do your stuff according to `content_type` ... - print("Your chat_id:" + str(chat_id)) # this will tell you your chat_id - if chat_id in adminchatid: # Store adminchatid variable in tokens.py + print("Ваш chat_id:" + str(chat_id)) # узнать chat_id + if chat_id in adminchatid: # Переменная adminchatid хранится в tokens.py if content_type == 'text': if msg['text'] == '/stats' and chat_id not in shellexecution: bot.sendChatAction(chat_id, 'typing') memory = psutil.virtual_memory() disk = psutil.disk_usage('/') + cpuget = psutil.getloadavg() + сpu_countF = psutil.cpu_count(logical=False) + сpu_countL = psutil.cpu_count(logical=True) + cpufr = psutil.cpu_freq(percpu=False) boottime = datetime.fromtimestamp(psutil.boot_time()) now = datetime.now() - timedif = "Online for: %.1f Hours" % (((now - boottime).total_seconds()) / 3600) - memtotal = "Total memory: %.2f GB " % (memory.total / 1000000000) - memavail = "Available memory: %.2f GB" % (memory.available / 1000000000) - memuseperc = "Used memory: " + str(memory.percent) + " %" - diskused = "Disk used: " + str(disk.percent) + " %" + cpuget = "Средняя нагрузка CPU: " + str(cpuget) + cpufr = "Текущая частота CPU: " + str(cpufr.current) + "Mhz" + сpu_countF = "Физических ядер: " + str(сpu_countF) + сpu_countL = "Логических ядер: " + str(сpu_countL) + timedif = "Сервер работает уже: %.1f часов" % (((now - boottime).total_seconds()) / 3600) + memtotal = "Всего ОЗУ: %.2f GB " % (memory.total / 1000000000) + memavail = "Свободно ОЗУ: %.2f GB" % (memory.available / 1000000000) + memuseperc = "Используется ОЗУ: " + str(memory.percent) + " %" + diskused = "Использовано HDD: " + str(disk.percent) + " %" + pidsinf = "Текущие процессы:" + pids = psutil.pids() pidsreply = '' procs = {} @@ -94,24 +107,29 @@ def on_chat_message(self, msg): else: procs[p.name()] = pmem except: - print("Hm") + print("ХМ?") sortedprocs = sorted(procs.items(), key=operator.itemgetter(1), reverse=True) for proc in sortedprocs: pidsreply += proc[0] + " " + ("%.2f" % proc[1]) + " %\n" reply = timedif + "\n" + \ + cpuget + "\n" + \ + cpufr + "\n" + \ memtotal + "\n" + \ + сpu_countF + "\n" + \ + сpu_countL + "\n" + \ memavail + "\n" + \ memuseperc + "\n" + \ diskused + "\n\n" + \ + pidsinf + "\n" + \ pidsreply - bot.sendMessage(chat_id, reply, disable_web_page_preview=True) + bot.sendMessage(chat_id, reply, disable_web_page_preview=True, parse_mode='HTML') elif msg['text'] == "Stop": clearall(chat_id) - bot.sendMessage(chat_id, "All operations stopped.", reply_markup=hide_keyboard) + bot.sendMessage(chat_id, "Все операции остановлены", reply_markup=hide_keyboard) elif msg['text'] == '/setpoll' and chat_id not in setpolling: bot.sendChatAction(chat_id, 'typing') setpolling.append(chat_id) - bot.sendMessage(chat_id, "Send me a new polling interval in seconds? (higher than 10)", reply_markup=stopmarkup) + bot.sendMessage(chat_id, "Отправьте мне новый интервал проверки в секундах? (выше 10)", reply_markup=stopmarkup) elif chat_id in setpolling: bot.sendChatAction(chat_id, 'typing') try: @@ -123,26 +141,26 @@ def on_chat_message(self, msg): else: 1/0 except: - bot.sendMessage(chat_id, "Please send a proper numeric value higher than 10.") + bot.sendMessage(chat_id, "Отправьте числовое значение выше 10.") elif msg['text'] == "/shell" and chat_id not in shellexecution: - bot.sendMessage(chat_id, "Send me a shell command to execute", reply_markup=stopmarkup) + bot.sendMessage(chat_id, "Отправьте мне Shell-комманду", reply_markup=stopmarkup) shellexecution.append(chat_id) elif msg['text'] == "/setmem" and chat_id not in settingmemth: bot.sendChatAction(chat_id, 'typing') settingmemth.append(chat_id) - bot.sendMessage(chat_id, "Send me a new memory threshold to monitor?", reply_markup=stopmarkup) + bot.sendMessage(chat_id, "Установите новый порог памяти для мониторинга", reply_markup=stopmarkup) elif chat_id in settingmemth: bot.sendChatAction(chat_id, 'typing') try: global memorythreshold memorythreshold = int(msg['text']) if memorythreshold < 100: - bot.sendMessage(chat_id, "All set!") + bot.sendMessage(chat_id, "Все получилось!") clearall(chat_id) else: 1/0 except: - bot.sendMessage(chat_id, "Please send a proper numeric value below 100.") + bot.sendMessage(chat_id, "Пожалуйста, отправьте числовое значение ниже 100.") elif chat_id in shellexecution: bot.sendChatAction(chat_id, 'typing') @@ -151,14 +169,12 @@ def on_chat_message(self, msg): if output != b'': bot.sendMessage(chat_id, output, disable_web_page_preview=True) else: - bot.sendMessage(chat_id, "No output.", disable_web_page_preview=True) + bot.sendMessage(chat_id, "Упс.", disable_web_page_preview=True) elif msg['text'] == '/memgraph': - bot.sendChatAction(chat_id, 'typing') - tmperiod = "Last %.2f hours" % ((datetime.now() - graphstart).total_seconds() / 3600) + bot.sendChatAction(chat_id, 'upload_photo') + tmperiod = "За %.2f часа" % ((datetime.now() - graphstart).total_seconds() / 3600) bot.sendPhoto(chat_id, plotmemgraph(memlist, xaxis, tmperiod)) - - TOKEN = telegrambot bot = YourBot(TOKEN) @@ -184,11 +200,11 @@ def on_chat_message(self, msg): memlist.append(mempercent) memfree = memck.available / 1000000 if mempercent > memorythreshold: - memavail = "Available memory: %.2f GB" % (memck.available / 1000000000) + memavail = "Доступно ОЗУ: %.2f GB" % (memck.available / 1000000000) graphend = datetime.now() - tmperiod = "Last %.2f hours" % ((graphend - graphstart).total_seconds() / 3600) + tmperiod = "За последние %.2f часов" % ((graphend - graphstart).total_seconds() / 3600) for adminid in adminchatid: - bot.sendMessage(adminid, "CRITICAL! LOW MEMORY!\n" + memavail) + bot.sendMessage(adminid, "ВНИМАНИЕ! МАЛО ОПЕРАТИВНОЙ ПАМЯТИ!\n" + memavail) bot.sendPhoto(adminid, plotmemgraph(memlist, xaxis, tmperiod)) time.sleep(10) # 10 seconds tr += 10 diff --git a/tokens.py_example b/tokens.py_example index c97a832..da53b3a 100644 --- a/tokens.py_example +++ b/tokens.py_example @@ -1,5 +1,4 @@ -# A token you get from the Telegram's botfather +# Токен бота telegrambot = '9999999:6666aaaaaa666666a6aaa' - -# A chat_id of your client +# Введите chat_id канала/группы/пользователя. Можно использовать несколько значений, подробнее в readme.md adminchatid = [99999999] \ No newline at end of file