|
|
|
@@ -13,7 +13,13 @@ from meta.sockets import Channel, register_channel
|
|
|
|
from utils.lib import utc_now, strfdelta
|
|
|
|
from utils.lib import utc_now, strfdelta
|
|
|
|
|
|
|
|
|
|
|
|
from . import logger
|
|
|
|
from . import logger
|
|
|
|
from .data import SubathonData, Subathon, RunningSubathon, SubathonContribution, SubathonGoal
|
|
|
|
from .data import (
|
|
|
|
|
|
|
|
SubathonData,
|
|
|
|
|
|
|
|
Subathon,
|
|
|
|
|
|
|
|
RunningSubathon,
|
|
|
|
|
|
|
|
SubathonContribution,
|
|
|
|
|
|
|
|
SubathonGoal,
|
|
|
|
|
|
|
|
)
|
|
|
|
from .subathon import ActiveSubathon, SubathonRegistry
|
|
|
|
from .subathon import ActiveSubathon, SubathonRegistry
|
|
|
|
from .channel import SubathonPayload, prepare_subathon, TimerChannel
|
|
|
|
from .channel import SubathonPayload, prepare_subathon, TimerChannel
|
|
|
|
|
|
|
|
|
|
|
|
@@ -49,6 +55,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
Description:
|
|
|
|
Description:
|
|
|
|
Examples:
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
@@ -58,7 +65,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
self.subathons = SubathonRegistry(self.data, bot.profiles.profiles)
|
|
|
|
self.subathons = SubathonRegistry(self.data, bot.profiles.profiles)
|
|
|
|
self.channel = TimerChannel(self.bot.profiles.profiles, self.subathons)
|
|
|
|
self.channel = TimerChannel(self.bot.profiles.profiles, self.subathons)
|
|
|
|
|
|
|
|
|
|
|
|
register_channel('SubTimer', self.channel)
|
|
|
|
register_channel("SubTimer", self.channel)
|
|
|
|
|
|
|
|
|
|
|
|
# ----- API -----
|
|
|
|
# ----- API -----
|
|
|
|
async def component_load(self):
|
|
|
|
async def component_load(self):
|
|
|
|
@@ -98,20 +105,24 @@ class SubathonComponent(cmds.Component):
|
|
|
|
# Blerp handler
|
|
|
|
# Blerp handler
|
|
|
|
@cmds.Component.listener()
|
|
|
|
@cmds.Component.listener()
|
|
|
|
async def event_message(self, message: twitchio.ChatMessage):
|
|
|
|
async def event_message(self, message: twitchio.ChatMessage):
|
|
|
|
if message.chatter.id not in ('253326823', '1361913054'):
|
|
|
|
if message.chatter.id not in ("253326823", "1361913054"):
|
|
|
|
return
|
|
|
|
return
|
|
|
|
if 'bits' not in message.text:
|
|
|
|
if "bits" not in message.text:
|
|
|
|
return
|
|
|
|
return
|
|
|
|
community = await self.bot.profiles.fetch_community(message.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(message.broadcaster)
|
|
|
|
cid = community.communityid
|
|
|
|
cid = community.communityid
|
|
|
|
if (active := await self.get_active_subathon(cid)) is not None and not await active.check_finished():
|
|
|
|
if (
|
|
|
|
|
|
|
|
active := await self.get_active_subathon(cid)
|
|
|
|
|
|
|
|
) is not None and not await active.check_finished():
|
|
|
|
# Extract count and user
|
|
|
|
# Extract count and user
|
|
|
|
match = re.match(r"(?P<name>\w+) used (?P<amount>\d+)", message.text)
|
|
|
|
match = re.match(r"(?P<name>\w+) used (?P<amount>\d+)", message.text)
|
|
|
|
if not match:
|
|
|
|
if not match:
|
|
|
|
match = re.match(r"!For (?P<amount>\d+) bits, (?P<name>\w+)", message.text)
|
|
|
|
match = re.match(
|
|
|
|
|
|
|
|
r"!For (?P<amount>\d+) bits, (?P<name>\w+)", message.text
|
|
|
|
|
|
|
|
)
|
|
|
|
if match:
|
|
|
|
if match:
|
|
|
|
amount = int(match['amount'])
|
|
|
|
amount = int(match["amount"])
|
|
|
|
name = match['name']
|
|
|
|
name = match["name"]
|
|
|
|
# This is for giving the contribution to the calling user
|
|
|
|
# This is for giving the contribution to the calling user
|
|
|
|
# user = await self.bot.fetch_user(login=name.lower())
|
|
|
|
# user = await self.bot.fetch_user(login=name.lower())
|
|
|
|
user = await message.chatter.user()
|
|
|
|
user = await message.chatter.user()
|
|
|
|
@@ -128,12 +139,15 @@ class SubathonComponent(cmds.Component):
|
|
|
|
@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
|
|
|
|
if (active := await self.get_active_subathon(event_row['communityid'])) is not None and not await active.check_finished():
|
|
|
|
if (
|
|
|
|
|
|
|
|
active := await self.get_active_subathon(event_row["communityid"])
|
|
|
|
|
|
|
|
) is not None and not await active.check_finished():
|
|
|
|
# In an active subathon
|
|
|
|
# In an active subathon
|
|
|
|
pid = event_row['profileid']
|
|
|
|
pid = event_row["profileid"]
|
|
|
|
uid = event_row['user_id']
|
|
|
|
uid = event_row["user_id"]
|
|
|
|
score = detail_row['bits'] * active.subathondata.bit_score
|
|
|
|
score = detail_row["bits"] * active.subathondata.bit_score
|
|
|
|
await active.add_contribution(pid, score, event_row['event_id'])
|
|
|
|
logger.info(f"Adding subathon contribution from bits payload {payload}")
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row["event_id"])
|
|
|
|
|
|
|
|
|
|
|
|
# Send message to channel
|
|
|
|
# Send message to channel
|
|
|
|
sec = active.get_score_time(score)
|
|
|
|
sec = active.get_score_time(score)
|
|
|
|
@@ -143,7 +157,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
added = f"{sec} seconds"
|
|
|
|
added = f"{sec} seconds"
|
|
|
|
name = bits_payload.user.name
|
|
|
|
name = bits_payload.user.name
|
|
|
|
pl = 's' if bits_payload.bits != 1 else ''
|
|
|
|
pl = "s" if bits_payload.bits != 1 else ""
|
|
|
|
|
|
|
|
|
|
|
|
contrib_str = f"{name} contributed {score} point{pl}"
|
|
|
|
contrib_str = f"{name} contributed {score} point{pl}"
|
|
|
|
if not await active.check_cap():
|
|
|
|
if not await active.check_cap():
|
|
|
|
@@ -152,8 +166,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
|
|
|
|
|
|
|
|
await bits_payload.broadcaster.send_message(
|
|
|
|
await bits_payload.broadcaster.send_message(
|
|
|
|
contrib_str,
|
|
|
|
contrib_str, sender=self.bot.bot_id
|
|
|
|
sender=self.bot.bot_id
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.goalcheck(active, bits_payload.broadcaster)
|
|
|
|
await self.goalcheck(active, bits_payload.broadcaster)
|
|
|
|
@@ -166,10 +179,12 @@ class SubathonComponent(cmds.Component):
|
|
|
|
# Ignore gifted here
|
|
|
|
# Ignore gifted here
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if (active := await self.get_active_subathon(event_row['communityid'])) is not None and not await active.check_finished():
|
|
|
|
if (
|
|
|
|
|
|
|
|
active := await self.get_active_subathon(event_row["communityid"])
|
|
|
|
|
|
|
|
) is not None and not await active.check_finished():
|
|
|
|
data = active.subathondata
|
|
|
|
data = active.subathondata
|
|
|
|
# In an active subathon
|
|
|
|
# In an active subathon
|
|
|
|
pid = event_row['profileid']
|
|
|
|
pid = event_row["profileid"]
|
|
|
|
tier = int(sub_payload.tier)
|
|
|
|
tier = int(sub_payload.tier)
|
|
|
|
|
|
|
|
|
|
|
|
if tier == 1000:
|
|
|
|
if tier == 1000:
|
|
|
|
@@ -183,13 +198,16 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
score = mult * 1
|
|
|
|
score = mult * 1
|
|
|
|
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row['event_id'])
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Adding subathon contribution from subscription payload {payload}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row["event_id"])
|
|
|
|
|
|
|
|
|
|
|
|
# Send message to channel
|
|
|
|
# Send message to channel
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
name = sub_payload.user.name
|
|
|
|
name = sub_payload.user.name
|
|
|
|
pl = 's' if score > 1 else ''
|
|
|
|
pl = "s" if score > 1 else ""
|
|
|
|
|
|
|
|
|
|
|
|
contrib_str = f"{name} contributed {score} point{pl}"
|
|
|
|
contrib_str = f"{name} contributed {score} point{pl}"
|
|
|
|
if not await active.check_cap():
|
|
|
|
if not await active.check_cap():
|
|
|
|
@@ -198,8 +216,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
|
|
|
|
|
|
|
|
await sub_payload.broadcaster.send_message(
|
|
|
|
await sub_payload.broadcaster.send_message(
|
|
|
|
contrib_str,
|
|
|
|
contrib_str, sender=self.bot.bot_id
|
|
|
|
sender=self.bot.bot_id
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
# Check goals
|
|
|
|
# Check goals
|
|
|
|
@@ -209,10 +226,12 @@ class SubathonComponent(cmds.Component):
|
|
|
|
async def event_safe_subscription_gift(self, payload):
|
|
|
|
async def event_safe_subscription_gift(self, payload):
|
|
|
|
event_row, detail_row, gift_payload = payload
|
|
|
|
event_row, detail_row, gift_payload = payload
|
|
|
|
|
|
|
|
|
|
|
|
if (active := await self.get_active_subathon(event_row['communityid'])) is not None and not await active.check_finished():
|
|
|
|
if (
|
|
|
|
|
|
|
|
active := await self.get_active_subathon(event_row["communityid"])
|
|
|
|
|
|
|
|
) is not None and not await active.check_finished():
|
|
|
|
data = active.subathondata
|
|
|
|
data = active.subathondata
|
|
|
|
# In an active subathon
|
|
|
|
# In an active subathon
|
|
|
|
pid = event_row['profileid']
|
|
|
|
pid = event_row["profileid"]
|
|
|
|
|
|
|
|
|
|
|
|
tier = int(gift_payload.tier)
|
|
|
|
tier = int(gift_payload.tier)
|
|
|
|
if tier == 1000:
|
|
|
|
if tier == 1000:
|
|
|
|
@@ -226,12 +245,15 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
score = mult * gift_payload.total
|
|
|
|
score = mult * gift_payload.total
|
|
|
|
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row['event_id'])
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Adding subathon contribution from subscription gift payload {payload}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row["event_id"])
|
|
|
|
|
|
|
|
|
|
|
|
# Send message to channel
|
|
|
|
# Send message to channel
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
name = gift_payload.user.name if gift_payload.user else 'Anonymous'
|
|
|
|
name = gift_payload.user.name if gift_payload.user else "Anonymous"
|
|
|
|
|
|
|
|
|
|
|
|
contrib_str = f"{name} contributed {score} points"
|
|
|
|
contrib_str = f"{name} contributed {score} points"
|
|
|
|
if not await active.check_cap():
|
|
|
|
if not await active.check_cap():
|
|
|
|
@@ -240,8 +262,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
|
|
|
|
|
|
|
|
await gift_payload.broadcaster.send_message(
|
|
|
|
await gift_payload.broadcaster.send_message(
|
|
|
|
contrib_str,
|
|
|
|
contrib_str, sender=self.bot.bot_id
|
|
|
|
sender=self.bot.bot_id
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
# Check goals
|
|
|
|
# Check goals
|
|
|
|
@@ -251,10 +272,12 @@ class SubathonComponent(cmds.Component):
|
|
|
|
async def event_safe_subscription_message(self, payload):
|
|
|
|
async def event_safe_subscription_message(self, payload):
|
|
|
|
event_row, detail_row, sub_payload = payload
|
|
|
|
event_row, detail_row, sub_payload = payload
|
|
|
|
|
|
|
|
|
|
|
|
if (active := await self.get_active_subathon(event_row['communityid'])) is not None and not await active.check_finished():
|
|
|
|
if (
|
|
|
|
|
|
|
|
active := await self.get_active_subathon(event_row["communityid"])
|
|
|
|
|
|
|
|
) is not None and not await active.check_finished():
|
|
|
|
data = active.subathondata
|
|
|
|
data = active.subathondata
|
|
|
|
# In an active subathon
|
|
|
|
# In an active subathon
|
|
|
|
pid = event_row['profileid']
|
|
|
|
pid = event_row["profileid"]
|
|
|
|
tier = int(sub_payload.tier)
|
|
|
|
tier = int(sub_payload.tier)
|
|
|
|
|
|
|
|
|
|
|
|
if tier == 1000:
|
|
|
|
if tier == 1000:
|
|
|
|
@@ -268,13 +291,16 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
score = mult * 1
|
|
|
|
score = mult * 1
|
|
|
|
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row['event_id'])
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Adding subathon contribution from subscription message payload {payload}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
await active.add_contribution(pid, score, event_row["event_id"])
|
|
|
|
|
|
|
|
|
|
|
|
# Send message to channel
|
|
|
|
# Send message to channel
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added_min = int(active.get_score_time(score) // 60)
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
added = f"{added_min} minutes"
|
|
|
|
name = sub_payload.user.name
|
|
|
|
name = sub_payload.user.name
|
|
|
|
pl = 's' if score > 1 else ''
|
|
|
|
pl = "s" if score > 1 else ""
|
|
|
|
|
|
|
|
|
|
|
|
contrib_str = f"{name} contributed {score} points{pl}"
|
|
|
|
contrib_str = f"{name} contributed {score} points{pl}"
|
|
|
|
if not await active.check_cap():
|
|
|
|
if not await active.check_cap():
|
|
|
|
@@ -283,8 +309,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
contrib_str += " towards our subathon! Thank you <3"
|
|
|
|
|
|
|
|
|
|
|
|
await sub_payload.broadcaster.send_message(
|
|
|
|
await sub_payload.broadcaster.send_message(
|
|
|
|
contrib_str,
|
|
|
|
contrib_str, sender=self.bot.bot_id
|
|
|
|
sender=self.bot.bot_id
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
# Check goals
|
|
|
|
# Check goals
|
|
|
|
@@ -297,17 +322,20 @@ class SubathonComponent(cmds.Component):
|
|
|
|
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 not None:
|
|
|
|
if active.running:
|
|
|
|
if active.running:
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Automatically paused suabathon {active.subathondata!r} from stream offline."
|
|
|
|
|
|
|
|
)
|
|
|
|
await active.pause()
|
|
|
|
await active.pause()
|
|
|
|
if not await active.check_finished():
|
|
|
|
if not await active.check_finished():
|
|
|
|
await payload.broadcaster.send_message(
|
|
|
|
await payload.broadcaster.send_message(
|
|
|
|
"Paused the subathon timer because the stream went offline!",
|
|
|
|
"Paused the subathon timer because the stream went offline!",
|
|
|
|
sender=self.bot.bot_id
|
|
|
|
sender=self.bot.bot_id,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
|
|
|
|
|
|
|
|
# ----- Commands -----
|
|
|
|
# ----- Commands -----
|
|
|
|
|
|
|
|
|
|
|
|
@cmds.group(name='subathon', aliases=['studython'], invoke_fallback=True)
|
|
|
|
@cmds.group(name="subathon", aliases=["studython"], invoke_fallback=True)
|
|
|
|
async def group_subathon(self, ctx: cmds.Context):
|
|
|
|
async def group_subathon(self, ctx: cmds.Context):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
cid = community.communityid
|
|
|
|
cid = community.communityid
|
|
|
|
@@ -325,13 +353,9 @@ class SubathonComponent(cmds.Component):
|
|
|
|
if secs > 0:
|
|
|
|
if secs > 0:
|
|
|
|
remaining = strfdelta(timedelta(seconds=secs))
|
|
|
|
remaining = strfdelta(timedelta(seconds=secs))
|
|
|
|
|
|
|
|
|
|
|
|
text = (
|
|
|
|
text = f"{active.name} running for {duration}! {score} points recieved, {goalstr}, and {remaining} left on the timer"
|
|
|
|
f"{active.name} running for {duration}! {score} points recieved, {goalstr}, and {remaining} left on the timer"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
text = (
|
|
|
|
text = f"{active.name} completed after {duration} with a total of {score} points, and {goalstr}!"
|
|
|
|
f"{active.name} completed after {duration} with a total of {score} points, and {goalstr}!"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await ctx.reply(text)
|
|
|
|
await ctx.reply(text)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
@@ -339,9 +363,19 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
# subathon start
|
|
|
|
# subathon start
|
|
|
|
# TODO: Usage line on command error
|
|
|
|
# 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, 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]
|
|
|
|
@@ -357,7 +391,9 @@ class SubathonComponent(cmds.Component):
|
|
|
|
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 not None:
|
|
|
|
await ctx.reply("There is already an active subathon running! Use !subathon stop to stop it!")
|
|
|
|
await ctx.reply(
|
|
|
|
|
|
|
|
"There is already an active subathon running! Use !subathon stop to stop it!"
|
|
|
|
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
initial_time = initial_hours * 60 * 60
|
|
|
|
initial_time = initial_hours * 60 * 60
|
|
|
|
timecap_seconds = timecap * 60 * 60 if timecap else None
|
|
|
|
timecap_seconds = timecap * 60 * 60 if timecap else None
|
|
|
|
@@ -371,9 +407,9 @@ class SubathonComponent(cmds.Component):
|
|
|
|
sub3_score=sub3,
|
|
|
|
sub3_score=sub3,
|
|
|
|
bit_score=bit,
|
|
|
|
bit_score=bit,
|
|
|
|
score_time=timescore,
|
|
|
|
score_time=timescore,
|
|
|
|
timecap=timecap_seconds
|
|
|
|
timecap=timecap_seconds,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
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(
|
|
|
|
await ctx.reply(
|
|
|
|
f"Setup your subathon! "
|
|
|
|
f"Setup your subathon! "
|
|
|
|
@@ -385,7 +421,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
|
|
|
|
|
|
|
|
# subathon stop
|
|
|
|
# subathon stop
|
|
|
|
@group_subathon.command(name='stop')
|
|
|
|
@group_subathon.command(name="stop")
|
|
|
|
@cmds.is_broadcaster()
|
|
|
|
@cmds.is_broadcaster()
|
|
|
|
async def cmd_stop(self, ctx: cmds.Context):
|
|
|
|
async def cmd_stop(self, ctx: cmds.Context):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
@@ -406,22 +442,20 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply("No active subathon to stop.")
|
|
|
|
await ctx.reply("No active subathon to stop.")
|
|
|
|
|
|
|
|
|
|
|
|
# subathon link
|
|
|
|
# subathon link
|
|
|
|
@group_subathon.command(name='link')
|
|
|
|
@group_subathon.command(name="link")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def cmd_link(self, ctx: cmds.Context):
|
|
|
|
async def cmd_link(self, ctx: cmds.Context):
|
|
|
|
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 not None:
|
|
|
|
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(
|
|
|
|
await ctx.reply(timer_link)
|
|
|
|
timer_link
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
await ctx.reply("No active subathon to stop.")
|
|
|
|
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()
|
|
|
|
async def cmd_pause(self, ctx: cmds.Context):
|
|
|
|
async def cmd_pause(self, ctx: cmds.Context):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
@@ -437,7 +471,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply("No active subathon to pause")
|
|
|
|
await ctx.reply("No active subathon to pause")
|
|
|
|
|
|
|
|
|
|
|
|
# subathon resume
|
|
|
|
# subathon resume
|
|
|
|
@group_subathon.command(name='resume')
|
|
|
|
@group_subathon.command(name="resume")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def cmd_resume(self, ctx: cmds.Context):
|
|
|
|
async def cmd_resume(self, ctx: cmds.Context):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
@@ -455,9 +489,11 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply("No active subathon to resume")
|
|
|
|
await ctx.reply("No active subathon to resume")
|
|
|
|
|
|
|
|
|
|
|
|
# subathon adjust
|
|
|
|
# subathon adjust
|
|
|
|
@group_subathon.command(name='adjust')
|
|
|
|
@group_subathon.command(name="adjust")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def cmd_subathon_adjust(self, ctx: Context, amount: int, *, user: Optional[twitchio.User] = None):
|
|
|
|
async def cmd_subathon_adjust(
|
|
|
|
|
|
|
|
self, ctx: Context, amount: int, *, user: Optional[twitchio.User] = None
|
|
|
|
|
|
|
|
):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Directly add or remove points from the subathon.
|
|
|
|
Directly add or remove points from the subathon.
|
|
|
|
If a user is provided, will adjust their contributed amount.
|
|
|
|
If a user is provided, will adjust their contributed amount.
|
|
|
|
@@ -476,7 +512,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
if (active := await self.get_active_subathon(cid)) is not None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is not None:
|
|
|
|
if user is not None:
|
|
|
|
if user is not None:
|
|
|
|
profile = await self.bot.profiles.fetch_profile(user)
|
|
|
|
profile = await self.bot.profiles.fetch_profile(user)
|
|
|
|
name = user.display_name or profile.nickname or 'Unknown'
|
|
|
|
name = user.display_name or profile.nickname or "Unknown"
|
|
|
|
pid = profile.profileid
|
|
|
|
pid = profile.profileid
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
profile = None
|
|
|
|
profile = None
|
|
|
|
@@ -524,7 +560,8 @@ class SubathonComponent(cmds.Component):
|
|
|
|
WARNING: This setting is retroactive and should generally not be changed.
|
|
|
|
WARNING: This setting is retroactive and should generally not be changed.
|
|
|
|
Accepts an integer. Example: !subathon config timecap 10
|
|
|
|
Accepts an integer. Example: !subathon config timecap 10
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
@group_subathon.group(name='config', aliases=('option',), invoke_fallback=True)
|
|
|
|
|
|
|
|
|
|
|
|
@group_subathon.group(name="config", aliases=("option",), invoke_fallback=True)
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def subathon_config_grp(self, ctx: Context, *, args: 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)
|
|
|
|
@@ -544,38 +581,35 @@ class SubathonComponent(cmds.Component):
|
|
|
|
sdata = active.subathondata
|
|
|
|
sdata = active.subathondata
|
|
|
|
|
|
|
|
|
|
|
|
# name
|
|
|
|
# name
|
|
|
|
parts.append(
|
|
|
|
parts.append(f'name="{sdata.name}"')
|
|
|
|
f"name=\"{sdata.name}\""
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# timecamp
|
|
|
|
# timecamp
|
|
|
|
cap = sdata.timecap or 0
|
|
|
|
cap = sdata.timecap or 0
|
|
|
|
caph = int(cap / 3600)
|
|
|
|
caph = int(cap / 3600)
|
|
|
|
parts.append(
|
|
|
|
parts.append(f"cap={caph} (hours, 0 means no cap)")
|
|
|
|
f"cap={caph} (hours, 0 means no cap)"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# scores
|
|
|
|
# scores
|
|
|
|
scores = map(float, (sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score))
|
|
|
|
scores = map(
|
|
|
|
scorestr = ' '.join(map(str, scores))
|
|
|
|
float,
|
|
|
|
parts.append(
|
|
|
|
(sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score),
|
|
|
|
f"scores={scorestr} (t1, t2, t3, and 1 bit point scores)"
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
scorestr = " ".join(map(str, scores))
|
|
|
|
|
|
|
|
parts.append(f"scores={scorestr} (t1, t2, t3, and 1 bit point scores)")
|
|
|
|
|
|
|
|
|
|
|
|
# timescore
|
|
|
|
# timescore
|
|
|
|
parts.append(
|
|
|
|
parts.append(f"timescore={sdata.score_time} (seconds per point)")
|
|
|
|
f"timescore={sdata.score_time} (seconds per point)"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Combine
|
|
|
|
# Combine
|
|
|
|
partstr = ' ; '.join(parts)
|
|
|
|
partstr = " ; ".join(parts)
|
|
|
|
await ctx.reply(
|
|
|
|
await ctx.reply(
|
|
|
|
f"{partstr} ; Use {ctx.prefix}subathon config <option> [value] to see or set each option!"
|
|
|
|
f"{partstr} ; Use {ctx.prefix}subathon config <option> [value] to see or set each option!"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@subathon_config_grp.command(name='name')
|
|
|
|
@subathon_config_grp.command(name="name")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def subathon_config_name_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
|
|
|
async def subathon_config_name_cmd(
|
|
|
|
|
|
|
|
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 None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is None:
|
|
|
|
@@ -586,20 +620,22 @@ class SubathonComponent(cmds.Component):
|
|
|
|
# Setting path
|
|
|
|
# Setting path
|
|
|
|
await active.subathondata.update(name=args)
|
|
|
|
await active.subathondata.update(name=args)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await ctx.reply(f"Updated the subathon name to \"{args}\"")
|
|
|
|
await ctx.reply(f'Updated the subathon name to "{args}"')
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# Display path
|
|
|
|
# Display path
|
|
|
|
name = active.subathondata.name
|
|
|
|
name = active.subathondata.name
|
|
|
|
await ctx.reply(
|
|
|
|
await ctx.reply(
|
|
|
|
"Name of the subathon, used whenever the subathon is mentioned. "
|
|
|
|
"Name of the subathon, used whenever the subathon is mentioned. "
|
|
|
|
"Accepts any string. "
|
|
|
|
"Accepts any string. "
|
|
|
|
f"Currently: \"{name}\" "
|
|
|
|
f'Currently: "{name}" '
|
|
|
|
f"Example: {ctx.prefix}subathon config name Birthday Subathon"
|
|
|
|
f"Example: {ctx.prefix}subathon config name Birthday Subathon"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@subathon_config_grp.command(name='cap')
|
|
|
|
@subathon_config_grp.command(name="cap")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def subathon_config_cap_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
|
|
|
async def subathon_config_cap_cmd(
|
|
|
|
|
|
|
|
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 None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is None:
|
|
|
|
@@ -608,11 +644,13 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
if args:
|
|
|
|
if args:
|
|
|
|
# Setting path
|
|
|
|
# Setting path
|
|
|
|
if args.lower() in ('none',):
|
|
|
|
if args.lower() in ("none",):
|
|
|
|
args = '0'
|
|
|
|
args = "0"
|
|
|
|
|
|
|
|
|
|
|
|
if not args.isdigit():
|
|
|
|
if not args.isdigit():
|
|
|
|
await ctx.reply("Provided timer cap must be an integer number of hours!")
|
|
|
|
await ctx.reply(
|
|
|
|
|
|
|
|
"Provided timer cap must be an integer number of hours!"
|
|
|
|
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
new_cap = int(args)
|
|
|
|
new_cap = int(args)
|
|
|
|
@@ -620,12 +658,16 @@ class SubathonComponent(cmds.Component):
|
|
|
|
# Unset the cap
|
|
|
|
# Unset the cap
|
|
|
|
await active.subathondata.update(timecap=None)
|
|
|
|
await active.subathondata.update(timecap=None)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await ctx.reply("The timer cap has been removed! To infinity and beyond!")
|
|
|
|
await ctx.reply(
|
|
|
|
|
|
|
|
"The timer cap has been removed! To infinity and beyond!"
|
|
|
|
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# Set the cap
|
|
|
|
# Set the cap
|
|
|
|
await active.subathondata.update(timecap=int(new_cap * 3600))
|
|
|
|
await active.subathondata.update(timecap=int(new_cap * 3600))
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await self.dispatch_update(active)
|
|
|
|
await ctx.reply(f"The subathon timer has been capped to {new_cap} hours.")
|
|
|
|
await ctx.reply(
|
|
|
|
|
|
|
|
f"The subathon timer has been capped to {new_cap} hours."
|
|
|
|
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# Display path
|
|
|
|
# Display path
|
|
|
|
current_cap = active.subathondata.timecap or 0
|
|
|
|
current_cap = active.subathondata.timecap or 0
|
|
|
|
@@ -639,9 +681,11 @@ class SubathonComponent(cmds.Component):
|
|
|
|
f"Example: {ctx.prefix}subathon config cap 24"
|
|
|
|
f"Example: {ctx.prefix}subathon config cap 24"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@subathon_config_grp.command(name='scores')
|
|
|
|
@subathon_config_grp.command(name="scores")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def subathon_config_scores_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
|
|
|
async def subathon_config_scores_cmd(
|
|
|
|
|
|
|
|
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 None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is None:
|
|
|
|
@@ -669,8 +713,11 @@ class SubathonComponent(cmds.Component):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# Display path
|
|
|
|
# Display path
|
|
|
|
sdata = active.subathondata
|
|
|
|
sdata = active.subathondata
|
|
|
|
scores = map(float, (sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score))
|
|
|
|
scores = map(
|
|
|
|
scorestr = ' '.join(map(str, scores))
|
|
|
|
float,
|
|
|
|
|
|
|
|
(sdata.sub1_score, sdata.sub2_score, sdata.sub3_score, sdata.bit_score),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
scorestr = " ".join(map(str, scores))
|
|
|
|
|
|
|
|
|
|
|
|
await ctx.reply(
|
|
|
|
await ctx.reply(
|
|
|
|
"The number of points each type of contribution (t1 sub, t2 sub, t3 sub, and 1 bit) "
|
|
|
|
"The number of points each type of contribution (t1 sub, t2 sub, t3 sub, and 1 bit) "
|
|
|
|
@@ -680,9 +727,11 @@ class SubathonComponent(cmds.Component):
|
|
|
|
f"Example: {ctx.prefix}subathon config scores 5 10 20 0.1"
|
|
|
|
f"Example: {ctx.prefix}subathon config scores 5 10 20 0.1"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@subathon_config_grp.command(name='timescore')
|
|
|
|
@subathon_config_grp.command(name="timescore")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def subathon_config_timescore_cmd(self, ctx: Context, *, args: Optional[str] = None):
|
|
|
|
async def subathon_config_timescore_cmd(
|
|
|
|
|
|
|
|
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 None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is None:
|
|
|
|
@@ -707,7 +756,13 @@ class SubathonComponent(cmds.Component):
|
|
|
|
f"Example: {ctx.prefix}subathon config timescore 10"
|
|
|
|
f"Example: {ctx.prefix}subathon config timescore 10"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@group_subathon.command(name='leaderboard', aliases=('top', 'lb',))
|
|
|
|
@group_subathon.command(
|
|
|
|
|
|
|
|
name="leaderboard",
|
|
|
|
|
|
|
|
aliases=(
|
|
|
|
|
|
|
|
"top",
|
|
|
|
|
|
|
|
"lb",
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
async def cmd_subathon_lb(self, ctx: Context):
|
|
|
|
async def cmd_subathon_lb(self, ctx: Context):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Display top contributors by points contributed, up to 10.
|
|
|
|
Display top contributors by points contributed, up to 10.
|
|
|
|
@@ -722,34 +777,36 @@ class SubathonComponent(cmds.Component):
|
|
|
|
|
|
|
|
|
|
|
|
if (active := await self.get_active_subathon(cid)) is not None:
|
|
|
|
if (active := await self.get_active_subathon(cid)) is not None:
|
|
|
|
# Get totals for all contributors
|
|
|
|
# Get totals for all contributors
|
|
|
|
query = self.data.subathon_contributions.select_where(subathon_id=active.subathondata.subathon_id)
|
|
|
|
query = self.data.subathon_contributions.select_where(
|
|
|
|
query.join('user_profiles', using=('profileid',), join_type=JOINTYPE.LEFT)
|
|
|
|
subathon_id=active.subathondata.subathon_id
|
|
|
|
query.select('subathon_id', 'profileid', total="SUM(score)")
|
|
|
|
)
|
|
|
|
query.order_by('total', direction=ORDER.DESC)
|
|
|
|
query.join("user_profiles", using=("profileid",), join_type=JOINTYPE.LEFT)
|
|
|
|
query.group_by('subathon_id', 'profileid')
|
|
|
|
query.select("subathon_id", "profileid", total="SUM(score)")
|
|
|
|
|
|
|
|
query.order_by("total", direction=ORDER.DESC)
|
|
|
|
|
|
|
|
query.group_by("subathon_id", "profileid")
|
|
|
|
query.with_no_adapter()
|
|
|
|
query.with_no_adapter()
|
|
|
|
results = await query
|
|
|
|
results = await query
|
|
|
|
|
|
|
|
|
|
|
|
parts = []
|
|
|
|
parts = []
|
|
|
|
caller_idx = None
|
|
|
|
caller_idx = None
|
|
|
|
for i, row in enumerate(results):
|
|
|
|
for i, row in enumerate(results):
|
|
|
|
if pid := row['profileid']:
|
|
|
|
if pid := row["profileid"]:
|
|
|
|
profile = await self.bot.profiles.profiles.get_profile(pid)
|
|
|
|
profile = await self.bot.profiles.profiles.get_profile(pid)
|
|
|
|
name = (profile.nickname if profile else None) or 'Unknown'
|
|
|
|
name = (profile.nickname if profile else None) or "Unknown"
|
|
|
|
if pid == caller_pid:
|
|
|
|
if pid == caller_pid:
|
|
|
|
caller_idx = i
|
|
|
|
caller_idx = i
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
name = 'Anonymous'
|
|
|
|
name = "Anonymous"
|
|
|
|
score = row['total']
|
|
|
|
score = row["total"]
|
|
|
|
part = f"{name}: {score} points"
|
|
|
|
part = f"{name}: {score} points"
|
|
|
|
parts.append(part)
|
|
|
|
parts.append(part)
|
|
|
|
|
|
|
|
|
|
|
|
header = ""
|
|
|
|
header = ""
|
|
|
|
leaderboard = ', '.join(parts[:10])
|
|
|
|
leaderboard = ", ".join(parts[:10])
|
|
|
|
footer = ""
|
|
|
|
footer = ""
|
|
|
|
if len(parts) > 10:
|
|
|
|
if len(parts) > 10:
|
|
|
|
header = f"{active.name} top 10 leaderboard: "
|
|
|
|
header = f"{active.name} top 10 leaderboard: "
|
|
|
|
leaderboard = ', '.join(parts[:10])
|
|
|
|
leaderboard = ", ".join(parts[:10])
|
|
|
|
if caller_idx is not None and caller_idx >= 10:
|
|
|
|
if caller_idx is not None and caller_idx >= 10:
|
|
|
|
caller_part = parts[caller_idx]
|
|
|
|
caller_part = parts[caller_idx]
|
|
|
|
footer = f" ... {caller_part}"
|
|
|
|
footer = f" ... {caller_part}"
|
|
|
|
@@ -764,7 +821,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply("No active subathon to show leaderboard of!")
|
|
|
|
await ctx.reply("No active subathon to show leaderboard of!")
|
|
|
|
|
|
|
|
|
|
|
|
# Subathon goals
|
|
|
|
# Subathon goals
|
|
|
|
@cmds.group(name='goals', invoke_fallback=True)
|
|
|
|
@cmds.group(name="goals", invoke_fallback=True)
|
|
|
|
async def group_goals(self, ctx: cmds.Context):
|
|
|
|
async def group_goals(self, ctx: cmds.Context):
|
|
|
|
# List the goals
|
|
|
|
# List the goals
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
@@ -777,14 +834,14 @@ class SubathonComponent(cmds.Component):
|
|
|
|
goalstrs.append(line)
|
|
|
|
goalstrs.append(line)
|
|
|
|
|
|
|
|
|
|
|
|
if goals:
|
|
|
|
if goals:
|
|
|
|
text = ', '.join(goalstrs)
|
|
|
|
text = ", ".join(goalstrs)
|
|
|
|
await ctx.reply(f"{active.name} Goals! -- {text}")
|
|
|
|
await ctx.reply(f"{active.name} Goals! -- {text}")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
await ctx.reply("No goals have been configured!")
|
|
|
|
await ctx.reply("No goals have been configured!")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
await ctx.reply("No active subathon running!")
|
|
|
|
await ctx.reply("No active subathon running!")
|
|
|
|
|
|
|
|
|
|
|
|
@group_goals.command(name='remaining', aliases=('left',))
|
|
|
|
@group_goals.command(name="remaining", aliases=("left",))
|
|
|
|
async def cmd_goals_remaining(self, ctx: cmds.Context):
|
|
|
|
async def cmd_goals_remaining(self, ctx: cmds.Context):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
cid = community.communityid
|
|
|
|
cid = community.communityid
|
|
|
|
@@ -798,7 +855,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
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("All goals completed, congratulations!")
|
|
|
|
await ctx.reply("All goals completed, congratulations!")
|
|
|
|
@@ -807,7 +864,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
await ctx.reply("No active subathon running!")
|
|
|
|
await ctx.reply("No active subathon running!")
|
|
|
|
|
|
|
|
|
|
|
|
@group_goals.command(name='add')
|
|
|
|
@group_goals.command(name="add")
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def cmd_add(self, ctx: cmds.Context, required: int, *, description: str):
|
|
|
|
async def cmd_add(self, ctx: cmds.Context, required: int, *, description: str):
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
|
|
|
|
@@ -823,7 +880,7 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply("No active subathon to add goal to!")
|
|
|
|
await ctx.reply("No active subathon to add goal to!")
|
|
|
|
|
|
|
|
|
|
|
|
# remove
|
|
|
|
# remove
|
|
|
|
@group_goals.command(name='remove', aliases=['del', 'delete', 'rm'])
|
|
|
|
@group_goals.command(name="remove", aliases=["del", "delete", "rm"])
|
|
|
|
@cmds.is_moderator()
|
|
|
|
@cmds.is_moderator()
|
|
|
|
async def cmd_goals_remove(self, ctx: cmds.Context, required: int):
|
|
|
|
async def cmd_goals_remove(self, ctx: cmds.Context, required: int):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
@@ -841,4 +898,3 @@ class SubathonComponent(cmds.Component):
|
|
|
|
await ctx.reply(f"Score {required} goal(s) removed!")
|
|
|
|
await ctx.reply(f"Score {required} goal(s) removed!")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
await ctx.reply(f"No goal set at {required} score to remove.")
|
|
|
|
await ctx.reply(f"No goal set at {required} score to remove.")
|
|
|
|
|
|
|
|
|
|
|
|
|