Added a database, and a /top command

This commit is contained in:
termite 2025-01-18 11:01:47 -08:00
parent 0f5b65586a
commit 4b8e4f8f52
17 changed files with 1211 additions and 219 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
.env
**/__pycache__
tags
.ycm_extra_conf.py
stats.db

508
bot.py
View file

@ -1,11 +1,20 @@
# bot.py
import os
import datetime
import time
import db
import sqlite3
import discord
from game_logic import connections as con
import re
from collections import Counter
from discord.ext import commands
import gamelogic as gamlog
import re
from collections import Counter
from dotenv import load_dotenv
WORDLE_CHANNEL = 1317916234863480832
@ -14,54 +23,94 @@ MISC_GEOGRAPHY = 1317916442342981722
GLOBLE_CHANNEL = 1320505660701413511
WHEN_WHERE_CHANNEL = 1320505906592612394
MISC_CHANNEL = 1317916469660618752
GENSHINDLE_CHANNEL = 1320505821112832010
CHANNELS = [
WORDLE_CHANNEL,
MISC_GEOGRAPHY,
GLOBLE_CHANNEL,
WHEN_WHERE_CHANNEL,
MISC_CHANNEL,
GENSHINDLE_CHANNEL
]
possible_games = [
"Wordle",
"Connections",
"Satle",
"Globle",
"Airport Guessr",
"WhereTaken",
"WhenTaken",
"Flagle",
"Genshindle",
"Planespottle"
]
chat_limit = 1000
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot()
bot = commands.Bot(intents=intents)
db.create_table();
@bot.slash_command(
name="stats",
description="Prints out your stats in a daily game, examples include wordle",
guild_ids=[261345598987436033]
)
async def stats(interaction: discord.Interaction, game, user: discord.User = None):
async def stats(
interaction: discord.Interaction,
game : discord.Option(
str,
choices=possible_games),
user: discord.User = None
):
await interaction.response.defer()
if user is None:
user = interaction.user
conn = sqlite3.connect("stats.db")
cursor = conn.cursor()
if not game in possible_games:
print("SQL Injection???")
await interaction.followup.send(
"Sorry, something fucky wucky happened",
ephemeral=True
)
return
sql = 'SELECT * FROM ' + game.upper().replace(" ", "_") + ' WHERE NAME = ?'
cursor.execute(sql, (user.id,))
data = cursor.fetchone()
conn.close()
if not data:
await interaction.followup.send(
"No %s results found." % (game),
ephemeral=True
)
return
match game.lower():
case "wordle":
channel = bot.get_channel(WORDLE_CHANNEL)
if user is None:
user = interaction.user
WORDLE_PATTERN = r"^Wordle (\d{1,3}(?:,\d{3})*) (X|\d+)/(\d+)"
total_guesses = 0
total_games = 0
wins = 0
async for message in channel.history(limit=1000): # Limit can be adjusted
if message.author == user:
for line in message.content.splitlines():
match = re.match(WORDLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(2)
if guesses != 'X':
total_guesses += int(guesses)
wins += 1
break
if total_games == 0:
await interaction.followup.send(
"No Wordle results found.",
ephemeral=True
)
return
average_guesses = total_guesses / wins
win_rate = (wins / total_games) * 100
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Wordle stats for {user.mention}:\n"
@ -71,47 +120,13 @@ async def stats(interaction: discord.Interaction, game, user: discord.User = Non
ephemeral=False # Send message only to the user who called the command
)
case "connections":
channel = bot.get_channel(WORDLE_CHANNEL)
if user is None:
user = interaction.user
CONNECTIONS_PATTERN = r"^Connections\nPuzzle #(\d+)\n((?:[\u2B1B\u2B1C\u2B1D\u2B1E\u2B20]{4}\n?)+)$"
total_guesses = 0
total_games = 0
wins = 0
perfects = 0
async for message in channel.history(limit=1000): # Limit can be adjusted
if message.author == user:
color_grid = con.extract_connections_grid( message.content)
if color_grid:
guesses = con.check_connections_win(color_grid)
total_games += 1
if guesses > 0:
total_guesses += guesses
wins += 1
perfect_game = con.is_perfect_game(color_grid)
if perfect_game:
perfects += 1
if total_games == 0:
await interaction.followup.send(
"No Connections results found.",
ephemeral=True
)
return
average_guesses = total_guesses / wins
win_rate = (wins / total_games) * 100
total_games = data[1]
wins = data[2]
perfects = data[3]
win_rate = data[4]
perfect_rate = data[5]
total_guesses = data[6]
average_guesses = data[7]
await interaction.followup.send(
f"Connections stats for {user.mention}:\n"
@ -123,40 +138,11 @@ async def stats(interaction: discord.Interaction, game, user: discord.User = Non
ephemeral=False # Send message only to the user who called the command
)
case "satle":
channel = bot.get_channel(MISC_GEOGRAPHY)
if user is None:
user = interaction.user
SATLE_PATTERN = r"🛰Satle #[0-9]+ (\d)/6"
total_guesses = 0
total_games = 0
wins = 0
async for message in channel.history(limit=1000): # Limit can be adjusted
if message.author == user:
lines = message.content.splitlines()
for line in lines:
match = re.match(SATLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(1)
if lines[lines.index(line)+1].__contains__("🟩"):
total_guesses += int(guesses)
wins += 1
break
if total_games == 0:
await interaction.followup.send(
"No Satle results found.",
ephemeral=True
)
return
average_guesses = total_guesses / wins
win_rate = (wins / total_games) * 100
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Satle stats for {user.mention}:\n"
@ -166,61 +152,9 @@ async def stats(interaction: discord.Interaction, game, user: discord.User = Non
ephemeral=False # Send message only to the user who called the command
)
case "globle":
misc_channel = bot.get_channel(MISC_GEOGRAPHY)
globle_channel = bot.get_channel(GLOBLE_CHANNEL)
if user is None:
user = interaction.user
GLOBLE_PATTERN = r"I guessed todays Globle in ([0-9]+) tries:"
BAD_GLOBLE_PATTERN = r".* = ([0-9]+)"
total_guesses = 0
total_games = 0
async for message in misc_channel.history(limit=1000): # Limit can be adjusted
if message.author == user:
lines = message.content.splitlines()
for line in lines:
match = re.match(GLOBLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(1)
total_guesses += int(guesses)
break
match = re.match(BAD_GLOBLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(1)
total_guesses += int(guesses)
break
async for message in globle_channel.history(limit=1000): # Limit can be adjusted
if message.author == user:
lines = message.content.splitlines()
for line in lines:
match = re.match(GLOBLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(1)
total_guesses += int(guesses)
break
match = re.match(BAD_GLOBLE_PATTERN, line.strip())
if match:
total_games += 1
guesses = match.group(1)
total_guesses += int(guesses)
break
if total_games == 0:
await interaction.followup.send(
"No Globle results found.",
ephemeral=True
)
return
average_guesses = total_guesses / total_games
total_games = data[1]
total_guesses = data[2]
average_guesses = data[3]
await interaction.followup.send(
f"Globle stats for {user.mention}:\n"
@ -228,15 +162,251 @@ async def stats(interaction: discord.Interaction, game, user: discord.User = Non
f"Average Guesses per Game: {average_guesses:.2f}\n",
ephemeral=False # Send message only to the user who called the command
)
case "airport guessr":
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Airport Guessr stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Guesses per Winning Game: {average_guesses:.2f}\n"
f"Win Rate: {win_rate:.2f}%\n",
ephemeral=False # Send message only to the user who called the command
)
case "whentaken":
total_games = data[1]
total_distance = data[2]
average_distance = data[3]
total_points = data[4]
average_points = data[5]
total_years = data[6]
average_years = data[7]
await interaction.followup.send(
f"Where Taken stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Distance: {average_distance:.2f} km\n"
f"Average Time Distance: {average_years:.2f} years\n"
f"Average Score: {average_points:.2f}\n",
ephemeral=False # Send message only to the user who called the command
)
case "wheretaken":
total_games = data[1]
total_distance = data[2]
average_distance = data[3]
total_points = data[4]
average_points = data[5]
await interaction.followup.send(
f"Where Taken stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Distance: {average_distance:.2f} km\n"
f"Average Score: {average_points:.2f}\n",
ephemeral=False # Send message only to the user who called the command
)
case "flagle":
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Flagle stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Guesses per Winning Game: {average_guesses:.2f}\n"
f"Win Rate: {win_rate:.2f}%\n",
ephemeral=False # Send message only to the user who called the command
)
case "genshindle":
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Genshindle stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Guesses per Winning Game: {average_guesses:.2f}\n"
f"Win Rate: {win_rate:.2f}%\n",
ephemeral=False # Send message only to the user who called the command
)
case "planespottle":
total_games = data[1]
wins = data[2]
win_rate = data[3]
total_guesses = data[4]
average_guesses = data[5]
await interaction.followup.send(
f"Planespottle stats for {user.mention}:\n"
f"Total Games Played: {total_games}\n"
f"Average Guesses per Winning Game: {average_guesses:.2f}\n"
f"Win Rate: {win_rate:.2f}%\n",
ephemeral=False # Send message only to the user who called the command
)
case _:
await interaction.followup.send(
"Not a game.",
ephemeral=True
)
table_dict = {
"games" : "GAMES",
"total wins" : "WINS",
"win rate" : "WIN_RATE",
"average guesses" : "GUESS_AVG",
"perfects" : "PERFECTS",
"perfect rate" : "PERFECT_RATE",
"distance" : "DISTANCE_AVG",
"score" : "POINTS_AVG",
"time distance" : "YEARS_AVG"
}
async def get_table_options(ctx : discord.AutocompleteContext):
game = ctx.options['game']
match game.lower():
case "wordle":
return ["games","total wins","win rate","average guesses"]
case "connections":
return ["games","total wins","win rate","average guesses","perfects","perfect_rate"]
case "satle":
return ["games","total wins","win rate","average guesses"]
case "globle":
return ["games","average guesses"]
case "airport guessr":
return ["games","total wins","win rate","average guesses"]
case "whentaken":
return ["games", "distance", "score", "time distance"]
case "wheretaken":
return ["games", "distance", "score"]
case "flagle":
return ["games","total wins","win rate","average guesses"]
case "genshindle":
return ["games","total wins","win rate","average guesses"]
case "planespottle":
return ["games","total wins","win rate","average guesses"]
case _:
return ["HOW DID YOU GET HERE? SCREW YOU FOR MESSING WITH MY PROGRAM"]
@bot.slash_command(
name="top",
description="Prints out the top players of the given game.",
guild_ids=[261345598987436033],
)
async def top(
interaction: discord.Interaction,
game : discord.Option(
str,
choices=possible_games),
sort_by : discord.Option(str, autocomplete=discord.utils.basic_autocomplete(get_table_options)),
count : discord.Option(int) = 3
):
await interaction.response.defer()
table_column = table_dict[sort_by]
conn = sqlite3.connect("stats.db")
cursor = conn.cursor()
if not game in possible_games:
print("SQL Injection???")
await interaction.followup.send(
"Sorry, something fucky wucky happened",
ephemeral=True
)
return
order = "DESC"
if table_column in ["GUESS_AVG", "DISTANCE_AVG", "YEARS_AVG"]:
order = "ASC"
sql = 'SELECT NAME,' + table_column + ' FROM ' + game.upper().replace(" ", "_") + ' ORDER BY ' + table_column + '=0, ' + table_column + " " + order
cursor.execute(sql)
data = cursor.fetchmany(count)
conn.close()
if not data:
await interaction.followup.send(
"No %s results found." % (game),
ephemeral=True
)
return
return_message = "Top " + str(len(data)) + " Players for " + game + " by " + sort_by + "\n"
for i, stat in enumerate(data):
modifier = ''
match table_column:
case "WIN_RATE" | "PERFECT_RATE":
modifier = "%"
case "DISTANCE_AVG":
modifier = " km"
case "YEARS_AVG":
modifier = " years"
case "GAMES":
modifier = " games"
case "GUESS_AVG":
modifier = " guesses"
line = f"#{i+1} <@{stat[0]}> Stats: {stat[1]:.2f}{modifier}\n"
return_message += line
await interaction.followup.send(
return_message,
ephemeral=False # Send message only to the user who called the command
)
def process_message(message : discord.message, reverse : bool):
channel = bot.get_channel(WORDLE_CHANNEL)
PATTERNS = {
gamlog.wordle : r"Wordle (\d{1,3}(?:,\d{3})*) (X|\d+)/(\d+)",
gamlog.connections : r"Connections\nPuzzle #",
gamlog.satle : r"🛰Satle #[0-9]+ (\d)/6",
gamlog.bad_globle: r"🌎 [a-zA-Z]{3} [0-9]+, [0-9]{4} 🌍",
gamlog.globle : r"I guessed todays Globle in ([0-9]+) tries:",
gamlog.airport_guessr : r"I (got the|did not guess the) airport (in \d (guess|guesses) )?today:",
gamlog.wheretaken : r"#WhereTaken #[0-9]+ \([0-9]{2}\.[0-9]{2}\.[0-9]{4}\)",
gamlog.whentaken : r"#WhenTaken #[0-9]+ \([0-9]{2}\.[0-9]{2}\.[0-9]{4}\)",
gamlog.flagle : r"#Flagle #[0-9]+ \(.*\) ./6",
gamlog.genshindle : r"I (found|couldn't find) today's #Genshindle",
gamlog.planespottle : r"Planespottle #[0-9]+ (failed to guess|in \d/5 guesses)!"
}
for game, pattern in PATTERNS.items():
match = re.search(pattern, message.content.strip())
if match:
game(message, reverse)
@bot.event
async def on_ready():
for channel in CHANNELS:
channel = bot.get_channel(channel)
async for message in channel.history(limit=chat_limit,oldest_first=True):
process_message(message, False)
print('Ready!')
@bot.event
async def on_message(message : discord.message):
process_message(message, False)
@bot.event
async def on_message_delete(message : discord.message):
process_message(message, True)
bot.run(TOKEN)

124
db.py Normal file
View file

@ -0,0 +1,124 @@
import sqlite3
import pathlib
def create_table():
cnt = None
cursor = None
try:
# please remove this later,
# it just removes the database for testing
pathlib.Path.unlink("./stats.db")
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
print('DB Init')
query = 'select sqlite_version();'
cursor.execute(query)
result = cursor.fetchall()
print('SQLite Version is {}'.format(result))
except sqlite3.Error as error:
print('Error occurred - ', error)
game_tables = [''' CREATE TABLE IF NOT EXISTS WORDLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS CONNECTIONS(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
PERFECTS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
PERFECT_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS SATLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS GLOBLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS AIRPORT_GUESSR(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS WHERETAKEN(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
DISTANCE REAL NOT NULL,
DISTANCE_AVG REAL NOT NULL,
POINTS INTEGER NOT NULL,
POINTS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS WHENTAKEN(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
DISTANCE REAL NOT NULL,
DISTANCE_AVG REAL NOT NULL,
POINTS INTEGER NOT NULL,
POINTS_AVG REAL NOT NULL,
YEARS INTEGER NOT NULL,
YEARS_AVG REAL NOT NULL,
LAST_SYNC INTEGER
); ''',
''' CREATE TABLE IF NOT EXISTS FLAGLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS GENSHINDLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); ''',
''' CREATE TABLE IF NOT EXISTS PLANESPOTTLE(
NAME INTEGER PRIMARY KEY NOT NULL,
GAMES INTEGER NOT NULL,
WINS INTEGER NOT NULL,
WIN_RATE REAL NOT NULL,
GUESSES INTEGER NOT NULL,
GUESS_AVG REAL NOT NULL,
LAST_SYNC INTEGER NOT NULL
); '''
]
for table in game_tables:
cursor.execute(table)
conn.commit()
conn.close()
print("Tables Created")

View file

View file

@ -1,50 +0,0 @@
from collections import Counter
def extract_connections_grid(message):
message_content = message.strip()
if message_content.startswith("Connections") and "Puzzle #" in message_content:
# Extract the puzzle number
puzzle_number_start = message_content.find("Puzzle #") + len("Puzzle #")
puzzle_number_end = message_content.find("\n", puzzle_number_start)
puzzle_number = message_content[puzzle_number_start:puzzle_number_end].strip()
# Extract the grid (assume the grid starts after the first two lines)
grid_start = message_content.find("\n", message_content.find("Puzzle #")) + 1
grid_content = message_content[grid_start:].strip()
# Split the grid into lines
color_grid = grid_content.split("\n")
return color_grid
else:
return False
def check_connections_win(color_grid):
"""
Check if the Connections game is a win and return the number of guesses.
A win is when there are exactly 4 groups of 4 colors each.
"""
# Flatten the color grid into a list of colors
uniform_rows = [row for row in color_grid if len(set(row)) == 1]
flattened_grid = ''.join(uniform_rows)
# Count the occurrences of each color in the grid
color_counts = Counter(flattened_grid)
# Check if there are exactly 4 different colors with 4 squares each
if len(color_counts) == 4 and all(count == 4 for count in color_counts.values()):
# A win: return the number of guesses (lines in the grid)
return len(color_grid)
return 0 # Not a win
def is_perfect_game(color_grid):
"""
Check if the Connections game is a perfect game (solved in 4 guesses, no mistakes).
"""
# A perfect game is one where the first 4 lines form perfect groups
if len(color_grid) == 4 and all(len(set(line)) == 1 for line in color_grid):
return True # It's a perfect game
return False

13
gamelogic/__init__.py Normal file
View file

@ -0,0 +1,13 @@
from gamelogic import wheretaken
from .wordle import wordle
from .connections import connections
from .satle import satle
from .globle import globle
from .globle import bad_globle
from .airport_guessr import airport_guessr
from .flagle import flagle
from .genshindle import genshindle
from .planespottle import planespottle
from .wheretaken import wheretaken
from .wheretaken import whentaken

View file

@ -0,0 +1,61 @@
import sys
import time
import discord
import re
import sqlite3
def airport_guessr(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"I got the airport in (\d) guess.*? today:"
FAIL_PATTERN = r"I did not guess the airport today:"
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM AIRPORT_GUESSR WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
match = re.search(FAIL_PATTERN, message.content.strip())
if not match:
match = re.search(PATTERN, message.content.strip())
guesses = match.group(1)
total_guesses += int(guesses) * reverse
wins += 1 * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO AIRPORT_GUESSR VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()

126
gamelogic/connections.py Normal file
View file

@ -0,0 +1,126 @@
from collections import Counter
import discord
import sqlite3
import sys
import time
import re
def connections(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM CONNECTIONS WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
total_guesses = 0
total_games = 0
wins = 0
perfects = 0
if data:
total_games = data[1]
wins = data[2]
perfects = data[3]
total_guesses = data[6]
sync_time = data[8]
if sync_time > new_sync_time and reverse == 1:
return
color_grid = extract_connections_grid( message.content)
if color_grid:
guesses = check_connections_win(color_grid)
total_games += 1 * reverse
if guesses > 0:
total_guesses += guesses * reverse
wins += 1 * reverse
perfect_game = is_perfect_game(color_grid)
if perfect_game:
perfects += 1 * reverse
else:
print("Connections: Failed parsing color grid")
print(message.content)
return
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
perfect_rate = (perfects / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO CONNECTIONS VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,
total_games,
wins,
perfects,
win_rate,
perfect_rate,
total_guesses,
average_guesses,
new_sync_time,
))
conn.commit()
conn.close()
def extract_connections_grid(message):
message_content = message.strip()
# Extract the puzzle number
puzzle_number_start = message_content.find("Puzzle #") + len("Puzzle #")
puzzle_number_end = message_content.find("\n", puzzle_number_start)
puzzle_number = message_content[puzzle_number_start:puzzle_number_end].strip()
# Extract the grid (assume the grid starts after the first two lines)
grid_start = message_content.find("\n", message_content.find("Puzzle #")) + 1
grid_content = message_content[grid_start:].strip()
# Split the grid into lines
color_grid = grid_content.split("\n")
return color_grid
def check_connections_win(color_grid):
"""
Check if the Connections game is a win and return the number of guesses.
A win is when there are exactly 4 groups of 4 colors each.
"""
# Flatten the color grid into a list of colors
uniform_rows = [row for row in color_grid if len(set(row)) == 1]
flattened_grid = ''.join(uniform_rows)
# Count the occurrences of each color in the grid
color_counts = Counter(flattened_grid)
# Check if there are exactly 4 different colors with 4 squares each
if len(color_counts) == 4 and all(count == 4 for count in color_counts.values()):
# A win: return the number of guesses (lines in the grid)
return len(color_grid)
return 0 # Not a win
def is_perfect_game(color_grid):
"""
Check if the Connections game is a perfect game (solved in 4 guesses, no mistakes).
"""
# A perfect game is one where the first 4 lines form perfect groups
if len(color_grid) == 4 and all(len(set(line)) == 1 for line in color_grid):
return True # It's a perfect game
return False

62
gamelogic/flagle.py Normal file
View file

@ -0,0 +1,62 @@
import sys
import time
import discord
import re
import sqlite3
def flagle(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"#Flagle #[0-9]+ \(.*\) (.)/6"
match = re.match(PATTERN, message.content.strip())
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM FLAGLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
guesses = match.group(1)
if guesses != 'X':
total_guesses += int(guesses) * reverse
wins += 1 * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO FLAGLE VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()

62
gamelogic/genshindle.py Normal file
View file

@ -0,0 +1,62 @@
import sys
import time
import discord
import re
import sqlite3
def genshindle(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"I found today's #Genshindle in (\d) tr.*?!"
FAIL_PATTERN = r"I couldn't find today's #Genshindle"
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM GENSHINDLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
match = re.search(FAIL_PATTERN, message.content.strip())
if not match:
match = re.search(PATTERN, message.content.strip())
guesses = match.group(1)
total_guesses += int(guesses) * reverse
wins += 1 * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO GENSHINDLE VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()

60
gamelogic/globle.py Normal file
View file

@ -0,0 +1,60 @@
import sys
import time
import discord
import re
import sqlite3
def globle(message : discord.message, reverse, bad = False):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
if bad:
PATTERN = r".* = ([0-9]+)"
else:
PATTERN = r"I guessed todays Globle in ([0-9]+) tries:"
match = re.search(PATTERN, message.content.strip())
if not match:
print("AAAAA")
print(message.content)
return
total_guesses = 0
total_games = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM GLOBLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
total_guesses = data[2]
sync_time = data[4]
if sync_time > new_sync_time and reverse == 1:
return
guesses = match.group(1)
total_guesses += int(guesses) * reverse
total_games += 1 * reverse
average_guesses = total_guesses / total_games
cursor.execute('''INSERT OR REPLACE INTO GLOBLE VALUES(?, ?, ?, ?, ?)''',
(message.author.id,total_games,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()
def bad_globle(message : discord.message, reverse):
globle(message, reverse, True)

62
gamelogic/planespottle.py Normal file
View file

@ -0,0 +1,62 @@
import sys
import time
import discord
import re
import sqlite3
def planespottle(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"Planespottle #[0-9]+ in (\d)/5"
FAIL_PATTERN = r"Planespottle #[0-9]+ failed to guess!"
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM PLANESPOTTLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
match = re.search(FAIL_PATTERN, message.content.strip())
if not match:
match = re.search(PATTERN, message.content.strip())
guesses = match.group(1)
total_guesses += int(guesses) * reverse
wins += 1 * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO PLANESPOTTLE VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()

61
gamelogic/satle.py Normal file
View file

@ -0,0 +1,61 @@
import sys
import time
import discord
import re
import sqlite3
def satle(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"🛰Satle #[0-9]+ (\d)/6"
match = re.match(PATTERN, message.content.strip())
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM SATLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
lines = message.content.splitlines()
match = re.match(PATTERN, lines[0].strip())
guesses = match.group(1)
if lines[1].__contains__("🟩"):
wins += 1 * reverse
total_guesses += int(guesses) * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO SATLE VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()

177
gamelogic/wheretaken.py Normal file
View file

@ -0,0 +1,177 @@
import sys
import time
import discord
import re
import sqlite3
def wheretaken(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
total_distance = 0
total_points = 0
total_games = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
cursor.execute('''SELECT * FROM WHERETAKEN WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
total_distance = data[2]
total_points = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
distances, points = parse_distances_and_points(message.content)
avg_distance = calculate_average(distances)
total_distance += avg_distance * reverse
total_points += sum(points) * reverse
total_games += 1 * reverse
average_distance = total_distance / total_games
average_points = total_points / total_games
cursor.execute('''INSERT OR REPLACE INTO WHERETAKEN VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,
total_games,
total_distance,
average_distance,
total_points,
average_points,
new_sync_time
))
conn.commit()
conn.close()
def whentaken(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
total_distance = 0
total_points = 0
total_games = 0
total_years = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
cursor.execute('''SELECT * FROM WHENTAKEN WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
total_distance = data[2]
total_points = data[4]
total_years = data[6]
sync_time = data[8]
if sync_time > new_sync_time and reverse == 1:
return
distances, points, years = parse_distances_points_years(message.content)
avg_distance = calculate_average(distances)
avg_years = calculate_average(years)
total_years += avg_years
total_distance += avg_distance * reverse
total_points += sum(points) * reverse
total_games += 1 * reverse
average_distance = total_distance / total_games
average_points = total_points / total_games
average_years = total_years / total_games
cursor.execute('''INSERT OR REPLACE INTO WHENTAKEN VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,
total_games,
total_distance,
average_distance,
total_points,
average_points,
total_years,
average_years,
new_sync_time
))
conn.commit()
conn.close()
def parse_distances_points_years(message):
pattern = r'.{4}([0-9]*\.?[0-9]+)([A-Za-z]?) ([A-Za-z]+) - ..([0-9]+) yrs - .([0-9]+)/200'
matches = re.findall(pattern, message)
converted_distances = []
points = []
years = []
for distance, thousands, unit, year, point in matches:
# Convert distance to float
distance = float(distance)
# Handle 'K' (thousands) notation
if thousands:
distance *= 1000 # Convert K to actual kilometers
# If the unit is meters, convert to kilometers
if unit == 'm':
distance /= 1000 # Convert meters to kilometers
# Store the distance and corresponding points
converted_distances.append(distance)
points.append(int(point))
years.append(int(year))
return converted_distances, points, years
def parse_distances_and_points(message):
# Find all the distances in the format of 'xxx km' or 'xxx m' and their corresponding points
pattern = r'(\d+(\.\d+)?)\s*(km|m|K)\s*(km)?\s*[^a-zA-Z0-9]*\s*(\d+)\s*/\s*\d+'
matches = re.findall(pattern, message)
converted_distances = []
points = []
for distance, _, unit, _, point in matches:
# Convert distance to float
distance = float(distance)
# Handle 'K' (thousands) notation
if unit == 'K':
distance *= 1000 # Convert K to actual kilometers
# If the unit is meters, convert to kilometers
elif unit == 'm':
distance /= 1000 # Convert meters to kilometers
# Store the distance and corresponding points
converted_distances.append(distance)
points.append(int(point))
return converted_distances, points
def calculate_average(distances):
# Calculate the average of the distances
if distances:
return sum(distances) / len(distances)
return 0

61
gamelogic/wordle.py Normal file
View file

@ -0,0 +1,61 @@
import sys
import time
import discord
import re
import sqlite3
def wordle(message : discord.message, reverse):
if reverse == True:
reverse = -1
else:
reverse = 1
conn = sqlite3.connect('stats.db')
cursor = conn.cursor()
PATTERN = r"^Wordle (\d{1,3}(?:,\d{3})*) (X|\d+)/(\d+)"
match = re.match(PATTERN, message.content.strip())
total_guesses = 0
total_games = 0
wins = 0
sync_time = 0
new_sync_time = (int)(time.mktime(message.created_at.timetuple()))
cursor.execute('''SELECT * FROM WORDLE WHERE NAME = ?''', (message.author.id,))
data = cursor.fetchone()
if data:
total_games = data[1]
wins = data[2]
total_guesses = data[4]
sync_time = data[6]
if sync_time > new_sync_time and reverse == 1:
return
guesses = match.group(2)
if guesses != 'X':
total_guesses += int(guesses) * reverse
wins += 1 * reverse
total_games += 1 * reverse
if wins != 0:
average_guesses = total_guesses / wins
else:
average_guesses = 0
win_rate = (wins / total_games) * 100
cursor.execute('''INSERT OR REPLACE INTO WORDLE VALUES(?, ?, ?, ?, ?, ?, ?)''',
(message.author.id,total_games,wins,win_rate,total_guesses,average_guesses,new_sync_time))
conn.commit()
conn.close()