""" Defines widgets for displaying a campaign """ from typing import Optional, TYPE_CHECKING import asyncio import datetime as dt import discord from discord.ui import Modal from discord.ui.select import select, Select, SelectOption from discord.ui.button import button, Button, ButtonStyle from discord.ui.text_input import TextInput, TextStyle from meta import LionBot from meta.errors import UserInputError from meta.config import conf from utils.lib import tabulate, utc_now, MessageArgs, parse_duration from utils.ui import MessageUI, AButton, AsComponents, ConfigEditor from utils.ui.micros import FastModal from utils.ui.pagers import BasePager, Pager from ...campaign import RewardCampaign from ...data import EarnedReward from .. import logger from .rewards import RewardList class CampaignDashboard(MessageUI): def __init__(self, bot: LionBot, campaign: RewardCampaign, **kwargs): super().__init__(**kwargs) self.bot = bot self.campaign = campaign # UI state # ------ UI API ----- # ------ UI Components ----- # Button to open rewards list @button(label="Rewards Earned") async def rewards_list_button(self, press: discord.Interaction, pressed: Button): await press.response.defer() widget = RewardList( bot=self.bot, campaign=self.campaign, callerid=self._callerid, ) self._slaves.append(widget) await widget.run(press) await widget.wait() self._slaves.remove(widget) @button(emoji=conf.emojis.cancel, style=ButtonStyle.red) async def quit_button(self, press: discord.Interaction, pressed: Button): """Close the UI and all children.""" await press.response.defer(thinking=False) await self.quit() @button(emoji=conf.emojis.refresh) async def refresh_button(self, press: discord.Interaction, pressed: Button): await press.response.defer() await self.refresh() # ------ UI Flow ----- async def refresh_layout(self): # Nothing to refresh pass async def make_message(self) -> MessageArgs: embed = discord.Embed( title=f"{self.campaign.row.campaign_name} Campaign Dashboard" ) # embed.set_footer with last update # Don't show the webhook url since it contains secrets, just show whether it is set # Hard-coded channel/timer URL for now campaign = self.campaign started_at = ( discord.utils.format_dt(campaign.row.started_at, "F") if campaign.row.started_at else "*Not Started*" ) all_rewards = await campaign.get_rewards() rewards_earned = len(all_rewards) reward_cap = campaign.row.target_rewards if reward_cap is not None: rewards = f"{rewards_earned} out of {reward_cap}" else: rewards = f"{rewards_earned}" description = ( f"Campaign running since {started_at} with {rewards} rewards given." ) table = { "Created at": discord.utils.format_dt(campaign.row.created_at, "F"), "Started at": started_at, "Moderator Role": f"<&@{campaign.row.moderator_role_id}>" if campaign.row.moderator_role_id else "*Not Set*", "Logging webhook": "*Set Up*" if campaign.row.logging_webhook_url else "*Not Set*", "Rewards Earned": str(rewards_earned), "Rewards Cap": str(reward_cap), } if campaign.row.completed_at is not None: table['Finished at'] = discord.utils.format_dt(campaign.row.completed_at, "F") prop_table = '\n'.join(tabulate(*table.items())) embed.description = f"{description}\n\n{prop_table}" # Brief summary of rewards in columns. 12 per column. Empty titles? rewardrows = [] for reward in all_rewards: name = reward.twitch_user_name or str(reward.twitch_user_id) or str(reward.profileid) fluffed = reward.fulfilled_at is not None fluffed_emoji = '✅' if fluffed else '🔳' rewardrows.append(f"{fluffed_emoji} {name}") blocks = ['\n'.join(rewardrows[i:i+12]) for i in range(0, len(rewardrows), 12)] embed.add_field( name="Rewards Summary", value=blocks[0] or 'No Rewards Earned', inline=True ) for block in blocks[1:]: embed.add_field( name="--", value=block, ) return MessageArgs(embed=embed) async def reload(self): await self.campaign.row.refresh()