Ruby-Cogs/levelup/shared/weeklyreset.py
Valerie 477974d53c
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Upload 2 Cogs & Update README
2025-05-23 01:30:53 -04:00

182 lines
7.1 KiB
Python

import logging
import typing as t
from contextlib import suppress
from datetime import timedelta
from io import StringIO
import discord
from redbot.core import commands
from redbot.core.i18n import Translator
from redbot.core.utils.chat_formatting import humanize_number
from ..abc import MixinMeta
from ..common import utils
from ..common.models import ProfileWeekly
log = logging.getLogger("red.vrt.levelup.shared.weeklyreset")
_ = Translator("LevelUp", __file__)
class WeeklyReset(MixinMeta):
async def reset_weekly(self, guild: discord.Guild, ctx: commands.Context = None) -> bool:
"""Announce and reset weekly stats
Args:
guild (discord.Guild): The guild where the weekly stats are being reset.
ctx (commands.Context, optional): Sends the announcement embed in the current channel. Defaults to None.
Returns:
bool: True if the weekly stats were reset, False otherwise.
"""
log.warning(f"Resetting weekly stats for {guild.name}")
conf = self.db.get_conf(guild)
if not conf.users_weekly:
log.info("No users in the weekly data")
if ctx:
await ctx.send(_("There are no users in the weekly data yet"))
conf.weeklysettings.refresh()
self.save()
return False
valid_users: t.Dict[discord.Member, ProfileWeekly] = {}
for user_id, stats in conf.users_weekly.items():
user = guild.get_member(user_id)
if user and stats.xp > 0:
valid_users[user] = stats
if not valid_users:
log.info("No users with XP in the weekly data")
if ctx:
await ctx.send(_("There are no users with XP in the weekly data yet"))
conf.weeklysettings.refresh()
self.save()
return False
channel = guild.get_channel(conf.weeklysettings.channel) if conf.weeklysettings.channel else None
total_xp = 0
total_messages = 0
total_voicetime = 0
total_stars = 0
for stats in valid_users.values():
total_xp += stats.xp
total_messages += stats.messages
total_voicetime += stats.voice
total_stars += stats.stars
total_xp = humanize_number(int(total_xp))
total_messages = humanize_number(total_messages)
total_voicetime = utils.humanize_delta(total_voicetime)
total_stars = humanize_number(total_stars)
next_reset = int(conf.weeklysettings.next_reset + timedelta(days=7).total_seconds())
embed = discord.Embed(
description=_(
"`Total Exp: `{}\n"
"`Total Messages: `{}\n"
"`Total Stars: `{}\n"
"`Total Voicetime: `{}\n"
"`Next Reset: `{}"
).format(
total_xp,
total_messages,
total_stars,
total_voicetime,
f"<t:{next_reset}:R>",
),
color=await self.bot.get_embed_color(channel or ctx),
)
embed.set_author(
name=_("Top Weekly Exp Earners"),
icon_url=guild.icon,
)
embed.set_thumbnail(url=guild.icon)
place_emojis = ["🥇", "🥈", "🥉"]
sorted_users = sorted(valid_users.items(), key=lambda x: x[1].xp, reverse=True)
top_user_ids = []
for idx, (user, stats) in enumerate(sorted_users):
place = idx + 1
if place > conf.weeklysettings.count:
break
top_user_ids.append(user.id)
position = place_emojis[idx] if idx < 3 else f"#{place}."
tmp = StringIO()
tmp.write(_("`Experience: `{}\n").format(humanize_number(round(stats.xp))))
tmp.write(_("`Messages: `{}\n").format(humanize_number(stats.messages)))
if stats.stars:
tmp.write(_("`Stars: `{}\n").format(humanize_number(stats.stars)))
if round(stats.voice):
tmp.write(_("`Voicetime: `{}\n").format(utils.humanize_delta(round(stats.voice))))
embed.add_field(name=f"{position} {user.display_name}", value=tmp.getvalue(), inline=False)
if ctx:
with suppress(discord.HTTPException):
await ctx.send(embed=embed)
command_channel_id = ctx.channel.id if ctx else 0
if channel and command_channel_id != channel.id:
mentions = ", ".join(f"<@{uid}>" for uid in top_user_ids)
with suppress(discord.HTTPException):
if conf.weeklysettings.ping_winners:
await channel.send(mentions, embed=embed)
else:
await channel.send(embed=embed)
top: t.List[t.Tuple[discord.Member, ProfileWeekly]] = sorted_users[: conf.weeklysettings.count]
if conf.weeklysettings.role_all:
winners: t.List[discord.Member] = [user[0] for user in top]
else:
winners: t.List[discord.Member] = [top[0][0]]
winner_ids = [user.id for user in winners]
perms = guild.me.guild_permissions.manage_roles
if not perms:
log.warning("Missing permissions to manage roles")
if ctx:
await ctx.send(_("Missing permissions to manage roles"))
role = guild.get_role(conf.weeklysettings.role) if conf.weeklysettings.role else None
if role and perms:
if conf.weeklysettings.remove:
for user_id in conf.weeklysettings.last_winners:
user = guild.get_member(user_id)
if not user:
continue
user_roles = [role.id for role in user.roles]
if role.id in user_roles and user.id not in winner_ids:
try:
await user.remove_roles(role, reason=_("Weekly winner role removal"))
except discord.Forbidden:
log.warning(f"Missing permissions to apply role {role} to {user.name}")
except discord.HTTPException:
pass
for winner in winners:
role_ids = [role.id for role in winner.roles]
if role.id in role_ids:
# User already has the role
continue
try:
await winner.add_roles(role, reason=_("Weekly winner role addition"))
except discord.Forbidden:
log.warning(f"Missing permissions to apply role {role} to {winner}")
except discord.HTTPException:
pass
conf.weeklysettings.last_winners = [user[0].id for user in top]
if bonus := conf.weeklysettings.bonus:
for i in top:
profile = conf.get_profile(i[0])
profile.xp += bonus
conf.weeklysettings.refresh()
conf.users_weekly.clear()
conf.weeklysettings.last_embed = embed.to_dict()
self.save()
if ctx:
await ctx.send(_("Weekly stats have been reset."))
log.info(f"Reset weekly stats for {guild.name}")
return True