forked from sass/tipibot
Added quests
This commit is contained in:
108
commands/economy_quests_commands.py
Normal file
108
commands/economy_quests_commands.py
Normal file
@@ -0,0 +1,108 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
import discord
|
||||
from discord import app_commands
|
||||
|
||||
from core import economy
|
||||
import strings as S
|
||||
|
||||
|
||||
_BAR_SLOTS = 8
|
||||
|
||||
|
||||
def _bar(progress: int, goal: int) -> str:
|
||||
"""Return an 8-slot ▰/▱ progress bar for a quest."""
|
||||
filled = 0 if goal <= 0 else min(_BAR_SLOTS, round(_BAR_SLOTS * progress / goal))
|
||||
return "▰" * filled + "▱" * (_BAR_SLOTS - filled)
|
||||
|
||||
|
||||
def _has_claimable(view_data: dict) -> bool:
|
||||
return any(
|
||||
q["done"] and not q["claimed"]
|
||||
for q in view_data["daily"] + view_data["weekly"]
|
||||
)
|
||||
|
||||
|
||||
def register_economy_quests_commands(
|
||||
tree: app_commands.CommandTree,
|
||||
bot: discord.Client,
|
||||
coin: Callable[[int], str],
|
||||
award_exp: Callable[[discord.Interaction, int], Awaitable[None]],
|
||||
) -> None:
|
||||
def _render(view_data: dict) -> discord.Embed:
|
||||
embed = discord.Embed(title=S.TITLE["quests"], color=0xF4C430)
|
||||
sections = (
|
||||
(S.QUEST_UI["daily_header"], view_data["daily"]),
|
||||
(S.QUEST_UI["weekly_header"], view_data["weekly"]),
|
||||
)
|
||||
for header, quests in sections:
|
||||
if quests:
|
||||
blocks = []
|
||||
for q in quests:
|
||||
desc = S.QUEST_DESCRIPTIONS.get(q["id"], q["id"])
|
||||
if q["claimed"]:
|
||||
status = S.QUEST_UI["completed"]
|
||||
elif q["done"]:
|
||||
status = S.QUEST_UI["ready"]
|
||||
else:
|
||||
status = S.QUEST_UI["progress"].format(progress=q["progress"], max=q["goal"])
|
||||
reward = S.QUEST_UI["reward"].format(coins=f"{q['coins']:,}", exp=q["exp"])
|
||||
blocks.append(
|
||||
f"{_bar(q['progress'], q['goal'])} **{desc}**\n{status} · {reward}"
|
||||
)
|
||||
value = "\n\n".join(blocks)
|
||||
else:
|
||||
value = S.QUEST_UI["empty"]
|
||||
embed.add_field(name=header, value=value, inline=False)
|
||||
return embed
|
||||
|
||||
class QuestView(discord.ui.View):
|
||||
def __init__(self, invoker_id: int, view_data: dict):
|
||||
super().__init__(timeout=180)
|
||||
self.invoker_id = invoker_id
|
||||
btn = discord.ui.Button(
|
||||
label=S.QUEST_UI["claim_btn"],
|
||||
style=discord.ButtonStyle.success,
|
||||
disabled=not _has_claimable(view_data),
|
||||
)
|
||||
btn.callback = self._claim
|
||||
self.add_item(btn)
|
||||
|
||||
async def _claim(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.invoker_id:
|
||||
await interaction.response.send_message(S.ERR["not_your_menu"], ephemeral=True)
|
||||
return
|
||||
try:
|
||||
res = await economy.claim_quests(self.invoker_id)
|
||||
except economy.DatabaseError:
|
||||
await interaction.response.send_message(S.QUEST_UI["error"], ephemeral=True)
|
||||
return
|
||||
if not res["ok"]:
|
||||
await interaction.response.send_message(S.QUEST_UI["nothing"], ephemeral=True)
|
||||
return
|
||||
new_data = await economy.get_quests(self.invoker_id)
|
||||
await interaction.response.edit_message(
|
||||
embed=_render(new_data), view=QuestView(self.invoker_id, new_data)
|
||||
)
|
||||
await interaction.followup.send(
|
||||
S.QUEST_UI["claimed_msg"].format(
|
||||
count=res["claimed"], coins=coin(res["coins"]), exp=res["exp"]
|
||||
),
|
||||
ephemeral=True,
|
||||
)
|
||||
if res["exp"]:
|
||||
asyncio.create_task(award_exp(interaction, res["exp"]))
|
||||
|
||||
@tree.command(name="quests", description=S.CMD["quests"])
|
||||
async def cmd_quests(interaction: discord.Interaction):
|
||||
await interaction.response.defer()
|
||||
try:
|
||||
data = await economy.get_quests(interaction.user.id)
|
||||
except economy.DatabaseError:
|
||||
await interaction.followup.send(S.QUEST_UI["error"], ephemeral=True)
|
||||
return
|
||||
await interaction.followup.send(embed=_render(data), view=QuestView(interaction.user.id, data))
|
||||
Reference in New Issue
Block a user