Skip to content

Commit

Permalink
Merge pull request #1 from Ctri-The-Third/serviceMonitor
Browse files Browse the repository at this point in the history
Service monitor
  • Loading branch information
Ctri-The-Third authored Mar 15, 2021
2 parents 3f48a38 + 6c304af commit a01338b
Show file tree
Hide file tree
Showing 12 changed files with 540 additions and 178 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,7 @@ auth.json
.vscode/launch.json
.vscode/settings.json
.vscode/launch.json
services.json
serviceControls/*
auth-dev.json
auth-prod.json
149 changes: 117 additions & 32 deletions bot.py
Original file line number Diff line number Diff line change
@@ -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'])
155 changes: 40 additions & 115 deletions handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}')
Expand All @@ -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:
Expand All @@ -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)

Loading

0 comments on commit a01338b

Please sign in to comment.