Compare commits
2 Commits
96e9b11bdf
...
cf2af10110
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2af10110 | ||
|
|
745300c921 |
55
bingo.py
Normal 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
|
After Width: | Height: | Size: 6.1 KiB |
BIN
bingo_images/card_blank.xcf
Normal file
BIN
bingo_images/free_space.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
bingo_images/squares/a_driver_does_no_laps.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
bingo_images/squares/aerorake.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
bingo_images/squares/anyone_but_max_p1.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
bingo_images/squares/big_changes.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
bingo_images/squares/big_spin_t12.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
bingo_images/squares/car_debris_on_track.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
bingo_images/squares/car_different.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
bingo_images/squares/carbon_fiber.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
bingo_images/squares/chadlonso_time_life.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
bingo_images/squares/commentator_car_confused.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
bingo_images/squares/driver_complains_impeding.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
bingo_images/squares/flowviz.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
bingo_images/squares/haas_stops.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
bingo_images/squares/honda_engine_dies.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
bingo_images/squares/its_only_testing.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
bingo_images/squares/lockup_t1.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
bingo_images/squares/mercedes_downplays.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
bingo_images/squares/no_power.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
bingo_images/squares/radical_concept_fails.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
bingo_images/squares/radical_concept_works.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
bingo_images/squares/red_flag.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
bingo_images/squares/redbull_run_most.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
bingo_images/squares/sandbagging.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
bingo_images/squares/silly_season.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
bingo_images/squares/square_template.xcf
Normal file
BIN
bingo_images/squares/team_hides_work.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
bingo_images/squares/team_misnamed.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
bingo_images/squares/torro_rosso_or_vcarb.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
bingo_images/squares/williams_first_out.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
bingo_images/squares/williams_top_speed_trap.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
92
robottas.py
@@ -1,11 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import asyncio
|
||||
from bingo import Bingo
|
||||
import collections.abc
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import random
|
||||
import re
|
||||
import sqlite3
|
||||
@@ -612,15 +614,35 @@ class Robottas(commands.Bot):
|
||||
else:
|
||||
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):
|
||||
try:
|
||||
|
||||
tz = datetime.timezone.utc
|
||||
con = sqlite3.connect('schedule.db')
|
||||
cur = con.cursor()
|
||||
now_str = datetime.datetime.now(tz=tz).isoformat(sep=' ')
|
||||
now_str = now_str.split(".")[0]
|
||||
t1 = datetime.datetime.now(tz=tz)
|
||||
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 > ? ' + \
|
||||
'order by date_start asc limit 1'
|
||||
@@ -629,12 +651,12 @@ class Robottas(commands.Bot):
|
||||
rows = cur.fetchall()
|
||||
|
||||
for row in rows:
|
||||
|
||||
t1 = datetime.datetime.fromisoformat(now_str)
|
||||
t2 = datetime.datetime.fromisoformat(row[3])
|
||||
t2 = datetime.datetime.fromisoformat(row[3] + "+00:00")
|
||||
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)
|
||||
|
||||
break # There should only be one row anyway
|
||||
@@ -648,8 +670,8 @@ class Robottas(commands.Bot):
|
||||
tz = datetime.timezone.utc
|
||||
con = sqlite3.connect('schedule.db')
|
||||
cur = con.cursor()
|
||||
now_str = datetime.datetime.now(tz=tz).isoformat(sep=' ')
|
||||
now_str = now_str.split(".")[0]
|
||||
t1 = datetime.datetime.now(tz=tz)
|
||||
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 " + \
|
||||
"session_type = 'Race' ORDER BY date_start ASC LIMIT 1"
|
||||
@@ -658,11 +680,12 @@ class Robottas(commands.Bot):
|
||||
rows = cur.fetchall()
|
||||
|
||||
for row in rows:
|
||||
t1 = datetime.datetime.fromisoformat(now_str)
|
||||
t2 = datetime.datetime.fromisoformat(row[3])
|
||||
t2 = datetime.datetime.fromisoformat(row[3] + "+00:00")
|
||||
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)
|
||||
|
||||
break
|
||||
@@ -670,6 +693,25 @@ class Robottas(commands.Bot):
|
||||
except:
|
||||
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
|
||||
|
||||
async def register_next_race_alerts(self, ctx):
|
||||
@@ -775,6 +817,8 @@ class Robottas(commands.Bot):
|
||||
# Set debug or not
|
||||
self.debug = True
|
||||
|
||||
self.bingo = Bingo()
|
||||
|
||||
# Discord authentication token
|
||||
self.token = self.get_token("token.txt")
|
||||
self.collector_command = "robottas_collector.py"
|
||||
@@ -926,6 +970,7 @@ class Robottas(commands.Bot):
|
||||
"(but you knew that already)\n" +
|
||||
"!next_event - Prints time until the next event.\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" +
|
||||
"!rbroot - I will tell you who I root for.\n" +
|
||||
"!rbreport - I will start race reporting in this channel. " +
|
||||
@@ -943,6 +988,7 @@ class Robottas(commands.Bot):
|
||||
" !forecast\n" +
|
||||
" !grandma\n" +
|
||||
" !grass\n" +
|
||||
" !liked\n" +
|
||||
" !no\n" +
|
||||
" !noengine\n" +
|
||||
" !pants\n" +
|
||||
@@ -1147,6 +1193,11 @@ class Robottas(commands.Bot):
|
||||
await ctx.send("Bono, my tyres are gone " + self.name_dict["HAM"])
|
||||
|
||||
# Commands that send images
|
||||
|
||||
@self.command()
|
||||
async def bingo(ctx):
|
||||
await self.send_image(ctx, "images/bingo_win.png")
|
||||
|
||||
@self.command()
|
||||
async def calm(ctx):
|
||||
await self.send_image(ctx, "images/calm.png")
|
||||
@@ -1163,6 +1214,10 @@ class Robottas(commands.Bot):
|
||||
async def forecast(ctx):
|
||||
await self.send_image(ctx, "images/forecast.png")
|
||||
|
||||
@self.command()
|
||||
async def liked(ctx):
|
||||
await self.send_image(ctx, "images/liked.png")
|
||||
|
||||
@self.command()
|
||||
async def no(ctx):
|
||||
await self.send_image(ctx, "images/no.png")
|
||||
@@ -1214,6 +1269,11 @@ class Robottas(commands.Bot):
|
||||
async def 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
|
||||
@self.command()
|
||||
async def register_next_race_alerts(ctx):
|
||||
@@ -1230,6 +1290,14 @@ class Robottas(commands.Bot):
|
||||
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__':
|
||||
rb = Robottas()
|
||||
rb.run_robottas()
|
||||
|
||||