(discord): Fill in discord interfaces

This commit is contained in:
2026-07-29 15:48:47 +03:00
parent 9203a795e3
commit 8fc0ff0667
4 changed files with 618 additions and 6 deletions
+1
View File
@@ -1 +1,2 @@
from .rewards import RewardList, RewardEditor
from .campaign import CampaignDashboard
+139
View File
@@ -0,0 +1,139 @@
"""
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()
+33 -3
View File
@@ -16,9 +16,8 @@ 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 import MessageUI
from utils.ui.micros import FastModal
from utils.ui.pagers import BasePager, Pager
from ...campaign import RewardCampaign
from ...data import EarnedReward
@@ -134,7 +133,38 @@ class RewardList(MessageUI):
value = int(selected.values[0])
reward = next(r for r in self._rewards if r.earned_id == value)
modal = RewardEditor.from_reward(reward)
currently_fluffed = (reward.fulfilled_at is not None)
@modal.submit_callback()
async def on_editor_submit(interaction: discord.Interaction):
update_args = {}
if modal.flufbox.component.value and not currently_fluffed:
# Reward has been fluffed
update_args['fulfilled_at'] = utc_now()
elif currently_fluffed and not modal.flufbox.component.value:
# Reward has been unfluffed
update_args['fulfilled_at'] = None
new_ref_value = modal.reference.value or None
if new_ref_value != reward.reference:
update_args['reference'] = new_ref_value
new_notes_value = modal.notes.value or None
if new_notes_value != reward.modnote:
update_args['modnote'] = new_notes_value
if update_args:
await interaction.response.defer(thinking=True, ephemeral=True)
await self.campaign.update_reward(reward.earned_id, **update_args)
await self.refresh(thinking=interaction)
else:
await interaction.response.defer(thinking=False)
await selection.response.send_modal(modal)
await self.refresh()
else:
await selection.response.defer()
@@ -200,7 +230,7 @@ class RewardList(MessageUI):
'Earned At': discord.utils.format_dt(reward.earned_at, 'F'),
'Earned From': reward.earned_from,
'Fulfilled At': fluf,
'Reference': "*No reference set*",
'Reference': reward.reference or "*No reference set*",
'Further notes': reward.modnote or "*No notes*",
}
prop_table = '\n'.join(tabulate(*table.items()))