├── .gitignore ├── tokens.py_example ├── servstatsbot.conf ├── README.md └── servstatsbot.py /.gitignore: -------------------------------------------------------------------------------- 1 | #tokens.py 2 | graph.png 3 | -------------------------------------------------------------------------------- /tokens.py_example: -------------------------------------------------------------------------------- 1 | # A token you get from the Telegram's botfather 2 | # 这边填入从@botfather 处取得的token 3 | telegrambot = '9999999:6666aaaaaa666666a6aaa' 4 | 5 | # A chat_id of your client 6 | # 这边填入管理员的chat_id 7 | adminchatid = [99999999] -------------------------------------------------------------------------------- /servstatsbot.conf: -------------------------------------------------------------------------------- 1 | start on runlevel [2345] 2 | stop on runlevel [016] 3 | 4 | respawn 5 | # Change PATH_TO_PY_FILE with the path to the bots py file 6 | # 将 PATH_TO_PY_FILE 改为servstatsbot.py文件路径 7 | # Place this file in /etc/init/ 8 | # 把这个文件放在/etc/init/ 里 9 | # And it will be running as "daemon". You can start|stop|restart like this: start servstatsbot 10 | # 之后bot就会以进程方式运行,以下命令有效:start|stop|restart servstatsbot 11 | exec python3 /PATH_TO_PY_FILE/servstatsbot.py > /dev/null 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Server Manager Bot 2 | 3 | 原项目详见fork源,您正在看的这个repo某种意义上属于二开版,Working in progress之后是原readme的完整翻译,也可以当做部署教程,实际上,这个repo只是比原repo维护+新功能+汉化。 4 | 5 | 后续更新请看[这里](https://github.com/iamydp/ServerMonitorBot),此版本不做后续维护 6 | 7 | 2020-3-31新增 8 | 9 | 输入/start或/help或help时回复所有可用的命令。(虽然在向@botfather添加命令过后这个显得有点多余) 10 | 11 | Known Issue 12 | * 机器人新上线一段时间内绘制图标x轴为负数,过一段时间后恢复正常。 13 | * 设置新阈值或探测间隔,收到新消息后不会自动stop。 14 | 15 | Working in Progress 16 | * 开机以来所用流量功能 17 | * 当前网速 18 | * 磁盘使用详细情况 19 | * 重启/掉进程后上线提示 20 | 21 | 欢迎与我或原作者(找不到人了)发邮件探讨/提issue! 22 | 23 | 功能: 24 | * 命令 25 | * `/stats` - 检查磁盘/CPU/内存使用情况(正在完善中) 26 | * `/shell` - 进入执行命令模式并返回结果 27 | * `/memgraph` - 绘制近一段时间的内存使用记录表 28 | * `/setmem` - 设置内存占用告警阈值,并在占用情况高于这个值时告警 29 | * `/setpoll` - 设置探测间隔(不少于10秒) 30 | * 检测内存使用情况并且在高于设定阈值时通过telegram发送告警消息 31 | 32 | 33 | `/stats`命令演示: [Gif](http://i.imgur.com/AhCvy9W.gifv) 34 | 35 | ![Bot](http://i.imgur.com/hXT0drx.png) 36 | 37 | 38 | `/shell`命令演示: 39 | 40 | ![Shell](https://i.imgur.com/PtvcaSD.png) 41 | 42 | 43 | `/memgraph`命令演示: [Gif](http://i.imgur.com/anX7rJR.gifv) 44 | 45 | ![Graph](http://i.imgur.com/K8mG3aM.jpg?1) 46 | 47 | # 使用方法 48 | 49 | ## 所需环境 50 | 51 | * Python 3+ 52 | * [Telepot](https://github.com/nickoala/telepot) 53 | * [Psutil](https://github.com/giampaolo/psutil) 54 | * 确保Psutil是为Python3工作,而非2 55 | * 确保`pip`是最新版本: 56 | * `curl -O https://bootstrap.pypa.io/get-pip.py` 57 | * `sudo python3 get-pip.py` 58 | * 之后使用`pip install psutil` 59 | * 在Stackoverflow上也有相关问题的回答[链接](http://stackoverflow.com/questions/11268501/how-to-use-pip-with-python-3-x-alongside-python-2-x) 60 | * [matplotlib](http://matplotlib.org/) 61 | * `sudo apt-get install python3-matplotlib` 62 | * Bot key & `tokens.py` 63 | * 把token放在`tokens.py`里. `tokens.py`只用来保存您的chat_id和bot token.不要随便定义/更改函数名. 64 | * 从@Bot Father新建机器人/获取token[Bot Father](https://telegram.me/BotFather) 65 | * 克隆该仓库 66 | * 在本地文件夹内新建文件`tokens.py` 67 | * 已经加入`.gitignore`豪华套餐,所以谁的key/token都不会提交上来 68 | * 将`telegrambot`后字符串换成自己的token 69 | * 如: `telegrambot = "000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 70 | * 编者按:原readme中的确是这样写的,但作者更新的时候可能忘了改了,这里直接vim编辑tokens.py_example再mv重命名去掉_example就可以了。 71 | 72 | ## 开始运行 73 | 74 | `python3 servstatsbot.py` 75 | 76 | ## 将bot作为进程运行 77 | 78 | * 参见文件: `servstatsbot.conf` 79 | * 根据文件内注释指示更改路径 80 | * 将`servstatsbot.conf`放在`/etc/init/`内 81 | * 用该命令启动进程: `start servstatsbot` 82 | * 之后,以下命令将可用:`start|stop|restart servstatsbot` 83 | * 如果进程崩溃将会自动重启 84 | * 开机启动 85 | 86 | ## 设定一个管理员 87 | 88 | 在`tokens.py`中设定`adminchatid`变量为chat_id(如果您的工具人是个公交车,参照如下方法). 89 | * `adminchatid = [443355]` 90 | * `adminchatid = [443355, 55667788, 99884433]` 91 | 92 | 之后我将会重新编写这块的代码 93 | 94 | 译者按:这句话是原作者写的,我没说过 95 | 96 | # 欢迎一键三连 XD 97 | 我在十分钟之内拼凑出这个代码,之后又花了一点时间使之成.我觉得这个bot是个很好的想法,并且阅读这段话当中的某些人会非常喜欢这个repo.所以请随便fork,pull,提交新功能! 98 | 99 | 可以给你contributors access哦!笔芯 100 | 101 | 真心希望看到这个项目能够快速发展起来:) 102 | 103 | 译者按:这些话也是作者说的,可是他已经4年没有更新了,会不会... 104 | 105 | 106 | 107 | # 其他的bot项目 108 | 109 | ## Alfred 110 | [http://alfredthebot.com](http://alfredthebot.com) 111 | 112 | 译者按:已404 113 | 114 | 115 | GB 116 | -------------------------------------------------------------------------------- /servstatsbot.py: -------------------------------------------------------------------------------- 1 | from tokens import * 2 | import matplotlib 3 | matplotlib.use("Agg") # has to be before any other matplotlibs imports to set a "headless" backend 4 | import matplotlib.pyplot as plt 5 | import psutil 6 | from datetime import datetime 7 | from subprocess import Popen, PIPE, STDOUT 8 | import operator 9 | import collections 10 | # import sys 11 | import time 12 | # import threading 13 | # import random 14 | import telepot 15 | # from telepot.namedtuple import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardHide, ForceReply 16 | # from telepot.namedtuple import InlineKeyboardMarkup, InlineKeyboardButton 17 | # from telepot.namedtuple import InlineQueryResultArticle, InlineQueryResultPhoto, InputTextMessageContent 18 | 19 | 20 | 21 | memorythreshold = 85 # If memory usage more this % 22 | poll = 300 # seconds 23 | 24 | shellexecution = [] 25 | timelist = [] 26 | memlist = [] 27 | xaxis = [] 28 | settingmemth = [] 29 | setpolling = [] 30 | graphstart = datetime.now() 31 | 32 | stopmarkup = {'keyboard': [['Stop']]} 33 | hide_keyboard = {'hide_keyboard': True} 34 | 35 | def clearall(chat_id): 36 | if chat_id in shellexecution: 37 | shellexecution.remove(chat_id) 38 | if chat_id in settingmemth: 39 | settingmemth.remove(chat_id) 40 | if chat_id in setpolling: 41 | setpolling.remove(chat_id) 42 | 43 | def plotmemgraph(memlist, xaxis, tmperiod): 44 | # print(memlist) 45 | # print(xaxis) 46 | plt.xlabel(tmperiod) 47 | plt.ylabel('% Used') 48 | plt.title('Memory Usage Graph') 49 | plt.text(0.1*len(xaxis), memorythreshold+2, 'Threshold: '+str(memorythreshold)+ ' %') 50 | memthresholdarr = [] 51 | for xas in xaxis: 52 | memthresholdarr.append(memorythreshold) 53 | plt.plot(xaxis, memlist, 'b-', xaxis, memthresholdarr, 'r--') 54 | plt.axis([0, len(xaxis)-1, 0, 100]) 55 | plt.savefig('/tmp/graph.png') 56 | plt.close() 57 | f = open('/tmp/graph.png', 'rb') # some file on local disk 58 | return f 59 | 60 | 61 | class YourBot(telepot.Bot): 62 | def __init__(self, *args, **kwargs): 63 | super(YourBot, self).__init__(*args, **kwargs) 64 | self._answerer = telepot.helper.Answerer(self) 65 | self._message_with_inline_keyboard = None 66 | 67 | def on_chat_message(self, msg): 68 | content_type, chat_type, chat_id = telepot.glance(msg) 69 | # Do your stuff according to `content_type` ... 70 | print("Your chat_id:" + str(chat_id)) # this will tell you your chat_id 71 | if chat_id in adminchatid: # Store adminchatid variable in tokens.py 72 | if content_type == 'text': 73 | if msg['text'] == '/stats' and chat_id not in shellexecution: 74 | bot.sendChatAction(chat_id, 'typing') 75 | memory = psutil.virtual_memory() 76 | disk = psutil.disk_usage('/') 77 | boottime = datetime.fromtimestamp(psutil.boot_time()) 78 | now = datetime.now() 79 | timedif = "在线时间: %.1f 小时" % (((now - boottime).total_seconds()) / 3600) 80 | memtotal = "总内存: %.2f GB " % (memory.total / 1000000000) 81 | memavail = "可用内存: %.2f GB" % (memory.available / 1000000000) 82 | memuseperc = "使用内存: " + str(memory.percent) + " %" 83 | diskused = "磁盘占用: " + str(disk.percent) + " %" 84 | pids = psutil.pids() 85 | pidsreply = '' 86 | procs = {} 87 | for pid in pids: 88 | p = psutil.Process(pid) 89 | try: 90 | pmem = p.memory_percent() 91 | if pmem > 0.5: 92 | if p.name() in procs: 93 | procs[p.name()] += pmem 94 | else: 95 | procs[p.name()] = pmem 96 | except: 97 | print("Hm") 98 | sortedprocs = sorted(procs.items(), key=operator.itemgetter(1), reverse=True) 99 | for proc in sortedprocs: 100 | pidsreply += proc[0] + " " + ("%.2f" % proc[1]) + " %\n" 101 | reply = timedif + "\n" + \ 102 | memtotal + "\n" + \ 103 | memavail + "\n" + \ 104 | memuseperc + "\n" + \ 105 | diskused + "\n\n" + \ 106 | pidsreply 107 | bot.sendMessage(chat_id, reply, disable_web_page_preview=True) 108 | elif msg['text'] == "help" or msg['text'] == "/help" or msg['text'] == "/start": 109 | bot.sendMessage(chat_id, "以下命令可用") 110 | bot.sendMessage(chat_id, "/stats -检查磁盘/CPU/内存使用情况") 111 | bot.sendMessage(chat_id, "/shell -字面意思") 112 | bot.sendMessage(chat_id, "/memgraph -绘制近一段时间的内存使用记录表") 113 | bot.sendMessage(chat_id, "/setmem -设置内存占用告警阈值,并在占用情况高于这个值是告警") 114 | bot.sendMessage(chat_id, "/setpoll -设置探测间隔(不少于10秒)") 115 | bot.sendMessage(chat_id, "/stop -AZ5") 116 | elif msg['text'] == "Stop": 117 | clearall(chat_id) 118 | bot.sendMessage(chat_id, "所有操作已经停止了", reply_markup=hide_keyboard) 119 | elif msg['text'] == '/setpoll' and chat_id not in setpolling: 120 | bot.sendChatAction(chat_id, 'typing') 121 | setpolling.append(chat_id) 122 | bot.sendMessage(chat_id, "发给我一个新的探测间隔(不少于10秒)", reply_markup=stopmarkup) 123 | elif chat_id in setpolling: 124 | bot.sendChatAction(chat_id, 'typing') 125 | try: 126 | global poll 127 | poll = int(msg['text']) 128 | if poll > 10: 129 | bot.sendMessage(chat_id, "整好了!") 130 | clearall(chat_id) 131 | else: 132 | 1/0 133 | except: 134 | bot.sendMessage(chat_id, "需要大于等于10秒,再来一次") 135 | elif msg['text'] == "/shell" and chat_id not in shellexecution: 136 | bot.sendMessage(chat_id, "发给我一条命令以执行", reply_markup=stopmarkup) 137 | shellexecution.append(chat_id) 138 | elif msg['text'] == "/setmem" and chat_id not in settingmemth: 139 | bot.sendChatAction(chat_id, 'typing') 140 | settingmemth.append(chat_id) 141 | bot.sendMessage(chat_id, "发给我一个新的内存占用告警阈值?", reply_markup=stopmarkup) 142 | elif chat_id in settingmemth: 143 | bot.sendChatAction(chat_id, 'typing') 144 | try: 145 | global memorythreshold 146 | memorythreshold = int(msg['text']) 147 | if memorythreshold < 100: 148 | bot.sendMessage(chat_id, "整好了!") 149 | clearall(chat_id) 150 | else: 151 | 1/0 152 | except: 153 | bot.sendMessage(chat_id, "都说了要小于100啊") 154 | 155 | elif chat_id in shellexecution: 156 | bot.sendChatAction(chat_id, 'typing') 157 | p = Popen(msg['text'], shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) 158 | output = p.stdout.read() 159 | if output != b'': 160 | bot.sendMessage(chat_id, output, disable_web_page_preview=True) 161 | else: 162 | bot.sendMessage(chat_id, "没有输出", disable_web_page_preview=True) 163 | elif msg['text'] == '/memgraph': 164 | bot.sendChatAction(chat_id, 'typing') 165 | tmperiod = "Last %.2f hours" % ((datetime.now() - graphstart).total_seconds() / 3600) 166 | bot.sendPhoto(chat_id, plotmemgraph(memlist, xaxis, tmperiod)) 167 | 168 | 169 | 170 | TOKEN = telegrambot 171 | 172 | bot = YourBot(TOKEN) 173 | bot.message_loop() 174 | tr = 0 175 | xx = 0 176 | # Keep the program running. 177 | while 1: 178 | if tr == poll: 179 | tr = 0 180 | timenow = datetime.now() 181 | memck = psutil.virtual_memory() 182 | mempercent = memck.percent 183 | if len(memlist) > 300: 184 | memq = collections.deque(memlist) 185 | memq.append(mempercent) 186 | memq.popleft() 187 | memlist = memq 188 | memlist = list(memlist) 189 | else: 190 | xaxis.append(xx) 191 | xx += 1 192 | memlist.append(mempercent) 193 | memfree = memck.available / 1000000 194 | if mempercent > memorythreshold: 195 | memavail = "可用内存: %.2f GB" % (memck.available / 1000000000) 196 | graphend = datetime.now() 197 | tmperiod = "近 %.2f 小时" % ((graphend - graphstart).total_seconds() / 3600) 198 | for adminid in adminchatid: 199 | bot.sendMessage(adminid, "换个大内存服务器吧\n" + memavail) 200 | bot.sendPhoto(adminid, plotmemgraph(memlist, xaxis, tmperiod)) 201 | time.sleep(10) # 10 seconds 202 | tr += 10 203 | --------------------------------------------------------------------------------