Compare commits
9 Commits
033fc5c1ae
...
32b84625df
| Author | SHA1 | Date | |
|---|---|---|---|
| 32b84625df | |||
| a19bb8a8cb | |||
| 7853d0c115 | |||
| c495e2387e | |||
| 7a9045d3dc | |||
| 891e121e99 | |||
| 0c4b2bfe32 | |||
| a82b58b2c4 | |||
| 21b1f9c3ab |
@@ -87,32 +87,32 @@ async def prepare_subathon(profiler: ProfilesRegistry, subathon: ActiveSubathon)
|
|||||||
last_contrib = ContributionPayload(
|
last_contrib = ContributionPayload(
|
||||||
user_name=name,
|
user_name=name,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
amount=contrib.score,
|
amount=float(contrib.score),
|
||||||
seconds_added=subathon.get_score_time(contrib.score),
|
seconds_added=subathon.get_score_time(contrib.score),
|
||||||
timestamp=contrib.created_at.isoformat()
|
timestamp=contrib.created_at.isoformat()
|
||||||
)
|
)
|
||||||
|
|
||||||
score_table = ScoreTablePayload(
|
score_table = ScoreTablePayload(
|
||||||
bit_score=subathon.subathondata.bit_score,
|
bit_score=float(subathon.subathondata.bit_score),
|
||||||
t1_score=subathon.subathondata.sub1_score,
|
t1_score=float(subathon.subathondata.sub1_score),
|
||||||
t2_score=subathon.subathondata.sub2_score,
|
t2_score=float(subathon.subathondata.sub2_score),
|
||||||
t3_score=subathon.subathondata.sub3_score,
|
t3_score=float(subathon.subathondata.sub3_score),
|
||||||
score_time=subathon.subathondata.score_time,
|
score_time=int(subathon.subathondata.score_time),
|
||||||
)
|
)
|
||||||
payload = SubathonPayload(
|
payload = SubathonPayload(
|
||||||
name=subathon.subathondata.name,
|
name=subathon.subathondata.name,
|
||||||
end_at=(await subathon.get_ending()).isoformat(),
|
end_at=(await subathon.get_ending()).isoformat(),
|
||||||
is_running=(subathon.running),
|
is_running=(subathon.running),
|
||||||
score_table=score_table,
|
score_table=score_table,
|
||||||
total_contribution=await subathon.get_score(),
|
total_contribution=float(await subathon.get_score()),
|
||||||
goals_met=goals_met,
|
goals_met=goals_met,
|
||||||
goals_total=total_goals,
|
goals_total=total_goals,
|
||||||
last_goal=GoalPayload(
|
last_goal=GoalPayload(
|
||||||
required=last_goal.required_score,
|
required=float(last_goal.required_score),
|
||||||
name=last_goal.description,
|
name=last_goal.description,
|
||||||
) if last_goal is not None else None,
|
) if last_goal is not None else None,
|
||||||
next_goal=GoalPayload(
|
next_goal=GoalPayload(
|
||||||
required=next_goal.required_score,
|
required=float(next_goal.required_score),
|
||||||
name=next_goal.description,
|
name=next_goal.description,
|
||||||
) if next_goal is not None else None,
|
) if next_goal is not None else None,
|
||||||
last_contribution=last_contrib
|
last_contribution=last_contrib
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import asyncio
|
||||||
|
import re
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -17,6 +19,36 @@ from .channel import SubathonPayload, prepare_subathon, TimerChannel
|
|||||||
|
|
||||||
|
|
||||||
class SubathonComponent(cmds.Component):
|
class SubathonComponent(cmds.Component):
|
||||||
|
"""
|
||||||
|
!subathon
|
||||||
|
!subathon setup <initial_hours> <t1score> <t2score> <t3score> <bitscore> <timescore> [cap]
|
||||||
|
!subathon stop
|
||||||
|
|
||||||
|
!subathon link
|
||||||
|
!subathon pause
|
||||||
|
!subathon resume
|
||||||
|
|
||||||
|
!subathon adjust <amount> [@user]
|
||||||
|
|
||||||
|
!subathon config
|
||||||
|
!subathon config name [new name]
|
||||||
|
!subathon config cap [new cap]
|
||||||
|
!subathon config scores [<t1score> <t2score> <t3score> <bitscore>]
|
||||||
|
!subathon config timescore [new score]
|
||||||
|
|
||||||
|
!subathon lb
|
||||||
|
|
||||||
|
!goals
|
||||||
|
!goals remaining
|
||||||
|
!goals add <points> <description>
|
||||||
|
!goals remove <points>
|
||||||
|
|
||||||
|
|
||||||
|
Command:
|
||||||
|
Permissions:
|
||||||
|
Description:
|
||||||
|
Examples:
|
||||||
|
"""
|
||||||
# TODO: Add explicit dependencies and version checks
|
# TODO: Add explicit dependencies and version checks
|
||||||
# for profile and event tracker modules
|
# for profile and event tracker modules
|
||||||
|
|
||||||
@@ -53,6 +85,9 @@ class SubathonComponent(cmds.Component):
|
|||||||
for i, goal in enumerate(goals):
|
for i, goal in enumerate(goals):
|
||||||
if not goal.notified and goal.required_score <= score:
|
if not goal.notified and goal.required_score <= score:
|
||||||
# Goal completed, notify channel
|
# Goal completed, notify channel
|
||||||
|
# TODO: Quick hack to avoid running into ratelimits
|
||||||
|
# Should probably wait on a central messaging slow-lock bucketed by channel instead
|
||||||
|
await asyncio.sleep(1)
|
||||||
await channel.send_message(
|
await channel.send_message(
|
||||||
f"We have reached Goal #{i+1}: {goal.description} !! Thank you everyone for your support <3",
|
f"We have reached Goal #{i+1}: {goal.description} !! Thank you everyone for your support <3",
|
||||||
sender=self.bot.bot_id,
|
sender=self.bot.bot_id,
|
||||||
@@ -60,6 +95,36 @@ class SubathonComponent(cmds.Component):
|
|||||||
await goal.update(notified=True)
|
await goal.update(notified=True)
|
||||||
|
|
||||||
# ----- Event Handlers -----
|
# ----- Event Handlers -----
|
||||||
|
# Blerp handler
|
||||||
|
@cmds.Component.listener()
|
||||||
|
async def event_message(self, message: twitchio.ChatMessage):
|
||||||
|
if message.chatter.id not in ('253326823', '1361913054'):
|
||||||
|
return
|
||||||
|
if 'bits' not in message.text:
|
||||||
|
return
|
||||||
|
community = await self.bot.profiles.fetch_community(message.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is not None and not await active.check_finished():
|
||||||
|
# Extract count and user
|
||||||
|
match = re.match(r"(?P<name>\w+) used (?P<amount>\d+)", message.text)
|
||||||
|
if not match:
|
||||||
|
match = re.match(r"!For (?P<amount>\d+) bits, (?P<name>\w+)", message.text)
|
||||||
|
if match:
|
||||||
|
amount = int(match['amount'])
|
||||||
|
name = match['name']
|
||||||
|
# This is for giving the contribution to the calling user
|
||||||
|
# user = await self.bot.fetch_user(login=name.lower())
|
||||||
|
user = await message.chatter.user()
|
||||||
|
profile = await self.bot.profiles.fetch_profile(user)
|
||||||
|
pid = profile.profileid
|
||||||
|
|
||||||
|
score = amount * active.subathondata.bit_score
|
||||||
|
contrib = await active.add_contribution(pid, score, None)
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
logger.info(f"Blerp contribution: {contrib!r} from {message!r}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Unknown blerp bit message: {message!r}")
|
||||||
|
|
||||||
@cmds.Component.listener()
|
@cmds.Component.listener()
|
||||||
async def event_safe_bits_use(self, payload):
|
async def event_safe_bits_use(self, payload):
|
||||||
event_row, detail_row, bits_payload = payload
|
event_row, detail_row, bits_payload = payload
|
||||||
@@ -257,7 +322,7 @@ class SubathonComponent(cmds.Component):
|
|||||||
duration = strfdelta(timedelta(seconds=dursecs))
|
duration = strfdelta(timedelta(seconds=dursecs))
|
||||||
|
|
||||||
secs = await active.get_remaining()
|
secs = await active.get_remaining()
|
||||||
if secs >= 0:
|
if secs > 0:
|
||||||
remaining = strfdelta(timedelta(seconds=secs))
|
remaining = strfdelta(timedelta(seconds=secs))
|
||||||
|
|
||||||
text = (
|
text = (
|
||||||
@@ -273,9 +338,10 @@ class SubathonComponent(cmds.Component):
|
|||||||
await ctx.reply("No active subathon running!")
|
await ctx.reply("No active subathon running!")
|
||||||
|
|
||||||
# subathon start
|
# subathon start
|
||||||
|
# TODO: Usage line on command error
|
||||||
@group_subathon.command(name='setup', alias='start')
|
@group_subathon.command(name='setup', alias='start')
|
||||||
@cmds.is_broadcaster()
|
@cmds.is_broadcaster()
|
||||||
async def cmd_setup(self, ctx: cmds.Context, name: str, initial_hours: float, sub1: float, sub2: float, sub3: float, bit: float, timescore: int, timecap: Optional[int]=None):
|
async def cmd_setup(self, ctx: cmds.Context, initial_hours: float, sub1: float, sub2: float, sub3: float, bit: float, timescore: int, timecap: Optional[int]=None):
|
||||||
"""
|
"""
|
||||||
Creates a new subathon.
|
Creates a new subathon.
|
||||||
USAGE: {prefix}subathon setup <initial_hours> <sub1_points> <sub2_points> <sub3_points> <bit_points> <timepoints> [timecap]
|
USAGE: {prefix}subathon setup <initial_hours> <sub1_points> <sub2_points> <sub3_points> <bit_points> <timepoints> [timecap]
|
||||||
@@ -297,8 +363,8 @@ class SubathonComponent(cmds.Component):
|
|||||||
timecap_seconds = timecap * 60 * 60 if timecap else None
|
timecap_seconds = timecap * 60 * 60 if timecap else None
|
||||||
|
|
||||||
subdata = await Subathon.create(
|
subdata = await Subathon.create(
|
||||||
|
name="Subathon",
|
||||||
communityid=cid,
|
communityid=cid,
|
||||||
name=name,
|
|
||||||
initial_time=initial_time,
|
initial_time=initial_time,
|
||||||
sub1_score=sub1,
|
sub1_score=sub1,
|
||||||
sub2_score=sub2,
|
sub2_score=sub2,
|
||||||
@@ -309,7 +375,12 @@ class SubathonComponent(cmds.Component):
|
|||||||
)
|
)
|
||||||
base_timer_url = self.bot.config.subathon['timer_url']
|
base_timer_url = self.bot.config.subathon['timer_url']
|
||||||
timer_link = f"{base_timer_url}?community={cid}"
|
timer_link = f"{base_timer_url}?community={cid}"
|
||||||
await ctx.reply(f"Setup your {name}! Use !subathon resume to get the timer running. Your timer link: {timer_link}")
|
await ctx.reply(
|
||||||
|
f"Setup your subathon! "
|
||||||
|
f"Use {ctx.prefix}subathon resume to get the timer running. "
|
||||||
|
f"Use {ctx.prefix}subathon config to see and set options, including the name. "
|
||||||
|
f"Your timer link for OBS: {timer_link}"
|
||||||
|
)
|
||||||
active = ActiveSubathon(subdata, None)
|
active = ActiveSubathon(subdata, None)
|
||||||
await self.dispatch_update(active)
|
await self.dispatch_update(active)
|
||||||
|
|
||||||
@@ -334,6 +405,21 @@ class SubathonComponent(cmds.Component):
|
|||||||
else:
|
else:
|
||||||
await ctx.reply("No active subathon to stop.")
|
await ctx.reply("No active subathon to stop.")
|
||||||
|
|
||||||
|
# subathon link
|
||||||
|
@group_subathon.command(name='link')
|
||||||
|
@cmds.is_moderator()
|
||||||
|
async def cmd_link(self, ctx: cmds.Context):
|
||||||
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is not None:
|
||||||
|
base_timer_url = self.bot.config.subathon['timer_url']
|
||||||
|
timer_link = f"{base_timer_url}?community={cid}"
|
||||||
|
await ctx.reply(
|
||||||
|
timer_link
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.reply("No active subathon to stop.")
|
||||||
|
|
||||||
# subathon pause
|
# subathon pause
|
||||||
@group_subathon.command(name='pause')
|
@group_subathon.command(name='pause')
|
||||||
@cmds.is_moderator()
|
@cmds.is_moderator()
|
||||||
@@ -413,41 +499,212 @@ class SubathonComponent(cmds.Component):
|
|||||||
else:
|
else:
|
||||||
await ctx.reply("No active subathon to adjust")
|
await ctx.reply("No active subathon to adjust")
|
||||||
|
|
||||||
# subathon config
|
"""
|
||||||
@group_subathon.command(name='config', aliases=('option',))
|
!subathon config
|
||||||
|
name="Birthday Subathon" ; cap=10 (hours) ; scores=5 10 20 0.1 (t1, t2, t3, and bit points) ; timescore=10 (seconds per point); Use !subathon config <option> [value] to see or set individual options
|
||||||
|
!subathon config name [value]
|
||||||
|
!subathon config cap [value]
|
||||||
|
!subathon config scores [value]
|
||||||
|
!subathon config timescore [value]
|
||||||
|
|
||||||
|
!subathon config name
|
||||||
|
Name of the subathon, used whenever the subathon is mentioned.
|
||||||
|
Accepts any string. Example: !subathon config name Birthday Subathon
|
||||||
|
!subathon config cap
|
||||||
|
Duration cap of the subathon, in hours, including the initial time.
|
||||||
|
The subathon may still be contributed to after this time, but contributions
|
||||||
|
will not raise the timer.
|
||||||
|
Accepts an integer, with 0 to unset. Example: !subathon config cap 10
|
||||||
|
!subathon config scores
|
||||||
|
The number of points each type of contribution (t1 sub, t2 sub, t3 sub, 1 bit)
|
||||||
|
will add to the subathon. Not retroactive. Accepts four floats.
|
||||||
|
Example: !subathon config scores 5 10 20 0.1
|
||||||
|
!subathon config timescore
|
||||||
|
The number of seconds each contributed point adds to the timer.
|
||||||
|
WARNING: This setting is retroactive and should generally not be changed.
|
||||||
|
Accepts an integer. Example: !subathon config timecap 10
|
||||||
|
"""
|
||||||
|
@group_subathon.group(name='config', aliases=('option',), invoke_fallback=True)
|
||||||
@cmds.is_moderator()
|
@cmds.is_moderator()
|
||||||
async def cmd_subathon_config(self, ctx: Context, option: str, *, value: Optional[str] = None):
|
async def subathon_config_grp(self, ctx: Context, *, args: Optional[str] = None):
|
||||||
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
cid = community.communityid
|
cid = community.communityid
|
||||||
if (active := await self.get_active_subathon(cid)) is not None:
|
if (active := await self.get_active_subathon(cid)) is None:
|
||||||
if option.lower() == 'cap':
|
await ctx.reply("No active subathon to configure!")
|
||||||
if value:
|
return
|
||||||
# Set the timer cap
|
if args:
|
||||||
if not value.isdigit():
|
|
||||||
await ctx.reply("Timer cap must be an integer number of hours!")
|
|
||||||
else:
|
|
||||||
await active.subathondata.update(timecap=int(value * 60 * 60))
|
|
||||||
await self.dispatch_update(active)
|
|
||||||
await ctx.reply(
|
await ctx.reply(
|
||||||
f"The timer cap has been set to {value} hours."
|
f"USAGE: {ctx.prefix}subathon config [option [value]]\n"
|
||||||
|
f"Use '{ctx.prefix}subathon config' to see available options and current values."
|
||||||
)
|
)
|
||||||
else:
|
return
|
||||||
# Display the timer cap
|
|
||||||
cap = active.subathondata.timecap
|
parts = []
|
||||||
if cap:
|
|
||||||
hours = cap / 3600
|
sdata = active.subathondata
|
||||||
await ctx.reply(f"The timer cap is currently {hours} hours")
|
|
||||||
elif option.lower() == 'name':
|
# name
|
||||||
if value:
|
parts.append(
|
||||||
await active.subathondata.update(name=value)
|
f"name=\"{sdata.name}\""
|
||||||
await self.dispatch_update(active)
|
)
|
||||||
await ctx.reply(f"Updated the subathon name to \"{value}\"")
|
|
||||||
else:
|
# timecamp
|
||||||
name = active.subathondata.name
|
cap = sdata.timecap or 0
|
||||||
await ctx.reply(f"This subathon is called \"{name}\"")
|
caph = int(cap / 3600)
|
||||||
else:
|
parts.append(
|
||||||
|
f"cap={caph} (hours, 0 means no cap)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# scores
|
||||||
|
scores = map(float, (sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score))
|
||||||
|
scorestr = ' '.join(map(str, scores))
|
||||||
|
parts.append(
|
||||||
|
f"scores={scorestr} (t1, t2, t3, and 1 bit point scores)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# timescore
|
||||||
|
parts.append(
|
||||||
|
f"timescore={sdata.score_time} (seconds per point)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine
|
||||||
|
partstr = ' ; '.join(parts)
|
||||||
await ctx.reply(
|
await ctx.reply(
|
||||||
f"Unknown option {option}! Configurable options: 'name', 'cap'"
|
f"{partstr} ; Use {ctx.prefix}subathon config <option> [value] to see or set each option!"
|
||||||
|
)
|
||||||
|
|
||||||
|
@subathon_config_grp.command(name='name')
|
||||||
|
@cmds.is_moderator()
|
||||||
|
async def subathon_config_name_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
||||||
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is None:
|
||||||
|
await ctx.reply("No active subathon to configure!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if args:
|
||||||
|
# Setting path
|
||||||
|
await active.subathondata.update(name=args)
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
await ctx.reply(f"Updated the subathon name to \"{args}\"")
|
||||||
|
else:
|
||||||
|
# Display path
|
||||||
|
name = active.subathondata.name
|
||||||
|
await ctx.reply(
|
||||||
|
"Name of the subathon, used whenever the subathon is mentioned. "
|
||||||
|
"Accepts any string. "
|
||||||
|
f"Currently: \"{name}\" "
|
||||||
|
f"Example: {ctx.prefix}subathon config name Birthday Subathon"
|
||||||
|
)
|
||||||
|
|
||||||
|
@subathon_config_grp.command(name='cap')
|
||||||
|
@cmds.is_moderator()
|
||||||
|
async def subathon_config_cap_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
||||||
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is None:
|
||||||
|
await ctx.reply("No active subathon to configure!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if args:
|
||||||
|
# Setting path
|
||||||
|
if args.lower() in ('none',):
|
||||||
|
args = '0'
|
||||||
|
|
||||||
|
if not args.isdigit():
|
||||||
|
await ctx.reply("Provided timer cap must be an integer number of hours!")
|
||||||
|
return
|
||||||
|
|
||||||
|
new_cap = int(args)
|
||||||
|
if new_cap <= 0:
|
||||||
|
# Unset the cap
|
||||||
|
await active.subathondata.update(timecap=None)
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
await ctx.reply("The timer cap has been removed! To infinity and beyond!")
|
||||||
|
else:
|
||||||
|
# Set the cap
|
||||||
|
await active.subathondata.update(timecap=int(new_cap * 3600))
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
await ctx.reply(f"The subathon timer has been capped to {new_cap} hours.")
|
||||||
|
else:
|
||||||
|
# Display path
|
||||||
|
current_cap = active.subathondata.timecap or 0
|
||||||
|
caph = int(current_cap / 3600)
|
||||||
|
await ctx.reply(
|
||||||
|
"Duration cap for this subathon, in hours, including the initial time. "
|
||||||
|
"Contributions given after the cap has been reached will be accepted, "
|
||||||
|
"but will not raise the timer. "
|
||||||
|
"Accepts an integer, with 0 meaning no cap. "
|
||||||
|
f"Currently: {caph} hours. "
|
||||||
|
f"Example: {ctx.prefix}subathon config cap 24"
|
||||||
|
)
|
||||||
|
|
||||||
|
@subathon_config_grp.command(name='scores')
|
||||||
|
@cmds.is_moderator()
|
||||||
|
async def subathon_config_scores_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
||||||
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is None:
|
||||||
|
await ctx.reply("No active subathon to configure!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if args:
|
||||||
|
# Setting path
|
||||||
|
# Validate
|
||||||
|
splits = args.split()
|
||||||
|
if not len(splits) == 4 and all(split.isdecimal() for split in splits):
|
||||||
|
await ctx.reply(
|
||||||
|
f"USAGE: {ctx.prefix}subathon config socres [<t1score> <t2score> <t3score> <bitscore>]"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
t1score, t2score, t3score, bitscore = map(float, splits)
|
||||||
|
await active.subathondata.update(
|
||||||
|
sub1_score=t1score,
|
||||||
|
sub2_score=t2score,
|
||||||
|
sub3_score=t3score,
|
||||||
|
bit_score=bitscore,
|
||||||
|
)
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
await ctx.reply("Successfully updated subathon score table.")
|
||||||
|
else:
|
||||||
|
# Display path
|
||||||
|
sdata = active.subathondata
|
||||||
|
scores = map(float, (sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score))
|
||||||
|
scorestr = ' '.join(map(str, scores))
|
||||||
|
|
||||||
|
await ctx.reply(
|
||||||
|
"The number of points each type of contribution (t1 sub, t2 sub, t3 sub, and 1 bit) "
|
||||||
|
"will add to the subathon. Not retroactive. "
|
||||||
|
"Accepts four floats. "
|
||||||
|
f"Currently: {scorestr} "
|
||||||
|
f"Example: {ctx.prefix}subathon config scores 5 10 20 0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
@subathon_config_grp.command(name='timescore')
|
||||||
|
@cmds.is_moderator()
|
||||||
|
async def subathon_config_timescore_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
||||||
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
||||||
|
cid = community.communityid
|
||||||
|
if (active := await self.get_active_subathon(cid)) is None:
|
||||||
|
await ctx.reply("No active subathon to configure!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if args:
|
||||||
|
# Setting path
|
||||||
|
if not args.isdigit():
|
||||||
|
await ctx.reply("Time score (seconds per point) must be an integer.")
|
||||||
|
return
|
||||||
|
await active.subathondata.update(score_time=int(args))
|
||||||
|
await self.dispatch_update(active)
|
||||||
|
await ctx.reply("Subathon time score updated (NOTE: This is retroactive).")
|
||||||
|
else:
|
||||||
|
# Display path
|
||||||
|
await ctx.reply(
|
||||||
|
"The number of seconds each contributed point adds to the timer. "
|
||||||
|
"WARNING: This setting is retroactive and should generally not be used. "
|
||||||
|
"Accepts an integer. "
|
||||||
|
f"Currently: {active.subathondata.score_time} (seconds per point) "
|
||||||
|
f"Example: {ctx.prefix}subathon config timescore 10"
|
||||||
)
|
)
|
||||||
|
|
||||||
@group_subathon.command(name='leaderboard', aliases=('top', 'lb',))
|
@group_subathon.command(name='leaderboard', aliases=('top', 'lb',))
|
||||||
@@ -540,11 +797,11 @@ class SubathonComponent(cmds.Component):
|
|||||||
line = f"{goal.required_score} points: {goal.description}"
|
line = f"{goal.required_score} points: {goal.description}"
|
||||||
goalstrs.append(line)
|
goalstrs.append(line)
|
||||||
|
|
||||||
if goalstrs
|
if goalstrs:
|
||||||
text = ', '.join(goalstrs)
|
text = ', '.join(goalstrs)
|
||||||
await ctx.reply(f"{active.name} Goals Remaining -- {text}")
|
await ctx.reply(f"{active.name} Goals Remaining -- {text}")
|
||||||
elif goals:
|
elif goals:
|
||||||
await ctx.reply(f"All goals completed, congratulations!")
|
await ctx.reply("All goals completed, congratulations!")
|
||||||
else:
|
else:
|
||||||
await ctx.reply("No goals have been configured!")
|
await ctx.reply("No goals have been configured!")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class ActiveSubathon:
|
|||||||
"""
|
"""
|
||||||
Return True if the subathon duration has exceeded its earned time.
|
Return True if the subathon duration has exceeded its earned time.
|
||||||
"""
|
"""
|
||||||
return (await self.get_duration() <= 0)
|
return (await self.get_remaining() <= 0)
|
||||||
|
|
||||||
async def pause(self):
|
async def pause(self):
|
||||||
"""
|
"""
|
||||||
@@ -97,10 +97,10 @@ class ActiveSubathon:
|
|||||||
async def get_remaining(self) -> int:
|
async def get_remaining(self) -> int:
|
||||||
"""
|
"""
|
||||||
Number of seconds remaining on the subathon timer.
|
Number of seconds remaining on the subathon timer.
|
||||||
Will be 0 if finished.
|
Will be 0 or slightly negative if finished.
|
||||||
"""
|
"""
|
||||||
total_time = await self.get_earned()
|
total_time = await self.get_earned()
|
||||||
return total_time - await self.get_duration()
|
return total_time - await self.get_duration() - 1
|
||||||
|
|
||||||
async def get_ending(self):
|
async def get_ending(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user