Skip to content

Commit

Permalink
Merge pull request #10 from IPLSplatoon/V2
Browse files Browse the repository at this point in the history
Support for Discord.py V2
  • Loading branch information
HoeenCoder authored Jul 22, 2023
2 parents e807e1d + 0c1a8d7 commit 9c043c4
Show file tree
Hide file tree
Showing 20 changed files with 230 additions and 153 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: ci

on:
push:
branches:
- 'master'

jobs:
build-keycloak:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v4
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/radia:latest
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3
FROM python:3.9

WORKDIR /usr/src/app

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Radia

**This is a V2 branch to move IPL's bot to discord.py V2 and is not stable**

> Radia is the mascot of Inkling Performance Labs. As the lead researcher, her duty is to take care of the teams participating in Low Ink as well as to see growth in the teams and individuals taking part in the tournaments.
<!-- Banner -->
Expand Down Expand Up @@ -87,7 +89,11 @@ Managing the check-in command staff can use these subcommands:
1. Copy the gsheet key from the url at `https://docs.google.com/spreadsheets/d/`**`{key}`**`/edit`, you will use this in the `.env`

#### MongoDB
(coming soon)
1. Create a MongoDB Database, either locally via a docker or using MongoDB Cloud
1. Form a valid URI to access the database. *e.g. `mongodb://username:password@localhost:27017`*
1. Place URI into environment variables

*Database will create tables as needed and used*

#### Bot Setup
1. Create a `.env` in the repository root:
Expand All @@ -98,10 +104,12 @@ Managing the check-in command staff can use these subcommands:
ICAL="https://calendar.google.com/calendar/ical/43cm%40group.calendar.google.com/private-1b6d/basic.ics"
MONGOURI="Your MongoDB Connection URI"
DATABASENAME=DatabaseName
MONGOURI=MongoDbURI
SENTRY="System Environment" # Optional
DEBUG=1 # Optional
```

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`

1. Run `docker-compose up` in the repository root.

33 changes: 21 additions & 12 deletions radia/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@

import os
import logging
import asyncio

from discord import Intents

from radia import cogs, battlefy, google
from radia.bot import Bot

# Create Bot
intents = Intents.default()
intents.members = True
intents = Intents.all()
# intents.members = True
debug = os.getenv("DEBUG", "false").lower() != "false"
bot = Bot(command_prefix="!" if not debug else "^", intents=intents)

# Load Cogs
for cog in cogs.names:
try:
bot.load_extension("radia.cogs." + cog)
logging.debug("Loaded cogs.%s", cog)
except Exception as e:
logging.warning("Failed to load cogs.%s", cog)
logging.error(type(e).__name__, e)

# Run Bot
# Get token from env variables
if not (token := os.getenv("TOKEN")):
logging.error(".env - 'TOKEN' key not found. Cannot start bot.")
raise EnvironmentError

bot.run(token)

async def run_bot() -> None:
"""
Loads in cogs and starts bot
:return: None
"""
async with bot:
for cog in cogs.names:
try:
await bot.load_extension("radia.cogs." + cog)
logging.debug("Loaded cogs.%s", cog)
except Exception as e:
logging.warning("Failed to load cogs.%s", cog)
logging.error(type(e).__name__, e)
await bot.start(token)

asyncio.run(run_bot())
10 changes: 6 additions & 4 deletions radia/battlefy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ class Connector:
"""Battlefy connector."""

def __init__(self):
self.session = aiohttp.ClientSession()
logging.debug("Loaded battlefy.connector")

async def query(self, url: str):
""" Get a response at url.
:param str url: The battlefy tournament id
"""
async with self.session.get("https://dtmwra1jsgyb0.cloudfront.net/" + url) as response:
session = aiohttp.ClientSession()
data = None
async with session.get("https://dtmwra1jsgyb0.cloudfront.net/" + url) as response:
if response.status != 200:
logging.error("Unable to query battlefy api, Status Code: %s", response.status)
return
return await response.json()
data = await response.json()
await session.close()
return data

async def get_tournament(self, tournament: str):
""" Get tournament object from battlefy api.
Expand Down
10 changes: 9 additions & 1 deletion radia/battlefy/objects/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ class Player:

def __init__(self, battlefy):
self.raw = battlefy
self.created_at = dateutil.parser.isoparse(self.raw.get("createdAt"))
if self.raw:
self.created_at = dateutil.parser.isoparse(self.raw.get("createdAt"))
else:
self.created_at = None


class Captain(Player):
Expand All @@ -27,6 +30,11 @@ def __init__(self, battlefy, discord_field, fc_field):
super().__init__(battlefy)
self.member_converter = commands.MemberConverter()
self.discord = discord_field
if self.discord:
if self.discord.endswith("#0000"):
self.discord = self.discord[:-5]
elif self.discord.endswith("#0"):
self.discord = self.discord[:-2]
self.fc = fc_field

async def get_discord(self, ctx):
Expand Down
12 changes: 6 additions & 6 deletions radia/battlefy/objects/tournament.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ def get_field_ids(cls, battlefy_teams):
# Loop over each field attempting to detect if it's a discord field or not.
for i, field in enumerate(custom_fields):
fields = custom_fields.copy() # Makes sure using .pop() doesn't mess with the actual team fields
# Regex pattern matches valid discord usernames
if re.match(r'^[^@#:]{1,}#\d{4}$', field["value"]):
discord_field = fields.pop(i)
# If there are still more fields, other field must be fc
# Regex pattern matches valid Friend codes
if re.match(r'\(?(SW|FC|sw|fc)?\s*([:\-=])?\s?(\d{4})\s*([- ._/=])\s*(\d{4})\s*([- ._/=])\s*(\d{4})\s*\)?', field["value"]):
fc_field = fields.pop(i)
# If there are still more fields, other field must be Discord Usernames
if fields:
fc_field = fields.pop(0)
discord_field = fields.pop(0)
else: # Otherwise, there's no fc field
fc_field = {"_id": None}
discord_field = {"_id": None}
# Return field ids
return discord_field["_id"], fc_field["_id"]
else:
Expand Down
3 changes: 3 additions & 0 deletions radia/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.help_command = utils.HelpCommand()

async def setup_hook(self) -> None:
pass

async def on_ready(self):
logging.info("Logged in as: %s", self.user.name)

Expand Down
81 changes: 45 additions & 36 deletions radia/cogs/checkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,26 @@
"B": {"name": "Beta", "id": 2},
"G": {"name": "Gamma", "id": 3},
"D": {"name": "Delta", "id": 4},
"Z": {"name": "Zeta", "id": 5},
"T": {"name": "Top 32", "id": 6},
"E": {"name": "Epsilon", "id": 7},
"ALPHA": {"name": "Alpha", "id": 1},
"BETA": {"name": "Beta", "id": 2},
"GAMMA": {"name": "Gamma", "id": 3},
"DELTA": {"name": "Delta", "id": 4}
"DELTA": {"name": "Delta", "id": 4},
"ZETA": {"name": "Zeta", "id": 5},
"TOP 32": {"name": "Top 32", "id": 6},
"EPSILON": {"name": "Epsilon", "id":7},
}

id_to_bracket = {
1: "Alpha",
2: "Beta",
3: "Gamma",
4: "Delta"
4: "Delta",
5: "Zeta",
6: "Top 32",
7: "Epsilon",
}


Expand Down Expand Up @@ -118,21 +127,21 @@ async def load(self, ctx, tourney: int = 0):
"""Load battlefy teams data."""
async with ctx.typing():
tourney = utils.agenda.tourney_at(tourney)
if not tourney:
return await ctx.send("⛔ **No event found**")
if not tourney:
return await ctx.send("⛔ **No event found**")

battlefy_teams = await battlefy.connector.get_teams(tourney.battlefy)
battlefy_teams = await battlefy.connector.get_teams(tourney.battlefy)

try:
await self.database.load_teams(battlefy_teams, tourney.battlefy)
self._battlefy_id = tourney.battlefy
embed = utils.Embed(
title=f"✅ **Success:** teams loaded for `{tourney.event.name}` checkin",
description=f"Loaded `{len(battlefy_teams)}` teams.")
await ctx.send(embed=embed)
except Exception as error:
await ctx.send(f"Error\n ```\n{error}\n```")
pass
try:
await self.database.load_teams(battlefy_teams, tourney.battlefy)
self._battlefy_id = tourney.battlefy
embed = utils.Embed(
title=f"✅ **Success:** teams loaded for `{tourney.event.name}` checkin",
description=f"Loaded `{len(battlefy_teams)}` teams.")
await ctx.send(embed=embed)
except Exception as error:
await ctx.send(f"Error\n ```\n{error}\n```")
pass

@commands.has_role("Staff")
@checkin.command(aliases=["setid"])
Expand Down Expand Up @@ -198,28 +207,28 @@ async def unassign(self, ctx, team_name: str):
@checkin.command(aliases=["list"])
async def view(self, ctx, bracket: str = None):
"""View all teams checked in/out for tournament"""
with ctx.typing():
async with ctx.typing():
if not bracket: # gets all teams for tournament with bracket > 0 if one isn't provided
bracket_teams = await self.database.get_bracket_teams(self._battlefy_id)
else:
if not (bracket_type := valid_bracket_type.get(bracket.upper())):
return await ctx.send(f"⛔ **Invalid Bracket Type**")
bracket_teams = await self.database.get_bracket_teams(self._battlefy_id, bracket_type['id'])
check_in, check_out = [], [] # Stores string of teams checked in/out
if not bracket_teams:
embed = utils.Embed(title=f"Not teams to list")
return await ctx.send(embed=embed)
for team in bracket_teams:
if team.checkin:
check_in.append(team.name[:32])
else:
check_out.append(team.name[:32])
embed = utils.Embed(title=f"Check in List for {'All' if not bracket else bracket_type['name']}")
if check_in:
embed.add_field(name=f"Checked in: {len(check_in)}", value=f"{utils.Embed.list(check_in)}", inline=False)
if check_out:
embed.add_field(name=f"Checked out: {len(check_out)}", value=f"{utils.Embed.list(check_out)}",
inline=False)
check_in, check_out = [], [] # Stores string of teams checked in/out
if not bracket_teams:
embed = utils.Embed(title=f"No teams to list")
return await ctx.send(embed=embed)
for team in bracket_teams:
if team.checkin:
check_in.append(team.name[:32])
else:
check_out.append(team.name[:32])
embed = utils.Embed(title=f"Check in List for {'All' if not bracket else bracket_type['name']}")
if check_in:
embed.add_field(name=f"Checked in: {len(check_in)}", value=f"{utils.Embed.list(check_in)}", inline=False)
if check_out:
embed.add_field(name=f"Checked out: {len(check_out)}", value=f"{utils.Embed.list(check_out)}",
inline=False)
await ctx.send(embed=embed)

@commands.has_role("Staff")
Expand All @@ -242,11 +251,11 @@ async def clear(self, ctx):
for member in role.members:
await member.remove_roles(role)
counter += 1
embed = utils.Embed(
title=f"✅ **Success:** bracket roles cleared from all members",
description=f"Cleared `{str(counter)}` roles.")
embed = utils.Embed(
title=f"✅ **Success:** bracket roles cleared from all members",
description=f"Cleared `{str(counter)}` roles.")
await ctx.send(embed=embed)


def setup(bot):
bot.add_cog(CheckIn(bot))
async def setup(bot):
await bot.add_cog(CheckIn(bot))
4 changes: 2 additions & 2 deletions radia/cogs/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,5 @@ def invalid_whatis(self, prefix):
])


def setup(bot):
bot.add_cog(Info(bot))
async def setup(bot):
await bot.add_cog(Info(bot))
14 changes: 8 additions & 6 deletions radia/cogs/lowink.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async def champion(self, ctx):
@champion.command(aliases=["coronate", "crown"])
async def add(self, ctx):
"""Add the Champion role to members."""
with ctx.typing():
async with ctx.typing():
# Create list of applicable champion roles
roles = self.get_roles(ctx,
"Past Low Ink Winner",
Expand All @@ -46,12 +46,14 @@ async def add(self, ctx):
@champion.command(aliases=["dethrone"])
async def remove(self, ctx):
"""Remove the champion roles from members who currently have it."""
with ctx.typing():
async with ctx.typing():
# Create list of applicable champion roles
roles = self.get_roles(ctx,
"Low Ink Current Champions",
"Low Beta Bracket Champions",
"Low Gamma Bracket Champions"
"LI Beta Bracket Champions",
"LI Gamma Bracket Champions",
"LI Delta Bracket Champion",
"LI Epsilon Bracket Champions"
)
# Create a set of all members with any champion role
all_champions = list()
Expand Down Expand Up @@ -80,5 +82,5 @@ def get_roles(ctx, *names):
]


def setup(bot):
bot.add_cog(LowInk(bot))
async def setup(bot):
await bot.add_cog(LowInk(bot))
Loading

0 comments on commit 9c043c4

Please sign in to comment.