diff --git a/bot_config.yaml.example b/bot_config.yaml.example index 105d6cd..af7fa4e 100644 --- a/bot_config.yaml.example +++ b/bot_config.yaml.example @@ -1,4 +1,5 @@ botname: 'Bob' +tmpdir: '/tmp/.skyfall/egs-bot' token: '' telnet: host: '' diff --git a/colors.rb b/colors.rb new file mode 100644 index 0000000..b4efdb2 --- /dev/null +++ b/colors.rb @@ -0,0 +1,28 @@ +class String +def black; "\e[30m#{self}\e[0m" end +def red; "\e[31m#{self}\e[0m" end +def green; "\e[32m#{self}\e[0m" end +def yellow; "\e[33m#{self}\e[0m" end +def brown; "\e[33m#{self}\e[0m" end +def blue; "\e[34m#{self}\e[0m" end +def magenta; "\e[35m#{self}\e[0m" end +def cyan; "\e[36m#{self}\e[0m" end +def gray; "\e[37m#{self}\e[0m" end + +def bg_black; "\e[40m#{self}\e[0m" end +def bg_red; "\e[41m#{self}\e[0m" end +def bg_green; "\e[42m#{self}\e[0m" end +def bg_yellow; "\e[43m#{self}\e[0m" end +def bg_brown; "\e[43m#{self}\e[0m" end +def bg_blue; "\e[44m#{self}\e[0m" end +def bg_magenta; "\e[45m#{self}\e[0m" end +def bg_cyan; "\e[46m#{self}\e[0m" end +def bg_gray; "\e[47m#{self}\e[0m" end + +def bold; "\e[1m#{self}\e[22m" end +def italic; "\e[3m#{self}\e[23m" end +def underline; "\e[4m#{self}\e[24m" end +def blink; "\e[5m#{self}\e[25m" end +def reverse_color; "\e[7m#{self}\e[27m" end +end + diff --git a/commands.rb b/commands.rb index a2f54a9..073a587 100644 --- a/commands.rb +++ b/commands.rb @@ -13,26 +13,26 @@ def message_from_admin?(message, adm) #puts message.from.id #puts adm if adm.include?(message.from.id) - puts "Command is from an admin. [#{message.from.username}]" + puts "Command is from an admin. [" + "#{message.from.username}".green.bold + "]" return true else - puts "Command is NOT from an admin! [#{message.from.username}]" + puts "Command is NOT from an admin! [" + "#{message.from.username}".yellow.bold + "]" return false end end def is_chat_authorized?(message, auth_chat) if auth_chat.include?(message.chat.id) - puts "Group [#{message.chat.id}][#{message.chat.title}] is authorized" + puts "Group [" + "#{message.chat.id}".green.bold + "][" + "#{message.chat.title}".green + "] is authorized" return true else - puts "Group [#{message.chat.id}][#{message.chat.title}] is NOT authorized!" + puts "Group [" + "#{message.chat.id}".red.bold + "][" + "#{message.chat.title}".red + "] is NOT authorized!" return false end end def process_command_srvstart(message, command, adm) - puts "Received command: srvstart" + #puts "Received command: " + "srvstart".green #pp message from_admin = message_from_admin?(message, adm) auth_grp = is_chat_authorized?(message, @auth_chat) @@ -52,7 +52,7 @@ def process_command_srvstart(message, command, adm) end def process_command_srvstop(message, command, adm) - puts "Received command: srvstop" + #puts "Received command: " + "srvstop".red from_admin = message_from_admin?(message, adm) auth_grp = is_chat_authorized?(message, @auth_chat) if ! auth_grp && ! from_admin @@ -79,11 +79,11 @@ def process_command_srvstatus(message, command, adm) if `./scripts/srvstatus.receipt #{@conf['token']} #{message.chat.id}` print "Receipt sent!\nFetching details...\n" else - print "Receipt FAILED to send (non-zero exit code on scripts/srvstatus.receipt) !!!\nContinuing to fetch details anyway...\n" + print "Receipt FAILED to send (non-zero exit code on scripts/srvstatus.receipt) !!!\nContinuing to fetch details anyway...\n".red.bold end - reply = `./scripts/srvstatus #{telnet['host']} #{telnet['port']} #{telnet['pass']}` + reply = `./scripts/srvstatus #{telnet['host']} #{telnet['port']} #{telnet['pass']} #{@tmpdir}` else - print "Server did not respond!\nSending failure message...\n" + print "Server did not respond!\nSending failure message...\n".red.bold reply = "Server is not responding!" end STDOUT.flush diff --git a/patchnotes.txt b/patchnotes.txt index 3ad3896..372beeb 100644 --- a/patchnotes.txt +++ b/patchnotes.txt @@ -1,16 +1,26 @@ SkyfallTech EGS Telebot Patch Notes: +v0.4.00 ++ Better server logs, round 3. + * Vastly improved sanity check + * Timestamps, colours, user IDs + * Better STDOUT synchronization ++ Code efficiency improvements. ++ Fixed a few typos in responses. ++ Fixed possible "binary file" bug for /status command. ++ Added configurable tmp directory. + v0.3.03 + Fixed 'server is up oops nevermind' condition in /status. + Fixed /whoami output formatting. + Fixed /whereareyou command. ---- (previously relied on a now-broken Contegix service) + * (previously relied on a now-broken Contegix service) v0.3.02 + Better server logs, round 2. + Less ambiguous patchnotes listing ---- (These are for the bot, not for Empyrion) + * (These are for the bot, not for Empyrion) v0.3.01 + Better server logs, round 1. diff --git a/run.rb b/run.rb index 8148660..bd831d5 100755 --- a/run.rb +++ b/run.rb @@ -4,54 +4,127 @@ require 'rubygems' require 'yaml' require 'telegram/bot' require 'pp' +require 'time' require_relative 'commands.rb' +require_relative 'colors.rb' + +def timestamp + Time.now.strftime("%F %H:%M:%S").yellow +end #conf = YAML.load(File.read("bot_config.yaml")) @conf = YAML.load_file("bot_config.yaml") @botname = @conf['botname'] +@tmpdir = @conf['tmpdir'] token = @conf['token'] telnet = @conf['telnet'] admin = @conf['admin'] @auth_chat = @conf['authorized_chats'] @allowed_sources = @conf['allowed_sources'] -# Sanity check +### Begin sanity check ### +STDOUT.sync = true errcount = 0 puts "Checking if environment is sane...\n\n" +print "Checking bot token ...................... " if token.nil? - puts "No bot token defined in bot_config.yaml!\nTHIS IS REQUIRED! Bot initialization failed; exiting..." + print "FAIL!\n\n".red.bold + puts "No bot token defined in bot_config.yaml!\n" + "THIS IS REQUIRED!".red.bold + " Bot initialization failed; exiting..." exit(1) +else + print "OK\n".green.bold end +print "Checking configured bot name ............ " if @botname.nil? errcount += 1 + print "FAIL!\n\n".red.bold puts "Error(#{errcount.to_s}): No bot name defined. This is superficial. We'll call him Bob.\n\n" @botname = "Bob" +else + print "OK\n".green.bold end +### Temporary directory check +print "Checking configured tmp directory ....... " +def is_tmp_writable? + system("mkdir -p #{@tmpdir} >/dev/null 2>&1") + if system("touch #{@tmpdir}/test.file >/dev/null 2>&1") + system("rm #{@tmpdir}/test.file >/dev/null 2>&1") + return true + else + system("rm #{@tmpdir}/test.file >/dev/null 2>&1") #Attempt to clean up anyway + return false + end +end + +if @tmpdir.nil? + errcount += 1 + print "FAIL!\n\n".red.bold + puts "Error(#{errcount.to_s}): No temporary directory defined. Using '/tmp/.skyfall/egs-bot'.\n" + @tmpdir = "/tmp/.skyfall/egs-bot" + if is_tmp_writable? + puts "Default temporary directory is writable. Continuing...\n\n" + else + errcount += 1 + puts "Error(#{errcount.to_s}): Temporary directory [" + @tmpdir.red.bold + "] is not writable!\n" + "THIS IS REQUIRED!".red.bold + " Bot initialization failed; exiting..." + exit(1) + end +else + if is_tmp_writable? + print "OK\n".green.bold + else + errcount += 1 + print "FAIL!\n\n".red.bold + puts "Error(#{errcount.to_s}): Temporary directory [" + @tmpdir.red.bold + "] is not writable!\n" + "THIS IS REQUIRED!".red.bold + " Bot initialization failed; exiting..." + exit(1) + end +end +### End tmpdir check +print "Checking telnet configuration ........... " if telnet.nil? errcount += 1 + print "FAIL!\n\n".red.bold puts "Error(#{errcount.to_s}): No telnet information provided in bot_config.yaml.\nThis is required for nearly all Empyrion-related " + "functions.\nTHIS SHOULD BE ADDRESSED. Continuing. (some commands will return broken messages)\n\n" +else + print "OK\n".green.bold end +print "Checking administrators ................. " if admin.nil? errcount += 1 + print "FAIL!\n\n".red.bold puts "Error(#{errcount.to_s}): No admin Telegram IDs provided in bot_config.yaml.\nThis is required for many functions.\n" + "THIS SHOULD BE ADDRESSED. Continuing. (some commands will not be available)\n\n" admin = ["0"] +else + print "OK\n".green.bold end +print "Checking authorized chats ............... " if @auth_chat.nil? errcount += 1 + print "FAIL!\n\n".red.bold puts "Error(#{errcount.to_s}): No authorized Telegram group IDs provided in bot_config.yaml.\nThis is required for most Empyrion-related " + "functions.\nTHIS SHOULD BE ADDRESSED. Continuing. (some commands will not be available)\n\n" @auth_chat = ["0"] +else + print "OK\n".green.bold end puts "Errors found: #{errcount.to_s}\n\n" - +if errcount > 0 + print "Environment is grinning and holding a spatula. Please review your configuration.\n\n".red.bold +else + print "Environment appears sane.\n\n".green.bold +end +STDOUT.sync = false +### End sanity check ### puts "Starting [#{@botname}]...\n\n" puts "Empyrion Host: #{telnet['host']}" puts "Empyrion Telnet Port: #{telnet['port']}" puts "Authorized administrator IDs: #{admin}" puts "Authorized chat IDs: #{@auth_chat}" -puts "Bot token: #{token}\n\n\n\n" +puts "Bot token: #{token}" +puts "Temporary direcotry: #{@tmpdir}" +puts "Start time: " + timestamp + "\n\n\n\n" +STDOUT.flush #def process_command_srvstart(message) # puts "Received command: srvstart" @@ -96,20 +169,27 @@ def handle_message(message) #conf = YAML.load_file("bot_config.yaml") telnet = @conf['telnet'] adm = @conf['admin'] - puts "Received command from #{message.from.username}: #{command}" + puts Time.now.strftime("%F %H:%M:%S").yellow + ": Received command from " + "#{message.from.username}".cyan.bold + " [" + "#{message.from.id}".cyan + "]: " + "#{command}".magenta.bold case command when '/start' reply = "Introduction: I am #{@botname}, and I am here to make life easier for meatbag admins like you. Currently I can retrieve information " + "from an Empyrion server as well as kill the server process. I also have some pretty sweet dance moves.\n\n" + "Commands available:\n/start (Shows this message)\n/srvstart (Currently non-functional)\n/srvstop\n/status\n/whoami or /chatinfo\n/whereareyou or /location\n\n" + - "Check again later to see if any new functions have been added, or use /patchnotes to learn about recent updates." - if ! message_from_admin?(message, adm) && ! is_chat_authorized?(message, @auth_chat) + "Check again later to see if any new functions have been added, or use /patchnotes to learn about recent updates.\n" + + "You can also view the source code at the following location:\nhttps://git.skyfall.tech/skyfall/empyrion-bot" + if message_from_admin?(message, adm) + msg_from_admin = true + end + if is_chat_authorized?(message, @auth_chat) + chat_authorized = true + end + if ! msg_from_admin && ! chat_authorized if message.from.id == message.chat.id - reply = reply + "\n\nWARNING: I am not authorized to work you directly. My functionality is limited." + reply = reply + "\n\nWARNING: I am not authorized to work with you directly. My functionality is limited." else reply = reply + "\n\nWARNING: I am not authorized to participate with this group. My functionality is limited." end - elsif message_from_admin?(message, adm) && ! is_chat_authorized?(message, @auth_chat) + elsif msg_from_admin && ! chat_authorized reply = reply + "\n\nWARNING: Although you are my master, I have not been authorized to participate in this group. My functionality is limited." end when '/srvstart' @@ -118,11 +198,6 @@ def handle_message(message) reply = process_command_srvstop(message, command, adm) when '/status' reply = process_command_srvstatus(message, command, adm) - #if is_chat_authorized?(message, @auth_chat) || message_from_admin?(message, adm) - # reply = `./scripts/srvstatus #{telnet['host']} #{telnet['port']} #{telnet['pass']}` - #else - # reply = "Refusal: I am not authorized to provide this information here." - #end when '/patch', '/patchnotes' reply = process_command_patchnotes(message, command, adm) when '/location', '/whereareyou' @@ -156,7 +231,8 @@ def handle_message(message) reply = "Mockery: My name is #{message.from.username}, I am a meatbag, and I think #{command} is a valid command." end # Verbose output: - puts "Sending #{reply.inspect}\n\n" + puts timestamp + ": Sending #{reply.inspect}\n\n" + STDOUT.flush #puts "End of case" return reply end diff --git a/scripts/srvstatus b/scripts/srvstatus index b0fe0bf..3a3f57c 100755 --- a/scripts/srvstatus +++ b/scripts/srvstatus @@ -3,16 +3,17 @@ host=$1 port=$2 pass=$3 +tmpfile=$4/.egsbot-stat if nc -zvw3 $host $port 2>/dev/null; then - ./scripts/srvstatus.expect $host $port $pass > /tmp/.stat - grep -B1 -A8 Empyrion\ dedicated\ server /tmp/.stat | egrep -v Playfield\|Game\ seed + ./scripts/srvstatus.expect $host $port $pass > $tmpfile + grep -a -B1 -A8 Empyrion\ dedicated\ server $tmpfile | egrep -v Playfield\|Game\ seed printf "\nOnline players:\n- - - - - - - - - - - - -" - sed -n '/Global\ online/,/Global\ players/p' /tmp/.stat | awk -F\= '{print $3}' | sed 's/....$//' - rm /tmp/.stat + sed -n '/Global\ online/,/Global\ players/p' $tmpfile | awk -F\= '{print $3}' | sed 's/....$//' + rm $tmpfile exit 0 else - printf "Server is DOWN!" + printf "Server is not responding to telnet requests!" exit 1 fi