commit
4099c0fafc
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -347,4 +347,8 @@ ids
|
||||||
|
|
||||||
# database
|
# database
|
||||||
matteo.db
|
matteo.db
|
||||||
|
matteo.db-wal
|
||||||
|
matteo.db-shm
|
||||||
/matteo_env/Lib/site-packages/flask_socketio/__init__.py
|
/matteo_env/Lib/site-packages/flask_socketio/__init__.py
|
||||||
|
|
||||||
|
env
|
||||||
|
|
|
@ -70,12 +70,12 @@ accepting pull requests, check the issues for to-dos.
|
||||||
- use this command at the top of a list with entries separated by new lines:
|
- use this command at the top of a list with entries separated by new lines:
|
||||||
- the away team's name.
|
- the away team's name.
|
||||||
- the home team's name.
|
- the home team's name.
|
||||||
- optionally, the number of innings, which must be greater than 2 and less than 31. if not included it will default to 9.
|
- optionally, the number of innings, which must be greater than 2 and less than 201. if not included it will default to 9.
|
||||||
- this command has fuzzy search so you don't need to type the full name of the team as long as you give enough to identify the team you're looking for.
|
- this command has fuzzy search so you don't need to type the full name of the team as long as you give enough to identify the team you're looking for.
|
||||||
- m;randomgame
|
- m;randomgame
|
||||||
- starts a 9-inning game between 2 entirely random teams. embrace chaos!
|
- starts a 9-inning game between 2 entirely random teams. embrace chaos!
|
||||||
- m;starttournament --rounddelay #
|
- m;starttournament --rounddelay #
|
||||||
- starts a randomly seeded tournament with up to 64 provided teams, automatically adding byes as necessary. all series have a 5 minute break between games. the current format is: best of 5 until the finals which are best of 7.
|
- starts a randomly seeded tournament with the provided teams, automatically adding byes as necessary. all series have a 5 minute break between games. the current format is: best of 5 until the finals which are best of 7.
|
||||||
- the --rounddelay is optional, if used, # must be between 1 and 120 and it'll set the delay between rounds to be # minutes. if not included it will default to 10.
|
- the --rounddelay is optional, if used, # must be between 1 and 120 and it'll set the delay between rounds to be # minutes. if not included it will default to 10.
|
||||||
- use this command at the top of a list with entries separated by new lines:
|
- use this command at the top of a list with entries separated by new lines:
|
||||||
- the name of the tournament.
|
- the name of the tournament.
|
||||||
|
@ -96,3 +96,5 @@ these folks are helping me a *ton* via patreon, and i cannot possibly thank them
|
||||||
- Chris Denmark
|
- Chris Denmark
|
||||||
- Astrid Bek
|
- Astrid Bek
|
||||||
- Kameleon
|
- Kameleon
|
||||||
|
- Ryan Littleton
|
||||||
|
- Evie Diver
|
||||||
|
|
24
database.py
24
database.py
|
@ -2,12 +2,17 @@
|
||||||
import os, json, datetime, re
|
import os, json, datetime, re
|
||||||
import sqlite3 as sql
|
import sqlite3 as sql
|
||||||
|
|
||||||
|
data_dir = "data"
|
||||||
|
|
||||||
def create_connection():
|
def create_connection():
|
||||||
#create connection, create db if doesn't exist
|
#create connection, create db if doesn't exist
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
conn = sql.connect("matteo.db")
|
conn = sql.connect(os.path.join(data_dir, "matteo.db"))
|
||||||
|
|
||||||
|
# enable write-ahead log for performance and resilience
|
||||||
|
conn.execute('pragma journal_mode=wal')
|
||||||
|
|
||||||
return conn
|
return conn
|
||||||
except:
|
except:
|
||||||
print("oops, db connection no work")
|
print("oops, db connection no work")
|
||||||
|
@ -181,9 +186,13 @@ def get_user_player_conn(conn, user):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
print(conn)
|
conn.close()
|
||||||
|
return False
|
||||||
except:
|
except:
|
||||||
print(conn)
|
conn.close()
|
||||||
|
return False
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
|
||||||
def get_user_player(user):
|
def get_user_player(user):
|
||||||
conn = create_connection()
|
conn = create_connection()
|
||||||
|
@ -206,6 +215,8 @@ def save_team(name, team_json_string, user_id):
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
|
||||||
def update_team(name, team_json_string):
|
def update_team(name, team_json_string):
|
||||||
conn = create_connection()
|
conn = create_connection()
|
||||||
|
@ -220,7 +231,10 @@ def update_team(name, team_json_string):
|
||||||
conn.close()
|
conn.close()
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
|
conn.close()
|
||||||
return False
|
return False
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
|
||||||
def get_team(name, owner=False):
|
def get_team(name, owner=False):
|
||||||
conn = create_connection()
|
conn = create_connection()
|
||||||
|
@ -253,6 +267,8 @@ def delete_team(team):
|
||||||
except:
|
except:
|
||||||
conn.close()
|
conn.close()
|
||||||
return False
|
return False
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
|
||||||
def assign_owner(team_name, owner_id):
|
def assign_owner(team_name, owner_id):
|
||||||
conn = create_connection()
|
conn = create_connection()
|
||||||
|
@ -266,6 +282,8 @@ def assign_owner(team_name, owner_id):
|
||||||
except:
|
except:
|
||||||
conn.close()
|
conn.close()
|
||||||
return False
|
return False
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
|
||||||
def get_all_teams():
|
def get_all_teams():
|
||||||
conn = create_connection()
|
conn = create_connection()
|
||||||
|
|
17
games.py
17
games.py
|
@ -2,8 +2,13 @@ import json, random, os, math, jsonpickle
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import database as db
|
import database as db
|
||||||
|
|
||||||
|
data_dir = "data"
|
||||||
|
games_config_file = os.path.join(data_dir, "games_config.json")
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
if not os.path.exists("games_config.json"):
|
if not os.path.exists(os.path.dirname(games_config_file)):
|
||||||
|
os.makedirs(os.path.dirname(games_config_file))
|
||||||
|
if not os.path.exists(games_config_file):
|
||||||
#generate default config
|
#generate default config
|
||||||
config_dic = {
|
config_dic = {
|
||||||
"default_length" : 3,
|
"default_length" : 3,
|
||||||
|
@ -16,11 +21,11 @@ def config():
|
||||||
"stolen_base_chance_mod" : 1,
|
"stolen_base_chance_mod" : 1,
|
||||||
"stolen_base_success_mod" : 1
|
"stolen_base_success_mod" : 1
|
||||||
}
|
}
|
||||||
with open("games_config.json", "w") as config_file:
|
with open(games_config_file, "w") as config_file:
|
||||||
json.dump(config_dic, config_file, indent=4)
|
json.dump(config_dic, config_file, indent=4)
|
||||||
return config_dic
|
return config_dic
|
||||||
else:
|
else:
|
||||||
with open("games_config.json") as config_file:
|
with open(games_config_file) as config_file:
|
||||||
return json.load(config_file)
|
return json.load(config_file)
|
||||||
|
|
||||||
def all_weathers():
|
def all_weathers():
|
||||||
|
@ -45,7 +50,7 @@ class appearance_outcomes(Enum):
|
||||||
single = "hits a single!"
|
single = "hits a single!"
|
||||||
double = "hits a double!"
|
double = "hits a double!"
|
||||||
triple = "hits a triple!"
|
triple = "hits a triple!"
|
||||||
homerun = "hits a home run!"
|
homerun = "hits a dinger!"
|
||||||
grandslam = "hits a grand slam!"
|
grandslam = "hits a grand slam!"
|
||||||
|
|
||||||
|
|
||||||
|
@ -502,7 +507,7 @@ class game(object):
|
||||||
|
|
||||||
if self.weather.name == "Slight Tailwind" and "mulligan" not in self.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk:
|
if self.weather.name == "Slight Tailwind" and "mulligan" not in self.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk:
|
||||||
mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1
|
mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1
|
||||||
if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] >= 5:
|
if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5:
|
||||||
result["mulligan"] = True
|
result["mulligan"] = True
|
||||||
return (result, 0)
|
return (result, 0)
|
||||||
|
|
||||||
|
@ -801,4 +806,4 @@ class weather(object):
|
||||||
self.counter_home = 0
|
self.counter_home = 0
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.emoji} {self.name}"
|
return f"{self.emoji} {self.name}"
|
||||||
|
|
44
onomancer.py
44
onomancer.py
|
@ -1,12 +1,29 @@
|
||||||
#interfaces with onomancer
|
#interfaces with onomancer
|
||||||
|
|
||||||
import requests, json, urllib
|
import requests, json, urllib
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from requests.packages.urllib3.util.retry import Retry
|
||||||
import database as db
|
import database as db
|
||||||
|
|
||||||
|
|
||||||
onomancer_url = "https://onomancer.sibr.dev/api/"
|
onomancer_url = "https://onomancer.sibr.dev/api/"
|
||||||
name_stats_hook = "getOrGenerateStats?name="
|
name_stats_hook = "getOrGenerateStats?name="
|
||||||
collection_hook = "getCollection?token="
|
collection_hook = "getCollection?token="
|
||||||
|
names_hook = "getNames"
|
||||||
|
|
||||||
|
|
||||||
|
def _retry_session(retries=3, backoff=0.3, status=(500, 501, 502, 503, 504)):
|
||||||
|
session = requests.Session()
|
||||||
|
retry = Retry(
|
||||||
|
total=retries,
|
||||||
|
read=retries,
|
||||||
|
connect=retries,
|
||||||
|
backoff_factor=backoff,
|
||||||
|
status_forcelist=status,
|
||||||
|
)
|
||||||
|
adapter = HTTPAdapter(max_retries=retry)
|
||||||
|
session.mount('https://', adapter)
|
||||||
|
return session
|
||||||
|
|
||||||
def get_stats(name):
|
def get_stats(name):
|
||||||
player = db.get_stats(name)
|
player = db.get_stats(name)
|
||||||
|
@ -14,7 +31,7 @@ def get_stats(name):
|
||||||
return player #returns json_string
|
return player #returns json_string
|
||||||
|
|
||||||
#yell at onomancer if not in cache or too old
|
#yell at onomancer if not in cache or too old
|
||||||
response = requests.get(onomancer_url + name_stats_hook + urllib.parse.quote_plus(name))
|
response = _retry_session().get(onomancer_url + name_stats_hook + urllib.parse.quote_plus(name))
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
stats = json.dumps(response.json())
|
stats = json.dumps(response.json())
|
||||||
db.cache_stats(name, stats)
|
db.cache_stats(name, stats)
|
||||||
|
@ -30,9 +47,32 @@ def get_scream(username):
|
||||||
return scream
|
return scream
|
||||||
|
|
||||||
def get_collection(collection_url):
|
def get_collection(collection_url):
|
||||||
response = requests.get(onomancer_url + collection_hook + urllib.parse.quote(collection_url))
|
response = _retry_session().get(onomancer_url + collection_hook + urllib.parse.quote(collection_url))
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
for player in response.json()['lineup'] + response.json()['rotation']:
|
for player in response.json()['lineup'] + response.json()['rotation']:
|
||||||
db.cache_stats(player['name'], json.dumps(player))
|
db.cache_stats(player['name'], json.dumps(player))
|
||||||
|
|
||||||
return json.dumps(response.json())
|
return json.dumps(response.json())
|
||||||
|
|
||||||
|
|
||||||
|
def get_names(limit=20, threshold=1):
|
||||||
|
"""
|
||||||
|
Get `limit` random players that have at least `threshold` upvotes.
|
||||||
|
Returns dictionary keyed by player name of stats.
|
||||||
|
"""
|
||||||
|
response = _retry_session().get(
|
||||||
|
onomancer_url + names_hook,
|
||||||
|
params={
|
||||||
|
'limit': limit,
|
||||||
|
'threshold': threshold,
|
||||||
|
'with_stats': 1,
|
||||||
|
'random': 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
res = {}
|
||||||
|
for stats in response.json():
|
||||||
|
name = stats['name']
|
||||||
|
db.cache_stats(name, json.dumps(stats))
|
||||||
|
res[name] = stats
|
||||||
|
return res
|
||||||
|
|
153
the_draft.py
Normal file
153
the_draft.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
from collections import namedtuple
|
||||||
|
import games
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import onomancer
|
||||||
|
|
||||||
|
DRAFT_SIZE = 20
|
||||||
|
REFRESH_DRAFT_SIZE = 4 # fewer players remaining than this and the list refreshes
|
||||||
|
DRAFT_ROUNDS = 13
|
||||||
|
|
||||||
|
Participant = namedtuple('Participant', ['handle', 'team'])
|
||||||
|
BOOKMARK = Participant(handle="bookmark", team=None) # keep track of start/end of draft round
|
||||||
|
|
||||||
|
|
||||||
|
class Draft:
|
||||||
|
"""
|
||||||
|
Represents a draft party with n participants constructing their team from a pool
|
||||||
|
of names.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def make_draft(cls):
|
||||||
|
draft = cls()
|
||||||
|
return draft
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._id = str(uuid.uuid4())[:6]
|
||||||
|
self._participants = []
|
||||||
|
self._active_participant = BOOKMARK # draft mutex
|
||||||
|
self._players = onomancer.get_names(limit=DRAFT_SIZE)
|
||||||
|
self._round = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def round(self):
|
||||||
|
"""
|
||||||
|
Current draft round. 1 indexed.
|
||||||
|
"""
|
||||||
|
return self._round
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_drafter(self):
|
||||||
|
"""
|
||||||
|
Handle of whomever is currently up to draft.
|
||||||
|
"""
|
||||||
|
return self._active_participant.handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_drafting_team(self):
|
||||||
|
return self._active_participant.team.name
|
||||||
|
|
||||||
|
def add_participant(self, handle, team_name, slogan):
|
||||||
|
"""
|
||||||
|
A participant is someone participating in this draft. Initializes an empty team for them
|
||||||
|
in memory.
|
||||||
|
|
||||||
|
`handle`: discord @ handle, for ownership and identification
|
||||||
|
"""
|
||||||
|
team = games.team()
|
||||||
|
team.name = team_name
|
||||||
|
team.slogan = slogan
|
||||||
|
self._participants.append(Participant(handle=handle, team=team))
|
||||||
|
|
||||||
|
def start_draft(self):
|
||||||
|
"""
|
||||||
|
Call after adding all participants and confirming they're good to go.
|
||||||
|
"""
|
||||||
|
self.advance_draft()
|
||||||
|
|
||||||
|
def refresh_players(self):
|
||||||
|
self._players = onomancer.get_names(limit=DRAFT_SIZE)
|
||||||
|
|
||||||
|
def advance_draft(self):
|
||||||
|
"""
|
||||||
|
The participant list is treated as a circular queue with the head being popped off
|
||||||
|
to act as the draftign mutex.
|
||||||
|
"""
|
||||||
|
if self._active_participant == BOOKMARK:
|
||||||
|
self._round += 1
|
||||||
|
self._participants.append(self._active_participant)
|
||||||
|
self._active_participant = self._participants.pop(0)
|
||||||
|
|
||||||
|
def get_draftees(self):
|
||||||
|
return list(self._players.keys())
|
||||||
|
|
||||||
|
def draft_player(self, handle, player_name):
|
||||||
|
"""
|
||||||
|
`handle` is the participant's discord handle.
|
||||||
|
"""
|
||||||
|
if self._active_participant.handle != handle:
|
||||||
|
raise ValueError(f'{self._active_participant.handle} is drafting, not you')
|
||||||
|
|
||||||
|
player_name = player_name.strip()
|
||||||
|
|
||||||
|
player = self._players.get(player_name)
|
||||||
|
if not player:
|
||||||
|
# might be some whitespace shenanigans
|
||||||
|
for name, stats in self._players.items():
|
||||||
|
if name.replace('\xa0', ' ').strip().lower() == player_name.lower():
|
||||||
|
player = stats
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# still not found
|
||||||
|
raise ValueError(f'Player `{player_name}` not in draft list')
|
||||||
|
del self._players[player['name']]
|
||||||
|
|
||||||
|
if len(self._players) <= REFRESH_DRAFT_SIZE:
|
||||||
|
self.refresh_players()
|
||||||
|
|
||||||
|
if self._round < DRAFT_ROUNDS:
|
||||||
|
self._active_participant.team.add_lineup(games.player(json.dumps(player)))
|
||||||
|
elif self._round == DRAFT_ROUNDS:
|
||||||
|
self._active_participant.team.add_pitcher(games.player(json.dumps(player)))
|
||||||
|
|
||||||
|
self.advance_draft()
|
||||||
|
if self._active_participant == BOOKMARK:
|
||||||
|
self.advance_draft()
|
||||||
|
|
||||||
|
return player
|
||||||
|
|
||||||
|
def get_teams(self):
|
||||||
|
teams = []
|
||||||
|
if self._active_participant != BOOKMARK:
|
||||||
|
teams.append((self._active_participant.handle, self._active_participant.team))
|
||||||
|
for participant in self._participants:
|
||||||
|
if participant != BOOKMARK:
|
||||||
|
teams.append((participant.handle, participant.team))
|
||||||
|
return teams
|
||||||
|
|
||||||
|
def finish_draft(self):
|
||||||
|
for handle, team in self.get_teams():
|
||||||
|
success = games.save_team(team, int(handle[3:-1]))
|
||||||
|
if not success:
|
||||||
|
raise Exception(f'Error saving team for {handle}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# extremely robust testing OC do not steal
|
||||||
|
DRAFT_ROUNDS = 2
|
||||||
|
draft = Draft.make_draft()
|
||||||
|
draft.add_participant('@bluh', 'Bluhstein Bluhs', 'bluh bluh bluh')
|
||||||
|
draft.add_participant('@what', 'Barcelona IDK', 'huh')
|
||||||
|
draft.start_draft()
|
||||||
|
|
||||||
|
while draft.round <= DRAFT_ROUNDS:
|
||||||
|
print(draft.get_draftees())
|
||||||
|
cmd = input(f'{draft.round} {draft.active_drafter}:')
|
||||||
|
drafter, player = cmd.split(' ', 1)
|
||||||
|
try:
|
||||||
|
draft.draft_player(drafter, player)
|
||||||
|
except ValueError as e:
|
||||||
|
print(e)
|
||||||
|
print(draft.get_teams())
|
184
the_prestige.py
184
the_prestige.py
|
@ -1,9 +1,13 @@
|
||||||
import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues
|
import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues
|
||||||
import database as db
|
import database as db
|
||||||
import onomancer as ono
|
import onomancer as ono
|
||||||
|
import random
|
||||||
|
from the_draft import Draft, DRAFT_ROUNDS
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
data_dir = "data"
|
||||||
|
config_filename = os.path.join(data_dir, "config.json")
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
def isauthorized(self, user):
|
def isauthorized(self, user):
|
||||||
|
@ -12,6 +16,12 @@ class Command:
|
||||||
async def execute(self, msg, command):
|
async def execute(self, msg, command):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
class DraftError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SlowDraftError(DraftError):
|
||||||
|
pass
|
||||||
|
|
||||||
class CommandError(Exception):
|
class CommandError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -109,10 +119,11 @@ class ShowPlayerCommand(Command):
|
||||||
class StartGameCommand(Command):
|
class StartGameCommand(Command):
|
||||||
name = "startgame"
|
name = "startgame"
|
||||||
template = "m;startgame [away] [home] [innings]"
|
template = "m;startgame [away] [home] [innings]"
|
||||||
description ="""Starts a game with premade teams made using saveteam, use this command at the top of a list followed by each of these in a new line (shift+enter in discord, or copy+paste from notepad) (this command has fuzzy search so you don't need to type the full name of the team as long as you give enough to identify the team you're looking for.):
|
description ="""Starts a game with premade teams made using saveteam, use this command at the top of a list followed by each of these in a new line (shift+enter in discord, or copy+paste from notepad):
|
||||||
- the away team's name.
|
- the away team's name.
|
||||||
- the home team's name.
|
- the home team's name.
|
||||||
- and finally, optionally, the number of innings, which must be greater than 2 and less than 31. if not included it will default to 9."""
|
- and finally, optionally, the number of innings, which must be greater than 2 and less than 201. if not included it will default to 9.
|
||||||
|
- this command has fuzzy search so you don't need to type the full name of the team as long as you give enough to identify the team you're looking for."""
|
||||||
|
|
||||||
async def execute(self, msg, command):
|
async def execute(self, msg, command):
|
||||||
league = None
|
league = None
|
||||||
|
@ -479,7 +490,6 @@ class AssignOwnerCommand(Command):
|
||||||
async def execute(self, msg, command):
|
async def execute(self, msg, command):
|
||||||
new_owner = msg.mentions[0]
|
new_owner = msg.mentions[0]
|
||||||
team_name = command.strip().split(new_owner.mention+" ")[1]
|
team_name = command.strip().split(new_owner.mention+" ")[1]
|
||||||
print(team_name)
|
|
||||||
if db.assign_owner(team_name, new_owner.id):
|
if db.assign_owner(team_name, new_owner.id):
|
||||||
await msg.channel.send(f"{team_name} is now owned by {new_owner.display_name}. Don't break it.")
|
await msg.channel.send(f"{team_name} is now owned by {new_owner.display_name}. Don't break it.")
|
||||||
else:
|
else:
|
||||||
|
@ -490,7 +500,7 @@ class StartTournamentCommand(Command):
|
||||||
template = """m;starttournament
|
template = """m;starttournament
|
||||||
[tournament name]
|
[tournament name]
|
||||||
[list of teams, each on a new line]"""
|
[list of teams, each on a new line]"""
|
||||||
description = "Starts a randomly seeded tournament with up to 64 provided teams, automatically adding byes as necessary. All series have a 5 minute break between games and by default there is a 10 minute break between rounds. The current tournament format is:\nBest of 5 until the finals, which are Best of 7."
|
description = "Starts a randomly seeded tournament with the provided teams, automatically adding byes as necessary. All series have a 5 minute break between games and by default there is a 10 minute break between rounds. The current tournament format is:\nBest of 5 until the finals, which are Best of 7."
|
||||||
|
|
||||||
async def execute(self, msg, command):
|
async def execute(self, msg, command):
|
||||||
if config()["game_freeze"]:
|
if config()["game_freeze"]:
|
||||||
|
@ -570,6 +580,152 @@ class StartTournamentCommand(Command):
|
||||||
await start_tournament_round(channel, tourney)
|
await start_tournament_round(channel, tourney)
|
||||||
|
|
||||||
|
|
||||||
|
class DraftPlayerCommand(Command):
|
||||||
|
name = "draft"
|
||||||
|
template = "m;draft [playername]"
|
||||||
|
description = "On your turn during a draft, use this command to pick your player."
|
||||||
|
|
||||||
|
async def execute(self, msg, command):
|
||||||
|
"""
|
||||||
|
This is a no-op definition. `StartDraftCommand` handles the orchestration directly,
|
||||||
|
this is just here to provide a help entry and so the command dispatcher recognizes it
|
||||||
|
as valid.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StartDraftCommand(Command):
|
||||||
|
name = "startdraft"
|
||||||
|
template = "m;startdraft [mention] [teamname] [slogan]"
|
||||||
|
description = """Starts a draft with an arbitrary number of participants. Send this command at the top of the list with each mention, teamname, and slogan on a new line (shift+enter in discord).
|
||||||
|
- The draft will proceed in the order that participants were entered.
|
||||||
|
- 20 players will be available for draft at a time, and the pool will refresh automatically when it becomes small.
|
||||||
|
- Each participant will be asked to draft 12 hitters then finally one pitcher.
|
||||||
|
- The draft will start only once every participant has given a 👍 to begin.
|
||||||
|
- use the command `d`, `draft`, or `m;draft` on your turn to draft someone
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def execute(self, msg, command):
|
||||||
|
draft = Draft.make_draft()
|
||||||
|
mentions = {f'<@!{m.id}>' for m in msg.mentions}
|
||||||
|
content = msg.content.split('\n')[1:] # drop command out of message
|
||||||
|
if len(content) % 3:
|
||||||
|
await msg.channel.send('Invalid list')
|
||||||
|
raise ValueError('Invalid length')
|
||||||
|
|
||||||
|
for i in range(0, len(content), 3):
|
||||||
|
handle_token = content[i].strip()
|
||||||
|
for mention in mentions:
|
||||||
|
if mention in handle_token:
|
||||||
|
handle = mention
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
await msg.channel.send(f"I don't recognize {handle_token}")
|
||||||
|
return
|
||||||
|
team_name = content[i + 1].strip()
|
||||||
|
if games.get_team(team_name):
|
||||||
|
await msg.channel.send(f'Sorry {handle}, {team_name} already exists')
|
||||||
|
return
|
||||||
|
slogan = content[i + 2].strip()
|
||||||
|
draft.add_participant(handle, team_name, slogan)
|
||||||
|
|
||||||
|
success = await self.wait_start(msg.channel, mentions)
|
||||||
|
if not success:
|
||||||
|
return
|
||||||
|
|
||||||
|
draft.start_draft()
|
||||||
|
footer = f"The draft class of {random.randint(2007, 2075)}"
|
||||||
|
while draft.round <= DRAFT_ROUNDS:
|
||||||
|
message_prefix = f'Round {draft.round}/{DRAFT_ROUNDS}:'
|
||||||
|
if draft.round == DRAFT_ROUNDS:
|
||||||
|
body = random.choice([
|
||||||
|
f"Now just choose a pitcher and we can finish off this paperwork for you, {draft.active_drafter}",
|
||||||
|
f"Pick a pitcher, {draft.active_drafter}, and we can all go home happy. 'Cept your players. They'll have to play bllaseball.",
|
||||||
|
f"Almost done, {draft.active_drafter}. Pick your pitcher.",
|
||||||
|
])
|
||||||
|
message = f"⚾️ {message_prefix} {body}"
|
||||||
|
else:
|
||||||
|
body = random.choice([
|
||||||
|
f"Choose a batter, {draft.active_drafter}",
|
||||||
|
f"{draft.active_drafter}, your turn. Pick one.",
|
||||||
|
f"Pick one to fill your next lineup slot, {draft.active_drafter}",
|
||||||
|
f"Alright, {draft.active_drafter}, choose a batter.",
|
||||||
|
])
|
||||||
|
message = f"🏏 {message_prefix} {body}"
|
||||||
|
await msg.channel.send(
|
||||||
|
message,
|
||||||
|
embed=build_draft_embed(draft.get_draftees(), footer=footer),
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
draft_message = await self.wait_draft(msg.channel, draft)
|
||||||
|
draft.draft_player(f'<@!{draft_message.author.id}>', draft_message.content.split(' ', 1)[1])
|
||||||
|
except SlowDraftError:
|
||||||
|
player = random.choice(draft.get_draftees())
|
||||||
|
await msg.channel.send(f"I'm not waiting forever. You get {player}. Next.")
|
||||||
|
draft.draft_player(draft.active_drafter, player)
|
||||||
|
except ValueError as e:
|
||||||
|
await msg.channel.send(str(e))
|
||||||
|
except IndexError:
|
||||||
|
await msg.channel.send("Quit the funny business.")
|
||||||
|
|
||||||
|
for handle, team in draft.get_teams():
|
||||||
|
await msg.channel.send(
|
||||||
|
random.choice([
|
||||||
|
f"Done and dusted, {handle}. Here's your squad.",
|
||||||
|
f"Behold the {team.name}, {handle}. Flawless, we think.",
|
||||||
|
f"Oh, huh. Interesting stat distribution. Good luck, {handle}.",
|
||||||
|
]),
|
||||||
|
embed=build_team_embed(team),
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
draft.finish_draft()
|
||||||
|
except Exception as e:
|
||||||
|
await msg.channel.send(str(e))
|
||||||
|
|
||||||
|
async def wait_start(self, channel, mentions):
|
||||||
|
start_msg = await channel.send("Sound off, folks. 👍 if you're good to go " + " ".join(mentions))
|
||||||
|
await start_msg.add_reaction("👍")
|
||||||
|
await start_msg.add_reaction("👎")
|
||||||
|
|
||||||
|
def react_check(react, user):
|
||||||
|
return f'<@!{user.id}>' in mentions and react.message == start_msg
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
react, _ = await client.wait_for('reaction_add', timeout=60.0, check=react_check)
|
||||||
|
if react.emoji == "👎":
|
||||||
|
await channel.send("We dragged out the photocopier for this! Fine, putting it back.")
|
||||||
|
return False
|
||||||
|
if react.emoji == "👍":
|
||||||
|
reactors = set()
|
||||||
|
async for user in react.users():
|
||||||
|
reactors.add(f'<@!{user.id}>')
|
||||||
|
if reactors.intersection(mentions) == mentions:
|
||||||
|
return True
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
await channel.send("Y'all aren't ready.")
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def wait_draft(self, channel, draft):
|
||||||
|
|
||||||
|
def check(m):
|
||||||
|
if m.channel != channel:
|
||||||
|
return False
|
||||||
|
if m.content.startswith('d ') or m.content.startswith('draft '):
|
||||||
|
return True
|
||||||
|
for prefix in config()['prefix']:
|
||||||
|
if m.content.startswith(prefix + 'draft '):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
draft_message = await client.wait_for('message', timeout=120.0, check=check)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
raise SlowDraftError('Too slow')
|
||||||
|
return draft_message
|
||||||
|
|
||||||
|
|
||||||
commands = [
|
commands = [
|
||||||
IntroduceCommand(),
|
IntroduceCommand(),
|
||||||
CountActiveGamesCommand(),
|
CountActiveGamesCommand(),
|
||||||
|
@ -594,6 +750,8 @@ commands = [
|
||||||
CreditCommand(),
|
CreditCommand(),
|
||||||
RomanCommand(),
|
RomanCommand(),
|
||||||
HelpCommand(),
|
HelpCommand(),
|
||||||
|
StartDraftCommand(),
|
||||||
|
DraftPlayerCommand(),
|
||||||
]
|
]
|
||||||
|
|
||||||
client = discord.Client()
|
client = discord.Client()
|
||||||
|
@ -605,7 +763,9 @@ thread1 = threading.Thread(target=main_controller.update_loop)
|
||||||
thread1.start()
|
thread1.start()
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
if not os.path.exists("config.json"):
|
if not os.path.exists(os.path.dirname(config_filename)):
|
||||||
|
os.makedirs(os.path.dirname(config_filename))
|
||||||
|
if not os.path.exists(config_filename):
|
||||||
#generate default config
|
#generate default config
|
||||||
config_dic = {
|
config_dic = {
|
||||||
"token" : "",
|
"token" : "",
|
||||||
|
@ -617,12 +777,12 @@ def config():
|
||||||
"soulscream channel id" : 0,
|
"soulscream channel id" : 0,
|
||||||
"game_freeze" : 0
|
"game_freeze" : 0
|
||||||
}
|
}
|
||||||
with open("config.json", "w") as config_file:
|
with open(config_filename, "w") as config_file:
|
||||||
json.dump(config_dic, config_file, indent=4)
|
json.dump(config_dic, config_file, indent=4)
|
||||||
print("please fill in bot token and any bot admin discord ids to the new config.json file!")
|
print("please fill in bot token and any bot admin discord ids to the new config.json file!")
|
||||||
quit()
|
quit()
|
||||||
else:
|
else:
|
||||||
with open("config.json") as config_file:
|
with open(config_filename) as config_file:
|
||||||
return json.load(config_file)
|
return json.load(config_file)
|
||||||
|
|
||||||
@client.event
|
@client.event
|
||||||
|
@ -1011,6 +1171,16 @@ async def team_delete_confirm(channel, team, owner):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def build_draft_embed(names, title="The Draft", footer="You must choose"):
|
||||||
|
embed = discord.Embed(color=discord.Color.purple(), title=title)
|
||||||
|
column_size = 7
|
||||||
|
for i in range(0, len(names), column_size):
|
||||||
|
draft = '\n'.join(names[i:i + column_size])
|
||||||
|
embed.add_field(name="-", value=draft, inline=True)
|
||||||
|
embed.set_footer(text=footer)
|
||||||
|
return embed
|
||||||
|
|
||||||
|
|
||||||
def build_team_embed(team):
|
def build_team_embed(team):
|
||||||
embed = discord.Embed(color=discord.Color.purple(), title=team.name)
|
embed = discord.Embed(color=discord.Color.purple(), title=team.name)
|
||||||
lineup_string = ""
|
lineup_string = ""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user