diff --git a/.authorized_users b/.authorized_users new file mode 100644 index 0000000..dbefc0c --- /dev/null +++ b/.authorized_users @@ -0,0 +1,2 @@ +AUTHORIZED_USER_ID_1 +AUTHORIZED_USER_ID_2 \ No newline at end of file diff --git a/lainmonitor.py b/lainmonitor.py index f053e0d..2550725 100644 --- a/lainmonitor.py +++ b/lainmonitor.py @@ -5,20 +5,27 @@ import subprocess import threading from time import sleep -import telebot +from telebot import * + #define the variables -status, hostname, uptime, zerotier, prosody, postgres, tailscale, disk, ping = 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown' +status, hostname, uptime, zerotier, prosody, postgres, tailscale, nginx, disk, ping = 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown', 'unknown' nodes, hostnames, reach, threads = [], [], [], [] -#change this to your hostname -host = 'lainlounge' +#change this to your instance's hostname +host = subprocess.check_output(['hostname']).decode().strip() +#print ('host:', host) # debug #load the token token = open('.env', 'r').read().strip() +#load the authorized users +authorized_users = [line.strip() for line in open('.authorized_users', 'r').readlines()] +#print('authorized users:', authorized_users) # debug + #bot init bot = telebot.TeleBot(token) +updater = bot.update_listener #get system info def getinfo(): @@ -30,12 +37,13 @@ def getinfo(): prosody = subprocess.Popen("sudo systemctl status prosody | grep 'Active'", shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() postgres = subprocess.Popen("sudo systemctl status postgresql | grep 'Active'", shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() tailscale = subprocess.Popen("sudo systemctl status tailscaled | grep 'Active'", shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() + nginx = subprocess.Popen("sudo systemctl status nginx | grep 'Active'", shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() disk = subprocess.check_output(['df', '-h']).decode().strip() if hostname == 'unknown': status = 'offline' else: status = 'online' - return hostname, uptime, zerotier, prosody, postgres, tailscale, disk + return hostname, uptime, zerotier, prosody, postgres, tailscale, nginx, disk #function to ping tailscale nodes def ping_node(node, hostname): @@ -64,27 +72,43 @@ def check_tailscale(): #restart services def restart_service(service): + print(f'restarting {service}...') subprocess.Popen(f'sudo systemctl restart {service}', shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() sleep(3) service_status = subprocess.Popen(f'sudo systemctl status {service} | grep "Active"', shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() - message = f'{service} restarted! status: {service_status}' + status_message = f'{service} restarted! status: {service_status}' + return status_message #restart services menu def restart_menu(): - keyboard = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True) - zerotier_button = telebot.types.KeyboardButton('zerotier-one', callback_data='zerotier') - prosody_button = telebot.types.KeyboardButton('prosody', callback_data='prosody') - postgres_button = telebot.types.KeyboardButton('postgresql', callback_data='postgresql') - tailscale_button = telebot.types.KeyboardButton('tailscaled', callback_data='tailscaled') + keyboard = [ + [telebot.types.InlineKeyboardButton('zerotier-one', callback_data='zerotier-one')], + [telebot.types.InlineKeyboardButton('prosody', callback_data='prosody')], + [telebot.types.InlineKeyboardButton('postgresql', callback_data='postgresql')], + [telebot.types.InlineKeyboardButton('tailscaled', callback_data='tailscaled')], + [telebot.types.InlineKeyboardButton('nginx', callback_data='nginx')], + [telebot.types.InlineKeyboardButton('cancel', callback_data='cancel')] + ] + reply_markup = telebot.types.InlineKeyboardMarkup(keyboard) + return reply_markup - return keyboard +#callback query handler +@bot.callback_query_handler(func=lambda call: True) +def callback_query(call): + service = call.data + if service != 'cancel': + status_message = restart_service(service) + bot.send_message(call.message.chat.id, status_message) + else: + bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None) + bot.send_message(call.message.chat.id, 'canceled') def reboot(): subprocess.Popen('sudo reboot', shell=True, stdout=subprocess.PIPE).stdout.read().decode().strip() #debug handler def check(): - global status, hostname, uptime, zerotier, prosody, postgres, tailscale, disk + global status, hostname, uptime, zerotier, prosody, postgres, tailscale, nginx, disk getinfo() print('system status:', status) print('hostname:', hostname) @@ -94,7 +118,7 @@ def check(): print('postgres:', postgres) print('tailscale:', tailscale) print('disk:', disk) - return status, hostname, uptime, zerotier, prosody, postgres, tailscale, disk + return status, hostname, uptime, zerotier, prosody, postgres, tailscale, nginx, disk #message handling @bot.message_handler(commands=['start', 'help', 'status', 'restart', 'reboot', 'ping']) @@ -102,26 +126,32 @@ def handle(message): if message.text == '/start': bot.reply_to(message, 'lainmonitor v1.0 --- standing by...') elif message.text == '/help': - bot.reply_to(message, 'commands: /start, /help, /status, /reboot, /ping') + bot.reply_to(message, 'commands: /start, /help, /status, /restart, /reboot, /ping') elif message.text == '/status': check() - status_message = f'hostname: {hostname}\nsystem status: {status}\nuptime: {uptime}\nzerotier: {zerotier}\nprosody: {prosody}\npostgres: {postgres}\ntailscale: {tailscale}' + status_message = f'hostname: {hostname}\nsystem status: {status}\nuptime: {uptime}\nzerotier: {zerotier}\nprosody: {prosody}\npostgres: {postgres}\ntailscale: {tailscale}\nnginx: {nginx}' bot.reply_to(message, status_message) bot.reply_to(message, f'filesystem info for {hostname}: \n\n{disk}') elif message.text == f'/restart {host}': - if host == hostname: + if message.text == f'/restart {host}' and str(message.from_user.id) in authorized_users: bot.send_message(message.chat.id, 'select a service to restart:', reply_markup=restart_menu()) else: - pass - elif message.text == f'/reboot {hostname}': - bot.reply_to(message, f'rebooting {host}...') - reboot() + bot.reply_to(message, 'you are not authorized to restart services on this host') + elif message.text == f'/reboot {host}': + if message.text == f'/reboot {host}' and str(message.from_user.id) in authorized_users: + bot.reply_to(message, f'rebooting {host}...') + reboot() + else: + bot.reply_to(message, 'you are not authorized to reboot this host') elif message.text == '/ping': - check_tailscale() - ping_status = '\n'.join(reach) - bot.reply_to(message, f'ping status:\n\n{ping_status}') - ping_status = '' - reach.clear() + if message.text == f'/restart {host}' and str(message.from_user.id) in authorized_users: + check_tailscale() + ping_status = '\n'.join(reach) + bot.reply_to(message, f'ping status:\n\n{ping_status}') + ping_status = '' + reach.clear() + else: + bot.reply_to(message, 'you are not authorized to view ping status') #polling bot.polling() \ No newline at end of file