Skip to content

Commit

Permalink
Update card extension for the api
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyrannicodin committed Nov 4, 2024
1 parent d7644f2 commit 91ab163
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 61 deletions.
98 changes: 39 additions & 59 deletions exts/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from matplotlib import pyplot as plt
from PIL import Image

from util import TYPE_COLORS, Card, EffectCard, HermitCard, hash_to_deck, hash_to_stars, probability
from util import TYPE_COLORS, Card, EffectCard, HermitCard, Server, ServerManager, probability


def take(items: int, iterable: Iterable) -> list:
Expand Down Expand Up @@ -70,26 +70,30 @@ def best_factors(number: int) -> tuple[int, int]:
class CardExt(Extension):
"""Get information about cards and decks."""

def __init__(self: "CardExt", _: Client, universe: dict[str, Card]) -> None:
def __init__(
self: "CardExt", _: Client, manager: ServerManager
) -> None:
"""Get information about cards and decks.
Args:
----
universe (dict): Dictionary that converts card ids to Card objects
manager (ServerManager): The manager for all servers the bot is in
"""
self.universe = universe
self.lastReload = time()
self.universe: dict[str, Card] = manager.universe
self.manager: ServerManager = manager
self.lastReload: int = time()

def get_stats(
self: "CardExt", deck: list[Card]
) -> tuple[Image.Image, tuple[int, int, int], dict[str, int]]:
) -> tuple[Image.Image, tuple[int, int, int], dict[str, int], int]:
"""Get information and an image of a deck.
Args:
----
deck (list): List of card ids in the deck
"""
type_counts = {
type_counts: dict[str, int] = {
"miner": 0,
"terraform": 0,
"speedrunner": 0,
Expand All @@ -101,26 +105,29 @@ def get_stats(
"redstone": 0,
"farm": 0,
}
cost: int = 0
hermits, items, effects = ([] for _ in range(3))
for card in deck:
if card.text_id.startswith("item"):
if card.category == "item":
items.append(card)
elif card.text_id.endswith(("rare", "common")):
elif card.category == "hermit":
hermits.append(card)
if card in self.universe.keys():
type_counts[card.hermit_type] += 1
else:
effects.append(card)
cost += card.cost

hermits.sort(key=lambda x: x.numeric_id)
items.sort(key=lambda x: x.numeric_id)
effects.sort(key=lambda x: x.numeric_id)
hermits.sort(key=lambda x: x.text_id)
items.sort(key=lambda x: x.text_id)
effects.sort(key=lambda x: x.text_id)
width, height = best_factors(len(deck))
im = Image.new("RGBA", (width * 200, height * 200))
for i, card in enumerate(hermits + effects + items):
new_card = card.image.resize((200, 200)).convert("RGBA")
card: Card
new_card = card.full_image.resize((200, 200)).convert("RGBA")
im.paste(new_card, ((i % width) * 200, (i // width) * 200), new_card)
return im, (len(hermits), len(effects), len(items)), type_counts
return im, (len(hermits), len(effects), len(items)), type_counts, cost

@global_autocomplete("card_name")
async def card_autocomplete(self: "CardExt", ctx: AutocompleteContext) -> None:
Expand All @@ -141,42 +148,38 @@ async def card(self: "CardExt", _: SlashContext) -> None:
"""Get information about cards and decks."""

@card.subcommand()
@slash_option("deck_hash", "The exported hash of the deck", OptionType.STRING, required=True)
@slash_option("name", "The name of your deck", OptionType.STRING)
@slash_option(
"hide_hash", "If the deck's hash should be hidden - defaults to False", OptionType.BOOLEAN
)
@slash_option("code", "The deck's export code", OptionType.STRING, required=True)
@slash_option("hide_hash", "If the deck's hash should be hidden", OptionType.BOOLEAN)
async def deck(
self: "CardExt",
ctx: SlashContext,
deck_hash: str,
name: Optional[str] = None,
*,
hide_hash: bool = False,
self: "CardExt", ctx: SlashContext, code: str, *, hide_hash: bool = False
) -> None:
"""Get information about a deck."""
if not name:
name = f"{ctx.author.display_name}'s deck"

deck_list = hash_to_deck(deck_hash, self.universe)
if len(deck_list) > 100:
await ctx.send(f"A deck of {len(deck_list)} cards is too large!", ephemeral=True)
if str(ctx.guild_id) not in self.manager.discord_links.keys():
await ctx.send("Couldn't find an online server for this discord!", ephemeral=True)
return
if not deck_list:
server: Server = self.manager.discord_links[str(ctx.guild_id)]

deck = server.get_deck(code)
if not deck:
await ctx.send("Invalid deck: Perhaps you're looking for /card info")
return
im, card_type_counts, hermit_type_counts = self.get_stats(deck_list)
if len(deck["cards"]) > 100:
await ctx.send(f"A deck of {len(deck["cards"])} cards is too large!", ephemeral=True)
return
im, card_type_counts, hermit_type_counts, cost = self.get_stats(
[self.universe[card["props"]["id"]] for card in deck["cards"]]
)
col = TYPE_COLORS[Counter(hermit_type_counts).most_common()[0][0]]

e = (
Embed(
title=name,
description=None if hide_hash else f"Hash: {deck_hash}",
title=deck["name"],
description=None if hide_hash else f"Code: {deck["code"]}",
timestamp=dt.now(tz=timezone.utc),
color=rgb_to_int(col),
)
.set_image("attachment://deck.png")
.add_field("Token cost", str(hash_to_stars(deck_hash, self.universe)), inline=True)
.add_field("Token cost", str(cost), inline=True)
.add_field(
"HEI ratio",
f"{card_type_counts[0]}:{card_type_counts[1]}:{card_type_counts[2]}",
Expand All @@ -198,19 +201,12 @@ async def deck(
emoji=":wastebasket:",
custom_id=f"delete_deck:{ctx.author_id}",
)
copy_button = Button(
style=ButtonStyle.LINK,
label="Copy",
emoji=":clipboard:",
url=f"https://hc-tcg.fly.dev/?deck={quote(deck_hash)}&name={quote(name)}",
disabled=hide_hash,
)
if hide_hash:
await ctx.send("This message handily obscures your deck hash!", ephemeral=True)
await ctx.send(
embeds=e,
files=File(im_binary, "deck.png"),
components=spread_to_rows(delete_button, copy_button),
components=spread_to_rows(delete_button),
)

@component_callback(re_compile("delete_deck:[0-9]"))
Expand Down Expand Up @@ -286,22 +282,6 @@ async def info(self: "CardExt", ctx: SlashContext, card_name: str) -> None:
else:
await ctx.send("Couldn't find that card!", ephemeral=True)

@card.subcommand()
async def reload(self: "CardExt", ctx: SlashContext) -> None:
"""Reload the card data and images."""
if (
self.lastReload + 60 * 30 < time()
): # Limit reloading to every 30 minutes as it's quite slow
await ctx.send("Reloading...", ephemeral=True)
start_time = time()
next(self.universe.values()).reload()
await ctx.send(f"Reloaded! Took {round(time()-start_time)} seconds", ephemeral=True)
self.lastReload = time()
return
await ctx.send(
"Reloaded within the last 30 minutes, please try again later.", ephemeral=True
)

@card.subcommand()
@slash_option(
"hermits", "The number of hermits in your deck", OptionType.INTEGER, required=True
Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ async def on_message_create(self: "Bot", message: MessageCreate) -> None:
server_manager = ServerManager(bot, servers, web_server, scheduler, data_gen.universe)

bot.load_extension("exts.admin", None, manager=server_manager)
bot.load_extension("exts.card", None, universe=data_gen.universe)
bot.load_extension("exts.card", None, manager=server_manager)
bot.load_extension("exts.dotd", None, manager=server_manager)
bot.load_extension("exts.forums", None, manager=server_manager)
bot.load_extension("exts.match", None, manager=server_manager)
Expand Down
2 changes: 1 addition & 1 deletion util/datagen.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def __init__(self: "Card", data: dict, generator: "DataGenerator") -> None:
self.generator: DataGenerator = generator

self.text_id: str = data["id"]
self.category: str = data["category"]
# self.numeric_id: int = data["numericId"]

self.cost: int = data["tokens"]
Expand Down Expand Up @@ -464,7 +465,6 @@ def load_rank_stars(self: "DataGenerator") -> tuple[defaultdict, list[Image.Imag
"""Get token star images."""
rank_stars: dict[str, Image.Image] = {}
iterator = self.get("api/ranks")
print(iterator.text)
iterator = iterator.json()
if has_progression:
iterator = tqdm(iterator, "Loading types")
Expand Down
15 changes: 15 additions & 0 deletions util/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,21 @@ def get_games(self: "Server") -> list[Game]:
except (ConnectionError, exceptions.JSONDecodeError, exceptions.Timeout):
return []

def get_deck(self: "Server", code: str) -> Optional[dict]:
"""Get information about a deck from the server.
Args:
----
code (str): The export code of the deck to retrieve
"""
try:
result = get(f"{self.api_url}/deck/{code}", timeout=5).json()
except (TimeoutError, exceptions.JSONDecodeError):
return
if result["type"] == "success":
return result
return None

def create_game(self: "Server") -> Optional[str]:
"""Create a server game."""
try:
Expand Down

0 comments on commit 91ab163

Please sign in to comment.