Skip to content

Commit

Permalink
Add half baked sendou.ink support
Browse files Browse the repository at this point in the history
  • Loading branch information
vlee489 committed Dec 13, 2024
1 parent 908041e commit aa85882
Show file tree
Hide file tree
Showing 14 changed files with 1,371 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.13

WORKDIR /usr/src/app

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Managing the check-in command staff can use these subcommands:
MONGOURI=MongoDbURI
SENTRY="System Environment" # Optional
DEBUG=1 # Optional
SENDOU_API_KEY="Sendou.ink API Key"
```

Please know that there are no `true` or `false` values in `.env` files. If you want to set a key to false, set it to `0`
Expand Down
13 changes: 11 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
version: "3.8"
services:

bot:
build: .
env_file: .env
restart: unless-stopped

redis:
image: valkey/valkey:8-alpine
restart: unless-stopped
healthcheck:
test: [ "CMD-SHELL", "redis-cli ping | grep PONG" ]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- ./volumes/redis:/data
856 changes: 856 additions & 0 deletions pdm.lock

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[project]
name = "Radia"
version = "0.1.0"
description = "Default template for PDM package"
authors = [
{name = "Vincent Lee", email = "[email protected]"},
]
dependencies = [
"discord-py",
"asyncio",
"sentry-sdk",
"aiohttp",
"aiographql-client",
"motor",
"dnspython",
"pymongo",
"gspread",
"pandas",
"ics",
"pyyaml",
"sendou-py>=1.2.12",
"audioop-lts>=0.2.1",
"aiohttp-client-cache>=0.12.4",
"redis>=5.2.1",
]
requires-python = "==3.13.*"
readme = "README.md"
license = {text = "MIT"}


[tool.pdm]
distribution = false
4 changes: 2 additions & 2 deletions radia/cogs/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ def invalid_whatis(self, prefix):
"it's a social construct",
"your parent/guardian",
"gay gay homosexual gay",
"\*poof*",
"\\*poof*",
"???",
"¯\_(ツ)_/¯",
\\_(ツ)_/¯",
"dark matter",
"a collection of subatomic particles",
"an empty void",
Expand Down
29 changes: 22 additions & 7 deletions radia/cogs/tourney.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import discord
from discord.ext import commands

from radia import utils, battlefy
from radia import utils, battlefy, sendouConnector


class Tourney(commands.Cog, command_attrs={"hidden": True}):
Expand All @@ -18,6 +18,7 @@ class Tourney(commands.Cog, command_attrs={"hidden": True}):

def __init__(self, bot):
self.bot = bot
self.sendou = sendouConnector.Connector()

# Agenda Command Group:

Expand Down Expand Up @@ -117,12 +118,16 @@ def tourney_desc(ctx, tourney):
:param utils.Event tourney: the tournament event object
"""
format_str = 'MMM DD, YYYY h:mm A UTC'
return "\n".join([
text = [
f"Event Begin Time: `{tourney.event.begin.format(format_str)}`",
f"Event End Time: `{tourney.event.end.format(format_str)}`",
f"Battlefy Tournament ID: `{tourney.battlefy}`",
f"Captain Role: {tourney.get_role(ctx).mention}",
])
]
if tourney.battlefy:
text.append(f"Battlefy Tournament ID: `{tourney.battlefy}`")
elif tourney.sendou:
text.append(f"Sendou Tournament ID: `{tourney.sendou}`")
return "\n".join(text)

# Captain Command Group:

Expand All @@ -144,7 +149,12 @@ async def check(self, ctx, index: int = 0, _invalid_captains=None):
tourney = utils.agenda.tourney_at(index)
if not tourney:
return await ctx.send("⛔ **No event found**")
teams = await battlefy.connector.get_teams(tourney.battlefy)
if tourney.battlefy:
teams = await battlefy.connector.get_teams(tourney.battlefy)
elif tourney.sendou:
teams = await self.sendou.get_teams(tourney.sendou)
else:
return await ctx.send("⛔ **No platform found**")

# Create list of invalid captains
if not _invalid_captains:
Expand All @@ -156,7 +166,7 @@ async def check(self, ctx, index: int = 0, _invalid_captains=None):
invalid_captains = [f"`{team.captain.discord}` | `{team.name}`" for team in _invalid_captains]

# Send status check embed
send = False
send_file = None
embed = utils.Embed(
title=f"🗒️ Captain status check for `{tourney.event.name}`",
description=f"Invalid Captains / Total Teams: `{len(invalid_captains)}/{len(teams)}`")
Expand Down Expand Up @@ -189,7 +199,12 @@ async def assign(self, ctx, index: int = 0, nick: bool = True):

# Loop over teams and assign valid captains
async with ctx.typing():
teams = await battlefy.connector.get_teams(tourney.battlefy)
if tourney.battlefy:
teams = await battlefy.connector.get_teams(tourney.battlefy)
elif tourney.sendou:
teams = await self.sendou.get_teams(tourney.sendou)
else:
return await ctx.send("⛔ **No platform found**")
for team in teams:
# Attempt to add captain role to members
try:
Expand Down
40 changes: 40 additions & 0 deletions radia/sendouConnector/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Initializes the Sendou connector."""

import logging
import sendou
from aiohttp_client_cache.backends import RedisBackend
from os import environ

from .objects import Tournament

class Connector:
def __init__(self):
self.__api_key = environ.get("SENDOU_API_KEY")
if not self.__api_key:
logging.error("No Sendou API Key found.")
raise ValueError("No Sendou API Key found.")
if redis_uri := environ.get("REDIS_URI"):
self.__redis_uri = redis_uri
else:
self.__redis_uri = "redis://redis:6379/"
self.client = sendou.Client(self.__api_key)
self.client.cache = RedisBackend(cache_name="sendou", redis_uri=self.__redis_uri, expire_after=300)
logging.debug("Loaded sendou.connector")

async def get_tournament(self, tournament_id: str):
""" Get tournament object from sendou api.
:param tournament_id: The sendou tournament ID
:rtype: Tournament
"""
tournament_data = await self.client.get_tournament(tournament_id)
teams = await tournament_data.get_teams()
return Tournament(tournament_data, teams)

async def get_teams(self, tournament_id: str):
""" Helper function that simply returns the ".teams" attribute of a tournament.
:return: List[Team]
"""
tourney = await self.get_tournament(tournament_id)
return tourney.teams
3 changes: 3 additions & 0 deletions radia/sendouConnector/objects/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .player import Player
from .team import Team
from .tournament import Tournament
37 changes: 37 additions & 0 deletions radia/sendouConnector/objects/player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Battlefy player object."""
from typing import Optional

import dateutil.parser
from discord.ext import commands
from sendou import TeamMember


class Player:
""" Function and utilities for managing players from the battlefy api.
:param dict sendou: The sendou player data
"""

def __init__(self, sendou: Optional[TeamMember]):
self.raw: Optional[TeamMember] = sendou
self.member_converter = commands.MemberConverter()
if self.raw:
self.created_at = sendou.joined_at
else:
self.created_at = None
@property
def discord(self):
return self.raw.discord_id

async def get_discord(self, ctx):
""" Return the discord member object using the discord field provided.
:return Optional[discord.Member]:
Returns None if the member isn't found in the server.
"""
if not self.raw.discord_id:
return None
try:
return await self.member_converter.convert(ctx, self.raw.discord_id)
except commands.BadArgument:
return None
17 changes: 17 additions & 0 deletions radia/sendouConnector/objects/team.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Sendou team object."""
from sendou import TournamentTeam
from typing import List

from .player import Player

class Team:
def __init__(self, sendou: TournamentTeam):
self.raw: TournamentTeam = sendou
self.id = sendou.id
self.name = sendou.name
self.logo = sendou.logo_url
self.players: List[Player] = [Player(p) for p in sendou.members]

@property
def captain(self):
return next(p for p in self.players if p.raw.captain)
16 changes: 16 additions & 0 deletions radia/sendouConnector/objects/tournament.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import sendou
from typing import List

from .team import Team


class Tournament:
"""Sendou tournament object."""

def __init__(self, sendou_data: sendou.Tournament, teams: List[sendou.TournamentTeam]):
self.raw: sendou.Tournament = sendou_data
self.id = sendou_data.id
self.name = sendou_data.name
self.registered_count = sendou_data.teams.registered_count
self.teams: List[Team] = [Team(t) for t in teams]

4 changes: 3 additions & 1 deletion radia/utils/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ def filter_cal(self):
class Event:
"""Represents a tournament event."""

def __init__(self, event, battlefy, role="406171863698505739", **kwargs):
def __init__(self, event, battlefy=None, sendou=None, role="406171863698505739", **kwargs):
self.event = event
# Event description params
self.battlefy = battlefy
self.sendou = sendou
self.sendou = sendou
self.role = role

def get_role(self, ctx):
Expand Down
Loading

0 comments on commit aa85882

Please sign in to comment.