diff --git a/.gitignore b/.gitignore index 1f2fa27..db03e2c 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,7 @@ auth.json .vscode/launch.json .vscode/settings.json .vscode/launch.json +services.json +serviceControls/* +auth-dev.json +auth-prod.json diff --git a/bot.py b/bot.py index 8dc4760..cd619f5 100644 --- a/bot.py +++ b/bot.py @@ -1,50 +1,135 @@ #pip install -U git+https://github.com/Rapptz/discord.py import discord -from discord.ext import commands import random import json import asyncio import re import handlers +import models.serviceMonitor as serviceMonitor +import models.statusMessage +import threading -description = '''An example bot to showcase the discord.ext.commands extension -module. -There are a number of utility commands being showcased here.''' -bot = commands.Bot(command_prefix='!', description=description) +class Warforged(discord.Client): + + _statusMessages = [] + serviceMonitorInstance = None + statusUpdaterLoop = None + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) -@bot.event -async def on_message(message): - global bot - selfID = bot.user.id - if message.author.id != selfID: #recursion check. do not react to your own messages + #detached loop not connected to the discord client for checking service + + + + def serviceUpdaterLoop(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self.serviceUpdater()) + loop.close() + + async def serviceUpdater(self): + while not self.is_closed(): + #await channel.send("%s" % counter) + await asyncio.sleep(5) # task runs every 60 seconds + await self.serviceMonitorInstance.doServiceUpdate() + print("thread2-heartbeat") + + + async def updateStatusMessagesLoop(self): + #service check should go here + #status messages should be updated with text + while True: + for statusM in self._statusMessages: + if statusM.destroyed: + self._statusMessages.remove(statusM) + else: + await statusM.sendOrUpdate() + await asyncio.sleep(10) + print("asyncMessage-heartbeat") + + async def registerStatusMessage(self,report): + message = report.message + #when a message is registered, check for other statusMessages in the channel. + #delete other messages in the channel (they will be older) + #store it in list. + #regularly (every 10 seconds) check list of statusMessages and run their update function. + async for history in message.channel.history(limit=100): + if history.author == self.user and history != message: + if re.match(r'\*\*Information requested:\*\* Service status',history.content): + print("FOUND a definite message\t%s" % (history.content)) + await history.delete() + # self._statusMessages.remove(history) + self._statusMessages.append(report) - regexText = r'(!0071?8?2?7?4?6?0?2? |\/0071?8?2?7?4?6?0?2? ){1}([a-zA-Z0-9 ]*){1}(-[a-zA-z]*)?' - if re.search(regexText,message.content): - pieces = re.split(regexText,message.content) - await handlers.cmdGeneral(message,pieces,bot) - elif message.guild is None: - regexText = r'([a-zA-Z0-9 ]*){1}(-[a-zA-z]*)?' - if re.search(regexText,message.content): + async def reactivateHistoricStatusMessages(self): + for guild in self.guilds: + for channel in guild.channels: + if channel.type.name == "text": + try: + async for history in channel.history(limit=100): + if history.author == self.user: + if re.match(r'\*\*Information requested:\*\* Service status',history.content): + print("FOUND a definite message\t%s" % (history.content)) + deadStatusMessage = models.statusMessage.statusMessage(channel,message=history) + self._statusMessages.append(deadStatusMessage) + except Exception as e: + print("ERROR in reactivating historical status messages -\t%s\n%s"% (channel.name,e)) + + + + + async def on_message(self,message): + + selfID = self.user.id + if message.author.id != selfID: #recursion check. do not react to your own messages + + + regexText = r'^(!0071?8?2?7?4?6?0?2? |\/0071?8?2?7?4?6?0?2? ){1}([a-zA-Z0-9 ]*){1}(-[a-zA-z]*)?' + if re.search(r'^[!?\/](0071?8?2?7?4?6?0?2? )?valh[ei]{2}m', message.content): + await handlers.cmdValheim(message,self) + elif re.search(r'^[!?\/](0071?8?2?7?4?6?0?2? )?a?waken?', message.content): + await handlers.cmdAwaken(message,self) + elif re.search(r'^[!?\/](0071?8?2?7?4?6?0?2? )?status', message.content): + await handlers.cmdStatus(message,self) + + elif re.search(regexText,message.content): pieces = re.split(regexText,message.content) - pieces = ['','',pieces[1],pieces[2], ''] - await handlers.cmdGeneral(message,pieces,bot) + await handlers.cmdGeneral(message,pieces,self) + elif message.guild is None: + regexText = r'^([a-zA-Z0-9 ]*){1}(-[a-zA-z]*)?' + if re.search(regexText,message.content): + pieces = re.split(regexText,message.content) + pieces = ['','',pieces[1],pieces[2], ''] + await handlers.cmdGeneral(message,pieces,self) + + elif re.search(r'(00718274602)',message.content): + await handlers.reactTrophies(message) + elif re.search(r'(007[0-9]*)',message.content): + await handlers.reactEyes(message) + + + + async def on_ready(self): - elif re.search(r'(00718274602)',message.content): - await handlers.reactTrophies(message) - elif re.search(r'(007[0-9]*)',message.content): - await handlers.reactEyes(message) + + print('Logged in as') + print(self.user.name) + print(self.user.id) + print('------') + await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="!007 help")) + self.serviceMonitorInstance = await serviceMonitor.getActiveMonitor() + self.statusUpdaterLoop = threading.Thread(target=self.serviceUpdaterLoop) + self.statusUpdaterLoop.start() + self.loop.create_task(self.updateStatusMessagesLoop()) + await self.reactivateHistoricStatusMessages() -@bot.event -async def on_ready(): - print('Logged in as') - print(bot.user.name) - print(bot.user.id) - print('------') +if __name__ == "__main__": + bot = Warforged() + with open("auth.json",'r') as json_file: + config = json.load(json_file) + bot.run(config['token']) -with open("auth.json",'r') as json_file: - config = json.load(json_file) -bot.run(config['token']) diff --git a/handlers.py b/handlers.py index edc3801..b24de0a 100644 --- a/handlers.py +++ b/handlers.py @@ -3,8 +3,10 @@ import platform import math import random +import re +from models.statusMessage import * from SQLconnector import * - +import serviceMonitors.serviceValheim async def reactEyes(message): try: await message.add_reaction('\N{EYES}') @@ -18,7 +20,25 @@ async def reactTrophies(message): except discord.HTTPException: pass +async def cmdValheim(message,bot): + serviceController = bot.serviceMonitorInstance + if serviceController is None: + message.channel.send("Unable to connect to service controller - try again in 2 seconds.") + return + foundServices = False + for service in serviceController.getServices(serviceType="valheim"): + if isinstance(service,serviceMonitors.serviceValheim.ServiceValheim): + await service.tryStartService() + foundServices = True + if foundServices == True: + await message.channel.send("Attempted to activate all valheim servers. See `!007 status` for progress.") + else: + await message.channel.send("No valheim services configured, have the administrator check `services.json`") +async def cmdAwaken(message, bot): + computerServices = bot.serviceMonitorInstance.getServices(serviceType = 'server') + for service in computerServices: + await service.tryStartService() async def cmdGeneral(message, pieces, bot): try: @@ -32,134 +52,39 @@ async def cmdGeneral(message, pieces, bot): await message.channel.send('`An error has been encountered. Request not processed`') if command.lower() == "status": - await cmdStatus(message) - elif command.lower() == "redact": - await cmdRedact(message,bot) + await cmdStatus(message,bot) elif command.lower() == "help": await cmdHelp(message) - elif command.lower() == "historical": - await cmdHistorical(message) - elif command.lower() == "morning" or command.lower() == "good morning": - await cmdMorning(message) + elif command.lower() == "ping": + await cmdPing(message,bot) + elif command.lower() == "valheim": + await cmdValheim(message,bot) else: await cmdNotFound(message) -async def cmdStatus(message): - - embed = discord.Embed() - embed.title = "System status" - embed.add_field(name = "Bot Status", value = "`Active` ✅", inline=True) - embed.add_field(name = "Bot Home", value = '`%s`' % platform.node(), inline=True) +async def cmdStatus(message,bot): + report = statusMessage(channel=message.channel) + await report.sendOrUpdate() + await bot.registerStatusMessage(report) + #await bot.updateStatusMessages() - try: - conn = connectToSource() - - cursor = conn.cursor() - sql = """ with data as ( - select row_number() over - ( - partition by healthstatus order by finished desc ) as row - , * from public."jobsView" - ) - select * from data - where finished is null or - (finished is not null and row <= 3 and row > 0) - order by finished desc, started asc""" - cursor.execute(sql) - results = cursor.fetchall() - embed.add_field(name = "DB Status", value = "`Connected` ✅", inline=True) - except: - embed.add_field(name = "DB Status", value = "`OFfline` ❌", inline=True) - - await message.channel.send(embed=embed) - embed = discord.Embed() - embed.title = "Active and recent ETL jobs" - - - for result in results: - statusString = "%s **%s**" %(result[4][-5:],result[1]) - if result[11] is not None: - statusString = "%s, %s%%" % (statusString, result[11]) - embed.add_field(name = statusString, value = "%s" % (result[3]+" "*40)[:40]) - completionTime = "%s" %(result[7]) - completionTime = completionTime[0:19] - embed.add_field(name="Completion time",value = "%s" % (completionTime),inline=True) - embed.add_field(name="Blocking | Blocked by",value = "0 | 0") - await message.channel.send(embed=embed) - - - -async def cmdRedact(message, bot): - -# msg = await message.channel.send('This message will self destruct in 3 seconds ') -# -# await asyncio.sleep(3.0) -# await msg.edit(content='`[DATA REDACTED]`') -# await asyncio.sleep(3.0) -# await msg.edit(content='`[████ ████████]`') -# await asyncio.sleep(30.0) -# await msg.delete() - counter = 0 - async for msg in message.channel.history(limit=7): - - if msg.author.id == bot.user.id or msg.id == message.id: - try: - await msg.delete() - counter = counter + 1 - except: - pass - #print(msg) - await message.channel.send("Expunged %s messages" % counter) +async def cmdPing(message,bot): + content = """> Channel - %s,%s\n> you - %s,%s\n> your message - %s """ % (message.channel.id, message.channel.type.name, message.author.id, message.author.display_name, message.id ) + await message.channel.send(content) async def cmdHelp(message): - msg = await message.channel.send('''Thank you for contacting █████ ████████ for assistance. -Unfortunately you are not authorised to receive assistance from ███ ████ resources. -Remain Calm. + msg = await message.channel.send('''Greetings. This bot primarily serves to monitor the status of services and servers. Use the following commands to interact with me. If you _must_, the shorthand `!007` will work. You are cleared for the following instructions. -> `!00718274602 morning` - a standard greeting. -> `!00718274602 redact` - this command removes classified messages from the public domain. -> `!00718274602 status` - This command details recent activities within the ███████ application. -> `!00718274602 help` - displays this message. -> `!00718274602 historical` - briefly retells a incident report from the █████ ████████ -\n at this time direct conversations are ███ monitored by ███ ████. ''' +> `!00718274602 status` - Shows the status of all servers and services being monitor +> `!00718274602 awaken`/`wake`/`awake` - Awakens the monitored servers +> `!00718274602 valheim` - activates all valheim game-servers. +\n +Note that servers and services may shutdown outside of normal "awake" hours if inactive for more than 20 minutes. ''' ) async def cmdNotFound(message): msg = await message.channel.send('''Unrecognised or unauthorised request. Try `!00718274602 help`''') - -async def cmdHistorical(message): - text = '''Did you ever ████ the ███████ of █████ ████████ The ████? -I thought not. It’s not a █████ ███ ████ would ████ you. -It’s a ████ legend. █████ ████████ was a ████ ████ of ███ ████, so powerful and so wise ██ could use the █████ to █████████ the █████████████ to create ████… -██ had such a knowledge of ███ ████ ████ that ██ could even keep the ones ██ cared about from █████. -███████████████████████████████████████████████████████████████████████████████████████. -██ became so powerful… the only thing ██ was ██████ of was losing ███ power, which eventually, of course, ██ ███. -Unfortunately, ██ ██████ ███ ██████████ everything ██ knew, then ███ apprentice killed ███ in ███ █████. -Ironic. ██ could ████ others from █████, but not ███████.''' - msg = await message.channel.send(text) - await asyncio.sleep(10.0) - await msg.delete() - -async def cmdMorning(message): - embed = discord.Embed() - embed.title = "Greeting" - embed.set_footer(text="NOTE: This greeting does not imply exceptional familiarity.") - additionalcomments = ["Weather based observations","Comment regarding frequency of public transportation"] - - max = len(additionalcomments) - addtionalComment = additionalcomments[random.randint(0,max-1)] - - - embed.add_field(name="greeting value",value="basic", inline=True) - embed.add_field(name="familiarity index", value="distant",inline = True) - embed.add_field(name="additional comments",value =addtionalComment, inline = False) - - - - embed.description = "This represents a single unity of recognition and good will with the parameters listed below." - msg = await message.channel.send("Good morning %s, please find your greeting attached." % message.author.mention, embed=embed) - \ No newline at end of file diff --git a/messageHandlers.js b/messageHandlers.js deleted file mode 100644 index 945de04..0000000 --- a/messageHandlers.js +++ /dev/null @@ -1,31 +0,0 @@ -const messageHandlers = {} - - -messageHandlers.shortName = function(bot, channelID, userID, cmd) -{ - bot.sendMessage({to:channelID, message : 'Additionally, please be advised that my full designation is 00718274602. Please do not shorten it.'}); -} -messageHandlers.help = function(bot, channelID, userID, cmd) -{ - bot.sendMessage({to:channelID, message : 'This functionality is still being developed.'}); -} -messageHandlers.default = function(bot, channelID, userID, cmd) -{ - bot.sendMessage({to:channelID, message : 'Either the request was not submitted in a correct fashion, or you do not have authorisation to make the request.\n I will neither confirm nor deny whether either of the previous statements are appropriate to your request.'}); -} - - -messageHandlers.intro = function(bot, channelID, userID, cmd) -{ - - bot.sendMessage({to:channelID, message : 'I am an █████████ ████████████████ analysis █████████, working on behalf of ████████ \nFor a list of options and ████████, use `!00718274602 help`'}) -} -messageHandlers.hello = messageHandlers.intro; -messageHandlers.hi = messageHandlers.intro; -messageHandlers.hey = messageHandlers.intro; -messageHandlers.test = function(bot, channelID, userID, cmd) -{ - - bot.sendMessage({to:channelID, embed : 'I am an █████████ ████████████████ analysis █████████, working on behalf of ████████ \nFor a list of options and ████████, use `!00718274602 help`'}) -} -module.exports = messageHandlers; \ No newline at end of file diff --git a/models/service.py b/models/service.py new file mode 100644 index 0000000..e2a093e --- /dev/null +++ b/models/service.py @@ -0,0 +1,22 @@ +import json + + + +class Service(): + serviceName = "" + serviceType = "" + def __init__(self,serviceName,serviceType): + self.serviceName = serviceName + self.serviceType = serviceType + + async def checkService(self): + return + + def getFriendlyName(self): + return self.serviceName + + def getStatusEmoji(self): + return "⬛" + + def getStatusText(self): + return "Unsupported service found!" diff --git a/models/serviceMonitor.py b/models/serviceMonitor.py new file mode 100644 index 0000000..a54c4d8 --- /dev/null +++ b/models/serviceMonitor.py @@ -0,0 +1,83 @@ + +import json +from models.service import * +from serviceMonitors.serviceValheim import * +from serviceMonitors.serviceComputer import * +_staticMonitor = None + + +async def getActiveMonitor(): + global _staticMonitor + if _staticMonitor is None: + _staticMonitor = monitor() + await _staticMonitor.doServiceUpdate() + return _staticMonitor + + +class monitor: + _services = [] + def __init__(self): + self.loadConfig() + + def getServices(self, serviceType = None): + if serviceType == None: + return self._services + returnObj = [] + for service in self._services: + if service.serviceType == serviceType: + returnObj.append(service) + return returnObj + + + async def getServiceListText(self): + returnString = "" + for service in self._services: + if service.serviceType != "server": + returnString = returnString + "> %s\n" % (service.getFriendlyName()) + return returnString + + async def getServiceStatusText(self): + returnString = "" + for service in self._services: + if service.serviceType != "server": + returnString = returnString + "%s `%s`\n" % (service.getStatusEmoji(), service.getStatusText()) + return returnString + + async def getServerListText(self): + returnString = "" + for service in self._services: + if service.serviceType == "server": + returnString = returnString + "> %s\n" % (service.getFriendlyName()) + return returnString + + async def getServerStatusText(self): + returnString = "" + for service in self._services: + if service.serviceType == "server": + returnString = returnString + "%s `%s`\n" % (service.getStatusEmoji(), service.getStatusText()) + return returnString + + def loadConfig(self): + self._services = [] + configFile = open("services.json","r") + servicesList = json.load(configFile) + for serviceJSON in servicesList: + if serviceJSON["serviceType"] == "valheim": + valheimServer = ServiceValheim(serviceJSON["serviceName"],serviceJSON["serviceType"],serviceJSON["host"],serviceJSON["statusPort"],serviceJSON["startupCommand"],serviceJSON["shutdownCommand"]) + self._services.append(valheimServer) + elif serviceJSON["serviceType"] == "server": + computerService = ServiceComputer(serviceJSON["serviceName"],serviceJSON["serviceType"],serviceJSON["host"],serviceJSON["shutdownCommand"],serviceJSON["serverMacAddress"]) + self._services.append(computerService) + else: + genericService = Service(serviceJSON["serviceName"],serviceJSON["serviceType"]) + self._services.append(genericService) + + async def doServiceUpdate(self): + for service in self._services: + await service.checkService() + + +if __name__ == "__main__": + monitor = getActiveMonitor() + print(monitor) + \ No newline at end of file diff --git a/models/statusMessage.py b/models/statusMessage.py new file mode 100644 index 0000000..882cf27 --- /dev/null +++ b/models/statusMessage.py @@ -0,0 +1,89 @@ +import discord +import datetime +import models.serviceMonitor + + +class statusMessage: + messageEmbed = None + message = None + messageState = None + messageText = None + messageFooter = None + serverListText = "None" + serverStatusText = "⬛ no services checked" + servicesListText = "None" + servicesStatusText = "⬛ no services checked" + channel = None + + destroyed = False + + async def _sendMessage(self): + newMessage = await self.channel.send(content=self.messageText, embed = self.messageEmbed) + self.message = newMessage + + + async def sendOrUpdate(self): + await self._refreshEmbed() + + if self.message is not None: + try: + await self.message.edit(embed=self.messageEmbed) + except Exception as e: + print("Couldn't updpate status message \n %s" % e) + destroyed = True + return + else: + await self._sendMessage() + return + #send + + + + async def _editMessage(self): + return + + + async def _refreshEmbed(self): + + serviceMonitorInstance = await models.serviceMonitor.getActiveMonitor() + self.servicesListText = await serviceMonitorInstance.getServiceListText() + self.servicesStatusText = await serviceMonitorInstance.getServiceStatusText() + self.serverListText = await serviceMonitorInstance.getServerListText() + self.serverStatusText = await serviceMonitorInstance.getServerStatusText() + + messageEmbed = discord.Embed() + + messageEmbed.add_field(name="Server", value = self.serverListText, inline=True) + messageEmbed.add_field(name = "Status", value = self.serverStatusText, inline=True) + messageEmbed.add_field(name = "\u200b", value="\u200b", inline=True) + messageEmbed.add_field(name = "Service", value = self.servicesListText, inline=True) + messageEmbed.add_field(name = "Status", value = self.servicesStatusText, inline=True) + + messageEmbed.colour = discord.Colour.green() + messageEmbed.set_footer(text=self._getFooter()) + + self.messageEmbed = messageEmbed + return + + def setEmbedServicesList(self,string): + self.servicesListText = string + + def setEmbedStatusList(self,string): + self.servicesStatusText = string + + + def _getFooter(self): + return "Last updated %s" % datetime.datetime.now().strftime(r'%Y-%b-%d %H:%M:%S') + + + + def __init__(self, channel ,message = None): + if message is not None: + self.message = message + else: + self.messageText = """**Information requested:** Service status +Beginning report...""" + + self.channel = channel #discord.channel + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..978e2d2 --- /dev/null +++ b/readme.md @@ -0,0 +1,28 @@ +# installation + +Needs an auth.json file in the following format: +```json +{ + "token":"bot token" +} +``` + +and a services.json dictating what it should be looking for, and where. + +```json +[ + { + "serviceName": "hordelings", + "serviceType": "valheim", + "host": "127.0.0.1", + "statusPort": 2459, + "startupCommand": "serviceControls\\startServer.bat", + "shutdownCommand": "serviceControls\\stopServer.bat" + }, + { + "serviceName": "LaserScraper", + "serviceType": "localApplication", + "processName": "CmdMenus.py", + } +] +``` \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6beecd9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +discord +psycopg2 +requests +wakeonlan +threading +wakeonlan \ No newline at end of file diff --git a/serviceControls/launchValheimHordelings.bat b/serviceControls/launchValheimHordelings.bat new file mode 100644 index 0000000..7b4f5ab --- /dev/null +++ b/serviceControls/launchValheimHordelings.bat @@ -0,0 +1,2 @@ +docker pull lloesche/valheim-server +docker run -d --name valheim-server-hordelings --cap-add=sys_nice -p 2456-2458:2456-2458/udp -p 2459:80 -v C:\ValheimServers\Hordelings\ValheimConfig:/config -v C:\ValheimServers\Hordelings\ValheimServer:/opt/valheim -e SERVER_NAME="Hordelings" -e WORLD_NAME="Dedicated_hordes" -e SERVER_PASS="Gwent" -e STATUS_HTTP=true lloesche/valheim-server diff --git a/serviceMonitors/serviceComputer.py b/serviceMonitors/serviceComputer.py new file mode 100644 index 0000000..87427a0 --- /dev/null +++ b/serviceMonitors/serviceComputer.py @@ -0,0 +1,59 @@ +import os +import subprocess +from models.service import * +from wakeonlan import send_magic_packet +import socket + +#all service requests should get a name, type in JSON, then specific config settings + +class ServiceComputer(Service): + serverMacAddress = '' + serviceName = '' + serviceType ='' + host = '' + shutdownCommand = '' + _statusEmoji = ":black_large_square:" + _statusText = "Not checked yet" + + + def __init__(self,serviceName, serviceType, host, shutdownCommand, serverMacAddress ): + super().__init__(serviceName,serviceType) + + self.serviceType = serviceType + self.serviceName = serviceName + self.serverMacAddress = serverMacAddress + self.shutdownCommand = shutdownCommand + self.host = host + + async def checkService(self): + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(3) + try: + s.connect((self.host,22)) + self._statusEmoji = ":green_square:" + self._statusText = "online" + except socket.error as e: + print("INFO: couldn't connect to %s, reason %s" % (self.serviceName,e)) + self._statusEmoji = ":zzz:" + self._statusText = "offline" + + + def getFriendlyName(self): + return self.serviceName + + def getStatusEmoji(self): + return self._statusEmoji + + def getStatusText(self): + return self._statusText + + async def tryStartService(self): + send_magic_packet(self.serverMacAddress) + + def tryStopService(self): + os.system(self.shutdownCommand) + +if __name__ == '__main__': + s = ServiceComputer(":desktop: Ramoth","server","serviceControls\\sleepRamoth.bat","18-c0-d4-49-59-d0") + s.tryStartService() \ No newline at end of file diff --git a/serviceMonitors/serviceValheim.py b/serviceMonitors/serviceValheim.py new file mode 100644 index 0000000..73a2135 --- /dev/null +++ b/serviceMonitors/serviceValheim.py @@ -0,0 +1,90 @@ +import os +import requests +import json +import datetime +from models.service import * + +#all service requests should get a name, type in JSON, then specific config settings + +class ServiceValheim(Service): + serverHost = '' + serverStatusPort = '' + serviceName = '' + serviceType ='' + startupCommand = '' + shutdownCommand = '' + lastOccupied = None + _statusEmoji = ":black_large_square:" + _statusText = "Not checked yet" + + + def __init__(self,serviceName, serviceType,serverHost, serverStatusPort, startupCommand, shutdownCommand): + super().__init__(serviceName,serviceType) + self.serverHost = serverHost + self.serverStatusPort = serverStatusPort + self.serviceType = serviceType + self.serviceName = serviceName + self.startupCommand = startupCommand + self.shutdownCommand = shutdownCommand + self.lastOccupied = datetime.datetime.now() + + async def checkService(self): + url = "http://%s:%s/status.json" % (self.serverHost,self.serverStatusPort) + playerCount = 0 + try: + response = requests.get(url,timeout=5) + + if response == None: + raise Exception("Did not receive response from server") + + except Exception as e: + self._statusEmoji = ":red_square:" + self._statusText = "Server unreachable" + self.lastOccupied = None + print(e) + return + + if response.status_code == 200: + servercont = {} + try: + serverStatus = json.loads(response.content) + except: + self._statusEmoji = ":red_square:" + self._statusText = "Invalid response" + if "player_count" in serverStatus: + self._statusEmoji = ":green_square:" + self._statusText = "%s/10 players" % (serverStatus["player_count"]) + playerCount = serverStatus["player_count"] + else: + self._statusEmoji = ":yellow_square:" + self._statusText = "Probably waking up" + self.lastOccupied = datetime.datetime.now() + + if playerCount > 0: + self.lastOccupied = datetime.datetime.now() + else: + timeSinceOccupied = datetime.datetime.now() - self.lastOccupied + print("Server [%s]- time since last occupied: %s" % (self.getFriendlyName(),timeSinceOccupied)) + if timeSinceOccupied.seconds > 1200: + self.tryStopService() + # throw in the box active but server inactive + return + + def getFriendlyName(self): + return self.serviceName + + def getStatusEmoji(self): + return self._statusEmoji + + def getStatusText(self): + return self._statusText + + async def tryStartService(self ): + + os.system(self.startupCommand) + self.lastOccupied = datetime.datetime.now() + return + def tryStopService(self): + os.system(self.shutdownCommand) + + return