Compare commits

...

2 Commits

Author SHA1 Message Date
tamservo
cf2af10110 added bingo card commands 2024-02-17 08:41:15 -05:00
tamservo
745300c921 adding bingo module 2024-02-17 08:01:17 -05:00
35 changed files with 135 additions and 12 deletions

55
bingo.py Normal file
View File

@@ -0,0 +1,55 @@
import glob
from PIL import Image
from random import randrange
from uuid import uuid4
class Bingo:
"""Class which allows creation and cleanup of F1 bingo card images."""
def __init__(self):
self.LAYOUT_WIDTH = 88
self.X_OFFSET = 10
self.Y_OFFSET = 110
self.SQUARES_PATH = "bingo_images/squares/*.png"
self.FREE_SQUARE_PATH = "bingo_images/free_space.png"
self.BLANK_CARD_PATH = "bingo_images/card_blank.png"
self.TEMP_FOLDER = "bingo_images/temp/"
self.square_files = glob.glob(self.SQUARES_PATH)
def get_card(self):
used_files = set()
with Image.open(self.BLANK_CARD_PATH) as card_img:
card_img.load()
card_img = card_img.convert('RGBA')
# Fill the grid
for y in range(5):
for x in range(5):
square_file = ""
# If this is the center square, use the free square
if x == 2 and y == 2:
square_file = self.FREE_SQUARE_PATH
# otherwise, find a random file that hasn't been used yet
else:
rand_file_idx = randrange(len(self.square_files))
while rand_file_idx in used_files:
rand_file_idx = randrange(len(self.square_files))
square_file = self.square_files[rand_file_idx]
used_files.add(rand_file_idx)
with Image.open(square_file) as square:
position = (self.X_OFFSET + (x * self.LAYOUT_WIDTH),
self.Y_OFFSET + (y * self.LAYOUT_WIDTH))
card_img.paste(square, position, square)
# Write image to temp file
outfile = "".join((self.TEMP_FOLDER, str(uuid4()), ".png"))
print(f"{outfile=}")
card_img.save(outfile)
return outfile

BIN
bingo_images/card_blank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
bingo_images/card_blank.xcf Normal file

Binary file not shown.

BIN
bingo_images/free_space.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,11 +1,13 @@
#!/usr/bin/python3 #!/usr/bin/python3
import asyncio import asyncio
from bingo import Bingo
import collections.abc import collections.abc
import datetime import datetime
import json import json
import logging import logging
import os import os
import pathlib
import random import random
import re import re
import sqlite3 import sqlite3
@@ -611,16 +613,36 @@ class Robottas(commands.Bot):
return 'Not Watched Yet' return 'Not Watched Yet'
else: else:
return 'Watched Already' return 'Watched Already'
def get_delta_str(self, delta):
min_str = "minute"
hour_str = "hour"
day_str = "day"
delta_str = str(delta)
(days, junk, rest) = delta_str.split(" ")
rest = rest.split(".")[0]
(hours, minutes, seconds) = rest.split(":")
if int(days) > 1:
day_str = "days"
if int(minutes) > 1:
min_str = "minutes"
if int(hours) > 1:
hour_str = "hours"
return f"{days} {day_str} {hours} {hour_str} {minutes} {min_str}"
async def report_next_event(self, ctx): async def report_next_event(self, ctx):
try: try:
tz = datetime.timezone.utc tz = datetime.timezone.utc
con = sqlite3.connect('schedule.db') con = sqlite3.connect('schedule.db')
cur = con.cursor() cur = con.cursor()
now_str = datetime.datetime.now(tz=tz).isoformat(sep=' ') t1 = datetime.datetime.now(tz=tz)
now_str = now_str.split(".")[0] now_str = f"{t1.year}-{t1.month:02d}-{t1.day:02d} {t1.hour:02d}:{t1.minute:02d}:{t1.second}"
query = 'select * from schedule where date_start > ? ' + \ query = 'select * from schedule where date_start > ? ' + \
'order by date_start asc limit 1' 'order by date_start asc limit 1'
@@ -629,12 +651,12 @@ class Robottas(commands.Bot):
rows = cur.fetchall() rows = cur.fetchall()
for row in rows: for row in rows:
t2 = datetime.datetime.fromisoformat(row[3] + "+00:00")
t1 = datetime.datetime.fromisoformat(now_str)
t2 = datetime.datetime.fromisoformat(row[3])
delta = t2 - t1 delta = t2 - t1
message = f"The next event is the {row[1]} - {row[2]} which is {delta} from now." delta_str = self.get_delta_str(delta)
message = f"The next event is Round {row[5]}: {row[1]} in {row[4]} which is {delta_str} from now."
await ctx.send(message) await ctx.send(message)
break # There should only be one row anyway break # There should only be one row anyway
@@ -648,8 +670,8 @@ class Robottas(commands.Bot):
tz = datetime.timezone.utc tz = datetime.timezone.utc
con = sqlite3.connect('schedule.db') con = sqlite3.connect('schedule.db')
cur = con.cursor() cur = con.cursor()
now_str = datetime.datetime.now(tz=tz).isoformat(sep=' ') t1 = datetime.datetime.now(tz=tz)
now_str = now_str.split(".")[0] now_str = f"{t1.year}-{t1.month:02d}-{t1.day:02d} {t1.hour:02d}:{t1.minute}:{t1.second}"
query = "SELECT * FROM schedule WHERE date_start > ? AND " + \ query = "SELECT * FROM schedule WHERE date_start > ? AND " + \
"session_type = 'Race' ORDER BY date_start ASC LIMIT 1" "session_type = 'Race' ORDER BY date_start ASC LIMIT 1"
@@ -658,11 +680,12 @@ class Robottas(commands.Bot):
rows = cur.fetchall() rows = cur.fetchall()
for row in rows: for row in rows:
t1 = datetime.datetime.fromisoformat(now_str) t2 = datetime.datetime.fromisoformat(row[3] + "+00:00")
t2 = datetime.datetime.fromisoformat(row[3])
delta = t2 - t1 delta = t2 - t1
message = f"The next race is the {row[1]} which is {delta} from now." delta_str = self.get_delta_str(delta)
message = f"The next race is Round {row[5]}: {row[1]} in {row[4]} which is {delta_str} from now."
await ctx.send(message) await ctx.send(message)
break break
@@ -670,6 +693,25 @@ class Robottas(commands.Bot):
except: except:
await ctx.send("Sorry, hit the wall tring to find the next race.") await ctx.send("Sorry, hit the wall tring to find the next race.")
async def report_all_races(self, ctx):
try:
con = sqlite3.connect('schedule.db')
cur = con.cursor()
query = "SELECT * FROM schedule where session_type = 'Race' ORDER BY date_start ASC"
cur.execute(query)
rows = cur.fetchall()
await ctx.send( "All times UTC\n" )
for row in rows:
await ctx.send( f"Round {row[5]}: {row[1]} in {row[4]} which takes place {row[3]}\n" )
except:
await ctx.send("Sorry, hit the wall tring to show all races.")
# Register to alert for next race # Register to alert for next race
async def register_next_race_alerts(self, ctx): async def register_next_race_alerts(self, ctx):
@@ -775,6 +817,8 @@ class Robottas(commands.Bot):
# Set debug or not # Set debug or not
self.debug = True self.debug = True
self.bingo = Bingo()
# Discord authentication token # Discord authentication token
self.token = self.get_token("token.txt") self.token = self.get_token("token.txt")
self.collector_command = "robottas_collector.py" self.collector_command = "robottas_collector.py"
@@ -926,6 +970,7 @@ class Robottas(commands.Bot):
"(but you knew that already)\n" + "(but you knew that already)\n" +
"!next_event - Prints time until the next event.\n" + "!next_event - Prints time until the next event.\n" +
"!next_race - Prints time until the next race.\n" + "!next_race - Prints time until the next race.\n" +
"!all_races - Prints the loation and date of all races in the schedule.\n" +
"!rbname - I will tell you my name.\n" + "!rbname - I will tell you my name.\n" +
"!rbroot - I will tell you who I root for.\n" + "!rbroot - I will tell you who I root for.\n" +
"!rbreport - I will start race reporting in this channel. " + "!rbreport - I will start race reporting in this channel. " +
@@ -943,6 +988,7 @@ class Robottas(commands.Bot):
" !forecast\n" + " !forecast\n" +
" !grandma\n" + " !grandma\n" +
" !grass\n" + " !grass\n" +
" !liked\n" +
" !no\n" + " !no\n" +
" !noengine\n" + " !noengine\n" +
" !pants\n" + " !pants\n" +
@@ -1147,6 +1193,11 @@ class Robottas(commands.Bot):
await ctx.send("Bono, my tyres are gone " + self.name_dict["HAM"]) await ctx.send("Bono, my tyres are gone " + self.name_dict["HAM"])
# Commands that send images # Commands that send images
@self.command()
async def bingo(ctx):
await self.send_image(ctx, "images/bingo_win.png")
@self.command() @self.command()
async def calm(ctx): async def calm(ctx):
await self.send_image(ctx, "images/calm.png") await self.send_image(ctx, "images/calm.png")
@@ -1163,6 +1214,10 @@ class Robottas(commands.Bot):
async def forecast(ctx): async def forecast(ctx):
await self.send_image(ctx, "images/forecast.png") await self.send_image(ctx, "images/forecast.png")
@self.command()
async def liked(ctx):
await self.send_image(ctx, "images/liked.png")
@self.command() @self.command()
async def no(ctx): async def no(ctx):
await self.send_image(ctx, "images/no.png") await self.send_image(ctx, "images/no.png")
@@ -1214,6 +1269,11 @@ class Robottas(commands.Bot):
async def next_race(ctx): async def next_race(ctx):
await self.report_next_race(ctx) await self.report_next_race(ctx)
# Show all races
@self.command()
async def all_races(ctx):
await self.report_all_races(ctx)
# Register to get monday next race alerts # Register to get monday next race alerts
@self.command() @self.command()
async def register_next_race_alerts(ctx): async def register_next_race_alerts(ctx):
@@ -1230,6 +1290,14 @@ class Robottas(commands.Bot):
await self.unregister_alerts(ctx) await self.unregister_alerts(ctx)
# Bingo card
@self.command()
async def bingo_card(ctx):
card_file = self.bingo.get_card()
await self.send_image(ctx, card_file)
pathlib.Path.unlink(card_file)
if __name__ == '__main__': if __name__ == '__main__':
rb = Robottas() rb = Robottas()
rb.run_robottas() rb.run_robottas()