Compare commits

...

15 Commits

Author SHA1 Message Date
e9b6ec4b5d seed random once on init 2023-04-15 11:44:59 +10:00
25a347cd88 leaguefacts 2023-04-15 11:44:39 +10:00
3b9f9d58a6 leaguefacts 2023-04-15 11:42:58 +10:00
55d227d05d fix db name 2023-04-10 10:05:53 +10:00
4f7a9b642e leaguefacts 2023-04-10 10:04:37 +10:00
57f249d028 leaguefacts 2023-04-10 09:47:07 +10:00
5d67b7aaa8 add game 5.3 2023-04-09 13:05:30 +10:00
49407f0c45 leaguefacts 2023-04-08 16:55:05 +10:00
fd4ac1d544 leaguefacts 2023-04-08 16:53:23 +10:00
73a31d7b99 add games 5.1 and 5.2 2023-04-08 16:53:08 +10:00
a8730ed579 leaguefacts 2023-04-08 09:50:32 +10:00
dfc1093b39 log complete update 2023-04-07 10:13:45 +10:00
29df16ecd3 fix move command 2023-04-07 10:13:11 +10:00
6b36720c37 add bot 2023-04-07 10:10:49 +10:00
39f412c0fe move main file 2023-04-07 10:10:49 +10:00
7 changed files with 161 additions and 6 deletions

3
.gitignore vendored
View File

@@ -8,5 +8,6 @@ tmp/
build
htmlcov
data.db
*.db
cache/
token

View File

@@ -11,8 +11,8 @@ fuck.
## Usage
```sh
$ ./main.py -h
usage: hhirlstats [-h] [-v] [-C] [-o FILE] replay [replay ...]
$ ./index.py -h
usage: hhirlstats [-h] [-v] [-C] [-t FILE] [-o FILE] replay [replay ...]
extracts stats from a Showdown replay
@@ -23,6 +23,8 @@ options:
-h, --help show this help message and exit
-v, --verbose add debugging info (default: None)
-C, --no-cache fetch replays instead of using cache (default: False)
-t FILE, --teams FILE
JSON file defining players to teams (default: teams.json)
-o FILE, --output FILE
output data file (default: data.db)
```

126
bot.py Executable file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python3
from discord.utils import setup_logging
from typing import Optional
import argparse
import discord
import logging
import os.path
import random
import re
import shutil
import subprocess as sp
discord.utils.setup_logging()
_log = logging.getLogger(__name__)
_GAMES = "games.txt"
_DB = "holy-heck.db"
_DB_DEST = f"/var/lib/grafana/{_DB}"
def _write_game(content: str):
try:
with open(_GAMES, "a") as f:
f.write(content)
f.write("\n")
except:
_log.exception(f"failed writing game {content}")
def _update_db():
try:
games = []
with open(_GAMES) as f:
for line in f:
games.append(line.strip())
sp.run(["./index.py", "-o", _DB] + games)
shutil.move(_DB, _DB_DEST)
_log.info("updated db")
except:
_log.exception(f"failed updating db")
class BotClient(discord.Client):
def __init__(
self,
intents: discord.Intents,
leaguefacts: Optional[list[str]] = None,
):
super().__init__(intents=intents)
self._leaguefacts = leaguefacts or []
random.seed()
async def on_ready(self):
_log.info(f"ready as {self.user}")
async def on_message(self, message: discord.Message):
content = message.content
if self.is_replay(message):
await self.on_replay(message)
elif self.is_leaguefact(message):
await self.on_leaguefact(message)
def is_replay(self, message: discord.Message) -> bool:
if re.match("https://replay.pokemonshowdown.com/dl-.*", message.content):
return True
return False
async def on_replay(self, message: discord.Message):
_log.info(f"Recognised {message.content} as a League game")
_write_game(message.content)
_update_db()
def is_leaguefact(self, message: discord.Message) -> bool:
return message.content.lower() in ["leaguefact", "leaguefacts"]
async def on_leaguefact(self, message: discord.Message):
_log.info("leaguefact requested")
fact = self._select_leaguefact()
if fact:
await message.channel.send(f"Did you know? {fact}")
else:
await message.channel.send("There are no league facts.")
def _select_leaguefact(self) -> Optional[str]:
if not self._leaguefacts:
return None
return random.choice(self._leaguefacts)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"-t",
"--token-file",
metavar="FILE",
default="token",
help="file containing Discord API token",
)
parser.add_argument(
"-f",
"--facts",
metavar="FILE",
default="facts.txt",
help="file containing leagefacts",
)
args = parser.parse_args()
facts = []
if os.path.exists(args.facts):
with open(args.facts) as f:
for line in f:
facts.append(line)
intents = discord.Intents.default()
intents.message_content = True
client = BotClient(leaguefacts=facts, intents=intents)
with open(args.token_file) as f:
token = f.read().strip()
client.run(token, log_handler=None)
if __name__ == "__main__":
main()

22
facts.txt Normal file
View File

@@ -0,0 +1,22 @@
The Canberra Mamoswines have fielded the most unique Pokémon, with 14. The Cheyenne Chimchars have fielded the least, with 6.
The Raleigh County Revavrooms have drafted the most unique Pokémon, with 18. The Cheyenne Chimchars have drafted the least, with 10.
Barrow's Biggest Birds have made the most free agency exchanges so far, with 2 exchanges totalling 6 members.
Despite their name, Barrow's Biggest Birds do not currently possess any of largest birds in season 3, and have only had at most three birds on their roster. They briefly had the tallest drafted bird, Quaquaval, but exchanged them in week 3.
The Raleigh County Revavrooms conducted the single largest free agency exchange of season 3 in week 3, replacing eight members of their roster.
The Canberra Mamoswines are the slowest team, with an average of 65 Speed across their roster. The Buenos Aires Aggrons are the fastest, with an average of 84.6 Speed.
The Nimbasa City Ninjasks have the greatest Speed differential between their fastest and slowest members, of 112 points. Emily's Eevees and the Raleigh County Revavrooms are tied for the lowest differential, of 72 points.
The Raleigh County Revavrooms have the largest roster, with up to 13 bodies across their 10 members. Prior to week 6, the Buenos Aires Aggrons had the most heads, with 14.
Emily's Eevees have the fewest limbs in season 3, with only 24. Only two of their members are in the Amporphous egg group.
The Nimbasa City Ninjasks has the most Toxic roster, with 2 Pokémon capable of learning the move.
Prior to week 6, Cerluedge had more kills than all other Fire-type Pokémon combined that aren't some kind of Volcarona, as befitting of its ability, Flash Fire.
Landorus is the only Force of Nature that has not been drafted in season 3.
Emily's Eevees is the only roster to not have a Pokémon with more than 100 Speed.
Most teams have an average member cost of 12 points. The San Francisco 549ers are most unevenly weighted team, with a standard distribution of 6.99 points. The Canberra Mamoswines are the most evenly weighted, with a standard distribution of 5.06 points.
Only one team does not have a regional or alternate variant of a Pokémon on their roster: the Nimbasa City Ninjasks.
The East Midland Milotics have the fattest roster, claiming both Dondozo and Bellibolt.
Three teams have more than one 2-cost Pokémon in their rosters: Barrow's Biggest Birds, Mew York, and the Nimbasa City Ninjasks, each with 2 members. Five teams have no 2-cost Pokémon at all.
Prior to week 6, the San Francisco 549ers had the smallest Paldean representation in season 3, with only one team member.
Moltres is the only legendary bird that has not been drafted in season 3.
Tyranitar, Metagross, and Kommo-o are the only pseudo-legendary Pokémon to not be drafted (either directly or as a variant) in season 3. Of these, only Metagross and Kommo-o are not available to be drafted.
Frosmoth is the biggest underdog in season 3 so far, having 4 knockouts to its name while only being a 2-point Pokémon.
The Buenos Aires Aggrons and Canberra Mamoswines are tied for having the largest type overlap on their rosters: the Aggrons have 4 Water-types, and the Mamoswines have 4 Electric-types.

View File

@@ -13,6 +13,7 @@
buildInputs =
let
python = pkgs.python3.withPackages (ps: [
ps.discordpy
ps.requests
]);
in

View File

@@ -18,3 +18,6 @@ https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-33751
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-33998
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-34087
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-34672
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-36025
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-36096
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-37058

View File

@@ -358,7 +358,7 @@ def fetch(replay: str, cache: bool = True) -> Replay:
return Replay(**data)
def main(args):
def main():
parser = argparse.ArgumentParser(
prog=APP,
description="extracts stats from a Showdown replay",
@@ -389,7 +389,7 @@ def main(args):
)
parser.add_argument("replay", nargs="+", help="replay ID or URL")
args = parser.parse_args(args)
args = parser.parse_args()
if args.verbose and args.verbose > 1:
LOG.setLevel(logging.TRACE)
elif args.verbose:
@@ -435,4 +435,4 @@ def main(args):
if __name__ == "__main__":
main(sys.argv[1:])
main()