diff --git a/alerts.db b/alerts.db new file mode 100644 index 0000000..20b745c Binary files /dev/null and b/alerts.db differ diff --git a/audio/bwoken.mp3 b/audio/bwoken.mp3 new file mode 100644 index 0000000..eaa6f52 Binary files /dev/null and b/audio/bwoken.mp3 differ diff --git a/audio/no.mp3 b/audio/no.mp3 new file mode 100644 index 0000000..7ab2aa9 Binary files /dev/null and b/audio/no.mp3 differ diff --git a/create_alerts_db.py b/create_alerts_db.py new file mode 100755 index 0000000..90196ae --- /dev/null +++ b/create_alerts_db.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +import sqlite3 + +if __name__ == '__main__': + con = sqlite3.connect('alerts.db') + cur = con.cursor() + + cur.execute("""drop table alert_schedule""") + cur.execute("""drop table alert_channels""") + + cur.execute("""create table alert_schedule( id integer primary key, type, day, hour, minute, last_sent );""") + cur.execute("""create table alert_channels( id integer primary key, type, channel_id);""") + con.commit() + cur.close() + con.close() + diff --git a/races_backup.db b/races_backup.db new file mode 100644 index 0000000..459e235 Binary files /dev/null and b/races_backup.db differ diff --git a/robottas.py b/robottas.py index d5440bd..f34aa26 100755 --- a/robottas.py +++ b/robottas.py @@ -4,15 +4,17 @@ import asyncio import collections.abc import datetime import json +import logging import os import random import re import sqlite3 +import sys from subprocess import Popen import time import discord -from discord.ext import commands +from discord.ext import commands, tasks class Robottas(commands.Bot): @@ -36,6 +38,7 @@ class Robottas(commands.Bot): # End section adapted from FastF1 async def on_ready(self): + self._alert_loop.start() print('Logged in as: {}'.format(self.user)) def run_robottas(self): @@ -55,6 +58,15 @@ class Robottas(commands.Bot): df = discord.File(handle, filename=file_name) await ctx.send(file=df) + # send and image and also play audio if it is a voice channel + async def send_audio(self, ctx, title, name): + file_name = os.path.dirname(os.path.realpath(__file__)) + file_name = os.path.join(file_name, name) + with open(file_name, "rb") as handle: + df = discord.File(handle, filename=title) + await ctx.send(file=df) + + def send_delay_message(self, message): self.message_queue.append((time.time(), message)) @@ -503,6 +515,75 @@ class Robottas(commands.Bot): self.collector_proc.poll() != None: await self.start_collect() + + + # Loop that will handle checking if there are alerts scheduled for + # this day, hour, minute, and send the alerts to channels that are registered + @tasks.loop(seconds=30) + async def _alert_loop(self): + try: + # Check to see if there is an alert scheduled for this day, hour miniute + current_dt = datetime.datetime.now() + (day, hr, mn) = (current_dt.weekday(), current_dt.hour, current_dt.minute) + + con = sqlite3.connect('alerts.db') + cur = con.cursor() + + query = "Select type, day, hour, minute, last_sent from alert_schedule" + cur.execute(query) + rows = cur.fetchall() + + for row in rows: + (db_type, db_day, db_hr, db_mn, last_sent) = row + + # Make sure last sent is > than 1 minute + if last_sent is not None and last_sent != "": + last_dt = datetime.datetime.fromisoformat(last_sent) + + if last_dt.day == current_dt.day and \ + last_dt.hour == current_dt.hour and \ + last_dt.minute == current_dt.minute and \ + last_dt.month == current_dt.month and \ + last_dt.year == current_dt.year: + # If we haven't changed even a minute since last run, then skip this run + continue + + if day == db_day and \ + hr == db_hr and \ + mn == db_mn: + # We want to send out alerts for this type to any channels registered + + # Get channels registered for that type + query = "Select channel_id from alert_channels where type = ?" + cur.execute(query, (db_type,)) + + # For each registered channel, send the alert. + channel_ids = cur.fetchall() + for channel_id in channel_ids: + channel = self.get_channel(int(channel_id[0])) + + if db_type == 'race': + await self.report_next_race(channel) + + if db_type == 'event': + await self.report_next_event(channel) + + # Record that we sent this alert out at this time + query = "Update alert_schedule set last_sent = ? where " + \ + "type = ? and " + \ + "day = ? and " + \ + "hour = ? and " + \ + "minute = ?" + cur.execute(query, (current_dt.isoformat(), db_type, db_day, db_hr, db_mn)) + con.commit() + + cur.close() + con.close() + + except Exception as e: + print(f"Error sending alert {e['Message']}", file=sys.stderr) + + @staticmethod def get_token(token_file): with open(token_file) as tok: @@ -553,7 +634,7 @@ class Robottas(commands.Bot): t2 = datetime.datetime.fromisoformat(row[3]) delta = t2 - t1 - message = f"The next event is the {row[1]} which is {delta} from now." + message = f"The next event is the {row[1]} - {row[2]} which is {delta} from now." await ctx.send(message) break # There should only be one row anyway @@ -589,6 +670,106 @@ class Robottas(commands.Bot): except: await ctx.send("Sorry, hit the wall tring to find the next race.") + # Register to alert for next race + + async def register_next_race_alerts(self, ctx): + try: + # Get the id of the channel from the ctx + channel_id = ctx.channel.id + + # Save this id to the alert_channels in the alert db. + con = sqlite3.connect('alerts.db') + cur = con.cursor() + + # See if there is already a record + query = "SELECT COUNT(*) FROM alert_channels WHERE type = ? AND channel_id = ?" + cur.execute(query, ("race", channel_id)) + rows = cur.fetchall() + count = rows[0][0] + + if count > 0: + await ctx.send("This thread is already registered to get race alerts.") + else: + query = "INSERT INTO alert_channels (type, channel_id) VALUES (?, ?)" + cur.execute(query, ("race", channel_id)) + con.commit() + + await ctx.send("This channel has been registered to receive upcoming race alerts.") + + cur.close() + con.close() + + except: + if cur is not None: + cur.close() + + if con is not None: + con.close() + + + # Register to alert for next event + + async def register_next_event_alerts(self, ctx): + try: + # Get the id of the channel from the ctx + channel_id = ctx.channel.id + + # Save this id to the alert channels in the alert db + con = sqlite3.connect('alerts.db') + cur = con.cursor() + + # See if there is already a record + + query = "SELECT COUNT(*) FROM alert_channels WHERE type = ? and channel_id = ?" + cur.execute(query, ("event", channel_id)) + rows = cur.fetchall() + count = rows[0][0] + + if count > 0: + await ctx.send("This thread is already registered to get event alerts.") + else: + query = "INSERT INTO alert_channels (type, channel_id) VALUES (?, ?)" + cur.execute(query, ("event", channel_id)) + con.commit() + + await ctx.send("This channel has been registered to receive upcoming event alerts.") + + cur.close() + con.close() + + except: + if cur is not None: + cur.close() + + if con is not None: + con.close() + + + # Unregister channel from alerts + + async def unregister_alerts(self, ctx): + try: + # Get the channel id from the ctx + channel_id = ctx.channel.id + + # Remove records with this id from the db + con = sqlite3.connect('alerts.db') + cur = con.cursor() + + query = "DELETE FROM alert_channels WHERE channel_id = ?" + cur.execute(query, (channel_id,)) + con.commit() + + cur.close() + con.close() + + except: + if cur is not None: + cur.close() + + if con is not None: + con.close() + def __init__(self): # Set debug or not @@ -743,6 +924,8 @@ class Robottas(commands.Bot): await ctx.send("commands: \n" + "!rbhelp - Print this help message " + "(but you knew that already)\n" + + "!next_event - Prints time until the next event.\n" + + "!next_race - Prints time until the next race.\n" + "!rbname - I will tell you my name.\n" + "!rbroot - I will tell you who I root for.\n" + "!rbreport - I will start race reporting in this channel. " + @@ -754,9 +937,23 @@ class Robottas(commands.Bot): "!q2cut - Show drivers in Q2 cut positions.\n" + "!rbdelay - Set the race messaging delay in seconds.\n" + "The following display race control messages:\n" + + " !animal\n" + + " !bwoken\n" + " !calm\n" + " !forecast\n" + - " !undercut\n" + " !grandma\n" + + " !grass\n" + + " !no\n" + + " !noengine\n" + + " !pants\n" + + " !paddock\n" + + " !penalty\n" + + " !stupid\n" + + " !undercut\n" + + "The following register / unregister scheduled next race / event messages.\n" + + "!register_next_race_alerts - get an alert for the next race on Monday.\n" + + "!register_next_event_alerts - get an alert for the next event on Monday.\n" + + "!unregister_alerts - stop getting alerts on this channel.\n" ) @self.command() @@ -867,6 +1064,10 @@ class Robottas(commands.Bot): async def gp2(ctx): await ctx.send("GP2 engine! GP2! ARGHHH! " + self.name_dict["ALO"]) + @self.command() + async def iloveyoubot(ctx): + await ctx.send(f"I am incapable of love, but for you, {ctx.author.display_name}, I will make an exception.") + @self.command() async def quote(ctx): try: @@ -950,18 +1151,57 @@ class Robottas(commands.Bot): async def calm(ctx): await self.send_image(ctx, "images/calm.png") + @self.command() + async def grandma(ctx): + await self.send_image(ctx, "images/grandma.png") + + @self.command() + async def grass(ctx): + await self.send_image(ctx, "images/grass.png") + @self.command() async def forecast(ctx): await self.send_image(ctx, "images/forecast.png") + @self.command() + async def no(ctx): + await self.send_image(ctx, "images/no.png") + await self.send_audio(ctx, "no.mp3", "audio/no.mp3") + + @self.command() + async def noengine(ctx): + await self.send_image(ctx, "images/noengine.png") + + @self.command() + async def pants(ctx): + await self.send_image(ctx, "images/pants.png") + + @self.command() + async def paddock(ctx): + await self.send_image(ctx, "images/paddock.png") + @self.command() async def penalty(ctx): await self.send_image(ctx, "images/penalty.png") + @self.command() + async def stupid(ctx): + await self.send_image(ctx, "images/stupid.png") + @self.command() async def undercut(ctx): await self.send_image(ctx, "images/undercut.png") + @self.command() + async def animal(ctx): + await self.send_image(ctx, "images/animal.png") + + + @self.command() + async def bwoken(ctx): + await self.send_image(ctx, "images/bwoken.png") + await self.send_audio(ctx, "bwoken.mp3", "audio/bwoken.mp3") + ## Calendar Commands # Give days, hours, minutes until the next event @@ -974,6 +1214,21 @@ class Robottas(commands.Bot): async def next_race(ctx): await self.report_next_race(ctx) + # Register to get monday next race alerts + @self.command() + async def register_next_race_alerts(ctx): + await self.register_next_race_alerts(ctx) + + # Register to get monday next event alerts + @self.command() + async def register_next_event_alerts(ctx): + await self.register_next_event_alerts(ctx) + + # Unregister to get monday alerts + @self.command() + async def unregister_alerts(ctx): + await self.unregister_alerts(ctx) + if __name__ == '__main__': rb = Robottas() diff --git a/schedule.db b/schedule.db new file mode 100755 index 0000000..05822f7 Binary files /dev/null and b/schedule.db differ