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 .env
**/__pycache__
tags tags
.ycm_extra_conf.py .ycm_extra_conf.py
stats.db

508
bot.py
View file

@ -1,11 +1,20 @@
# bot.py
import os import os
import datetime
import time
import db
import sqlite3
import discord import discord
from game_logic import connections as con
import re
from collections import Counter
from discord.ext import commands from discord.ext import commands
import gamelogic as gamlog
import re
from collections import Counter
from dotenv import load_dotenv from dotenv import load_dotenv
WORDLE_CHANNEL = 1317916234863480832 WORDLE_CHANNEL = 1317916234863480832
@ -14,54 +23,94 @@ MISC_GEOGRAPHY = 1317916442342981722
GLOBLE_CHANNEL = 1320505660701413511 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() load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN') 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( @bot.slash_command(
name="stats", name="stats",
description="Prints out your stats in a daily game, examples include wordle", description="Prints out your stats in a daily game, examples include wordle",
guild_ids=[261345598987436033] 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() 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(): match game.lower():
case "wordle": case "wordle":
channel = bot.get_channel(WORDLE_CHANNEL) total_games = data[1]
wins = data[2]
if user is None: win_rate = data[3]
user = interaction.user total_guesses = data[4]
average_guesses = data[5]
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
await interaction.followup.send( await interaction.followup.send(
f"Wordle stats for {user.mention}:\n" 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 ephemeral=False # Send message only to the user who called the command
) )
case "connections": case "connections":
channel = bot.get_channel(WORDLE_CHANNEL) total_games = data[1]
wins = data[2]
if user is None: perfects = data[3]
user = interaction.user win_rate = data[4]
perfect_rate = data[5]
CONNECTIONS_PATTERN = r"^Connections\nPuzzle #(\d+)\n((?:[\u2B1B\u2B1C\u2B1D\u2B1E\u2B20]{4}\n?)+)$" total_guesses = data[6]
average_guesses = data[7]
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
await interaction.followup.send( await interaction.followup.send(
f"Connections stats for {user.mention}:\n" 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 ephemeral=False # Send message only to the user who called the command
) )
case "satle": case "satle":
channel = bot.get_channel(MISC_GEOGRAPHY) total_games = data[1]
wins = data[2]
if user is None: win_rate = data[3]
user = interaction.user total_guesses = data[4]
average_guesses = data[5]
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
await interaction.followup.send( await interaction.followup.send(
f"Satle stats for {user.mention}:\n" 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 ephemeral=False # Send message only to the user who called the command
) )
case "globle": case "globle":
misc_channel = bot.get_channel(MISC_GEOGRAPHY) total_games = data[1]
globle_channel = bot.get_channel(GLOBLE_CHANNEL) total_guesses = data[2]
average_guesses = data[3]
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
await interaction.followup.send( await interaction.followup.send(
f"Globle stats for {user.mention}:\n" 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", f"Average Guesses per Game: {average_guesses:.2f}\n",
ephemeral=False # Send message only to the user who called the command 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 _: case _:
await interaction.followup.send( await interaction.followup.send(
"Not a game.", "Not a game.",
ephemeral=True 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 @bot.event
async def on_ready(): 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!') 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) 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()