Ruby-Cogs/referrals/commands/user.py
2025-05-23 02:30:00 -04:00

335 lines
15 KiB
Python

import logging
from datetime import datetime
from io import StringIO
import discord
from discord import app_commands
from redbot.core import bank, commands
from redbot.core.bot import Red
from redbot.core.errors import BalanceTooHigh
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import humanize_timedelta
from ..abc import MixinMeta
from ..common import utils
from ..common.checks import ensure_db_connection
from ..db.tables import Referral
from ..views.dynamic_menu import DynamicMenu
log = logging.getLogger("red.vrt.referrals.commands.admin")
_ = Translator("Referrals", __file__)
@app_commands.context_menu(name="Referred By")
@app_commands.guild_only()
async def referredby_context(interaction: discord.Interaction, member: discord.Member):
if not interaction.guild:
return await interaction.response.send_message(_("This command can only be used in a server."), ephemeral=True)
if member.bot:
return await interaction.response.send_message(_("Bots can't refer users."), ephemeral=True)
if member.id == interaction.user.id:
return await interaction.response.send_message(_("You can't refer yourself."), ephemeral=True)
if not member.guild:
return await interaction.response.send_message(
_("The user you are trying to refer is not in this server."), ephemeral=True
)
bot: Red = interaction.client
cog: MixinMeta = bot.get_cog("Referrals")
if not cog.db:
return await interaction.response.send_message(
_("Database connection is not active, try again later."), ephemeral=True
)
await interaction.response.defer(thinking=True)
settings = await cog.db_utils.get_create_guild(interaction.guild)
if not settings.enabled:
return await interaction.followup.send(_("Referral rewards are not enabled for this server."))
if not settings.referral_reward and not settings.referred_reward:
return await interaction.followup.send(_("Referral rewards are not configured for this server."))
if interaction.user.id in settings.initialized_users:
return await interaction.followup.send(
_("You were already a part of this server when the referral system was initialized.")
)
if interaction.user.joined_at:
if interaction.user.joined_at < member.joined_at:
return await interaction.followup.send(
_("This user joined the server after you, they couldn't have referred you.")
)
if log_channel := bot.get_channel(settings.referral_channel):
if not all(
[
log_channel.permissions_for(interaction.guild.me).send_messages,
log_channel.permissions_for(interaction.guild.me).embed_links,
]
):
log_channel = None
account_age_minutes = int((datetime.now().astimezone() - interaction.user.created_at).total_seconds() / 60)
if settings.min_account_age_minutes and account_age_minutes < settings.min_account_age_minutes:
required_time = humanize_timedelta(seconds=settings.min_account_age_minutes * 60)
account_age_humanized = humanize_timedelta(seconds=account_age_minutes * 60)
if log_channel:
txt = _("Account age less than {} old. ({} old)").format(f"`{required_time}`", f"`{account_age_humanized}`")
embed = utils.referral_error(
referrer=member,
referred=interaction.user,
error=txt,
channel=interaction.channel.mention,
)
await log_channel.send(embed=embed)
return await interaction.followup.send(
_("You must have an account age of at least {} to be eligible for referral rewards.").format(
f"**{required_time}**"
)
)
join_minutes = int((datetime.now().astimezone() - interaction.user.joined_at).total_seconds() / 60)
if settings.claim_timeout_minutes and join_minutes > settings.claim_timeout_minutes:
required_time = humanize_timedelta(seconds=settings.claim_timeout_minutes * 60)
in_server_time = humanize_timedelta(seconds=join_minutes * 60)
if log_channel:
txt = _("Missed claim timeout of {} after joining. ({} ago)").format(
f"`{required_time}`", f"`{in_server_time}`"
)
embed = utils.referral_error(
referrer=member,
referred=interaction.user,
error=txt,
channel=interaction.channel.mention,
)
await log_channel.send(embed=embed)
return await interaction.followup.send(
_("You joined the server too long ago to claim a referral reward. You must claim it within {}.").format(
f"**{required_time}**"
)
)
existing_referral = await Referral.objects().get(
(Referral.guild == interaction.guild.id) & (Referral.referred_id == interaction.user.id)
)
if existing_referral:
referrer = await bot.fetch_user(existing_referral.referrer_id)
return await interaction.followup.send(_("You have already been referred by {}.").format(f"**{referrer}**"))
currency_name = await bank.get_currency_name(interaction.guild)
response = StringIO()
response.write(_("You have been referred by {}!\n").format(f"**{member}**"))
if settings.referred_reward:
try:
await bank.deposit_credits(interaction.user, settings.referred_reward)
except BalanceTooHigh as e:
await bank.set_balance(interaction.user, e.max_balance)
txt = _("-# You have received {} as a reward for being referred!\n").format(
f"`{settings.referred_reward} {currency_name}`"
)
response.write(txt)
if settings.referral_reward:
try:
await bank.deposit_credits(member, settings.referral_reward)
except BalanceTooHigh as e:
await bank.set_balance(member, e.max_balance)
txt = _("-# {} has received {} as a reward for referring you!").format(
f"`{member}`", f"`{settings.referral_reward} {currency_name}`"
)
response.write(txt)
referral = Referral(
guild=interaction.guild.id,
referred_id=interaction.user.id,
referrer_id=member.id,
)
await referral.save()
await interaction.followup.send(response.getvalue())
if log_channel:
embed = utils.referral_embed(
referrer=member,
referred=interaction.user,
referrer_reward=settings.referral_reward,
referred_reward=settings.referred_reward,
currency=currency_name,
channel=interaction.channel.mention,
)
await log_channel.send(embed=embed)
@cog_i18n(_)
class User(MixinMeta):
@app_commands.command(name="referredby", description="Claim a referral")
@app_commands.describe(referred_by="The user who referred you")
@app_commands.guild_only()
async def referredby_slash(self, interaction: discord.Interaction, referred_by: discord.Member):
"""Claim a referral
Claim a referral from a user who referred you
If referral rewards are enabled, you will receive the reward for being referred.
If referrer rewards are enabled, the person who referred you will also receive a reward.
"""
await interaction.response.defer()
ctx = await commands.Context.from_interaction(interaction)
async with ctx.channel.typing():
await self.handle_referral_claim(ctx, referred_by)
@commands.command()
@commands.guild_only()
@ensure_db_connection()
async def referredby(self, ctx: commands.Context, referred_by: discord.Member):
"""Claim a referral
Claim a referral from a user who referred you
If referral rewards are enabled, you will receive the reward for being referred.
If referrer rewards are enabled, the person who referred you will also receive a reward.
"""
async with ctx.typing():
await self.handle_referral_claim(ctx, referred_by)
async def handle_referral_claim(self, ctx: commands.Context, referred_by: discord.Member):
if referred_by.id == ctx.author.id:
return await ctx.send(_("You can't refer yourself."))
if referred_by.bot:
return await ctx.send(_("Bots can't refer users."))
settings = await self.db_utils.get_create_guild(ctx.guild)
if not settings.enabled:
return await ctx.send(_("Referral rewards are not enabled for this server."))
if ctx.author.joined_at:
if ctx.author.joined_at < referred_by.joined_at:
return await ctx.send(_("This user joined the server after you, they couldn't have referred you."))
log_channel = self.bot.get_channel(settings.referral_channel)
if log_channel:
if not all(
[
log_channel.permissions_for(ctx.guild.me).send_messages,
log_channel.permissions_for(ctx.guild.me).embed_links,
]
):
log_channel = None
if not settings.referral_reward and not settings.referred_reward:
return await ctx.send(_("Referral rewards are not configured for this server."))
if ctx.author.id in settings.initialized_users:
return await ctx.send(_("You were already a part of this server when the referral system was initialized."))
account_age_minutes = int((datetime.now().astimezone() - ctx.author.created_at).total_seconds() / 60)
if settings.min_account_age_minutes and account_age_minutes < settings.min_account_age_minutes:
required_time = humanize_timedelta(seconds=settings.min_account_age_minutes * 60)
account_age_humanized = humanize_timedelta(seconds=account_age_minutes * 60)
if log_channel:
txt = _("Account age less than {} old. ({} old)").format(
f"`{required_time}`", f"`{account_age_humanized}`"
)
embed = utils.referral_error(
referrer=referred_by,
referred=ctx.author,
error=txt,
channel=ctx.channel.mention,
)
await log_channel.send(embed=embed)
return await ctx.send(
_("You must have an account age of at least {} to be eligible for referral rewards.").format(
f"**{required_time}**"
)
)
join_minutes = int((datetime.now().astimezone() - ctx.author.joined_at).total_seconds() / 60)
if settings.claim_timeout_minutes and join_minutes > settings.claim_timeout_minutes:
required_time = humanize_timedelta(seconds=settings.claim_timeout_minutes * 60)
in_server_time = humanize_timedelta(seconds=join_minutes * 60)
if log_channel:
txt = _("Missed claim timeout of {} after joining. ({} ago)").format(
f"`{required_time}`", f"`{in_server_time}`"
)
embed = utils.referral_error(
referrer=referred_by,
referred=ctx.author,
error=txt,
channel=ctx.channel.mention,
)
await log_channel.send(embed=embed)
return await ctx.send(
_("You joined the server too long ago to claim a referral reward. You must claim it within {}.").format(
f"**{required_time}**"
)
)
existing_referral = await Referral.objects().get(
(Referral.guild == ctx.guild.id) & (Referral.referred_id == ctx.author.id)
)
if existing_referral:
referrer = await self.bot.fetch_user(existing_referral.referrer_id)
return await ctx.send(_("You have already been referred by {}.").format(f"**{referrer}**"))
currency_name = await bank.get_currency_name(ctx.guild)
response = StringIO()
response.write(_("You have been referred by {}!\n").format(f"**{referred_by}**"))
if settings.referred_reward:
try:
await bank.deposit_credits(ctx.author, settings.referred_reward)
except BalanceTooHigh as e:
await bank.set_balance(ctx.author, e.max_balance)
txt = _("-# You have received {} as a reward for being referred!\n").format(
f"`{settings.referred_reward} {currency_name}`"
)
response.write(txt)
if settings.referral_reward:
try:
await bank.deposit_credits(referred_by, settings.referral_reward)
except BalanceTooHigh as e:
await bank.set_balance(referred_by, e.max_balance)
txt = _("-# {} has received {} as a reward for referring you!").format(
f"`{referred_by}`", f"`{settings.referral_reward} {currency_name}`"
)
response.write(txt)
referral = Referral(
guild=ctx.guild.id,
referred_id=ctx.author.id,
referrer_id=referred_by.id,
)
await referral.save()
await ctx.send(response.getvalue())
if log_channel:
embed = utils.referral_embed(
referrer=referred_by,
referred=ctx.author,
referrer_reward=settings.referral_reward,
referred_reward=settings.referred_reward,
currency=currency_name,
channel=ctx.channel.mention,
)
await log_channel.send(embed=embed)
@commands.hybrid_command()
@commands.guild_only()
@ensure_db_connection()
async def myreferrals(self, ctx: commands.Context):
"""Check your referrals"""
referrals = await Referral.objects().where(
(Referral.guild == ctx.guild.id) & (Referral.referrer_id == ctx.author.id)
)
if not referrals:
return await ctx.send(_("You have not referred anyone yet."))
color = await self.bot.get_embed_color(ctx)
embeds = []
chunk_size = 5
for idx, chunk in enumerate(utils.chunk(referrals, chunk_size)):
txt = StringIO()
for idxx, referral in enumerate(chunk, start=idx):
referred_user = await self.bot.get_or_fetch_user(referral.referred_id)
txt.write(f"{idxx + 1}. {referred_user}\n")
embed = discord.Embed(title=_("Your Referrals"), description=txt.getvalue(), color=color)
embed.set_footer(text=_("Page {}/{}").format(idx + 1, len(referrals)))
embeds.append(embed)
await DynamicMenu(ctx, embeds).refresh()
@commands.hybrid_command()
@commands.guild_only()
@ensure_db_connection()
async def whoreferred(self, ctx: commands.Context, *, user: discord.Member):
"""Check if a specific user has been referred"""
referral = await Referral.objects().get((Referral.guild == ctx.guild.id) & (Referral.referred_id == user.id))
if not referral:
return await ctx.send(_("{} has not been referred by anyone.").format(user))
referrer = await self.bot.get_or_fetch_user(referral.referrer_id)
await ctx.send(_("{} was referred by {}.").format(user, referrer))