Require OAuth Nitro verification for giveaways (#14)
Adds OAuth-based Nitro status checks to giveaways and allows excluding Nitro users. Reviewed-on: #14 Co-authored-by: Slipstream <me@slipstreamm.dev> Co-committed-by: Slipstream <me@slipstreamm.dev>
This commit is contained in:
parent
28d0c11e48
commit
b9fb671bed
@ -9,16 +9,35 @@ import json
|
|||||||
import os
|
import os
|
||||||
import aiofiles # Import aiofiles
|
import aiofiles # Import aiofiles
|
||||||
import aiofiles.os
|
import aiofiles.os
|
||||||
|
import discord_oauth
|
||||||
|
|
||||||
GIVEAWAY_DATA_FILE = "data/giveaways.json"
|
GIVEAWAY_DATA_FILE = "data/giveaways.json"
|
||||||
DATA_DIR = "data"
|
DATA_DIR = "data"
|
||||||
|
|
||||||
|
|
||||||
# --- Helper Functions ---
|
# --- Helper Functions ---
|
||||||
|
async def get_nitro_status_oauth(user: discord.User | discord.Member) -> bool | None:
|
||||||
|
"""Return True if user has Nitro according to OAuth info, False if not, None if unknown."""
|
||||||
|
token = await discord_oauth.get_token(str(user.id))
|
||||||
|
if not token:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
user_info = await discord_oauth.get_user_info(token)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
return user_info.get("premium_type", 0) in (1, 2)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Additional Helper Functions ---
|
||||||
async def is_user_nitro_like(
|
async def is_user_nitro_like(
|
||||||
user: discord.User | discord.Member, bot: commands.Bot = None
|
user: discord.User | discord.Member, bot: commands.Bot = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Checks if a user has an animated avatar or a banner, indicating Nitro."""
|
"""Heuristically check if a user has Nitro, falling back to OAuth if available."""
|
||||||
|
|
||||||
|
nitro_oauth = await get_nitro_status_oauth(user)
|
||||||
|
if nitro_oauth is not None:
|
||||||
|
return nitro_oauth
|
||||||
|
|
||||||
# Fetch the full user object to get banner information
|
# Fetch the full user object to get banner information
|
||||||
if bot:
|
if bot:
|
||||||
try:
|
try:
|
||||||
@ -59,10 +78,23 @@ class GiveawayEnterButton(ui.Button["GiveawayEnterView"]):
|
|||||||
await interaction.message.edit(view=self.view)
|
await interaction.message.edit(view=self.view)
|
||||||
return
|
return
|
||||||
|
|
||||||
if giveaway["is_nitro_giveaway"]:
|
if giveaway["is_nitro_giveaway"] or giveaway.get("exclude_nitro_users"):
|
||||||
if not await is_user_nitro_like(interaction.user, bot=self.cog.bot):
|
nitro_status = await get_nitro_status_oauth(interaction.user)
|
||||||
|
if nitro_status is None:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"This is a Nitro-exclusive giveaway. You don't appear to have Nitro (animated avatar or banner).",
|
"Please authenticate with /auth so I can verify your Nitro status before entering.",
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if giveaway["is_nitro_giveaway"] and not nitro_status:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"This is a Nitro-exclusive giveaway, and your account does not appear to have Nitro.",
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if giveaway.get("exclude_nitro_users") and nitro_status:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"Nitro users are excluded from this giveaway.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -150,11 +182,16 @@ class GiveawayRerollButton(ui.Button["GiveawayEndView"]):
|
|||||||
except discord.NotFound:
|
except discord.NotFound:
|
||||||
continue # Skip if user cannot be found
|
continue # Skip if user cannot be found
|
||||||
if user and not user.bot:
|
if user and not user.bot:
|
||||||
# Apply Nitro check again if it was a nitro giveaway
|
if giveaway_data.get("is_nitro_giveaway") or giveaway_data.get(
|
||||||
if giveaway_data.get(
|
"exclude_nitro_users"
|
||||||
"is_nitro_giveaway", False
|
):
|
||||||
) and not is_user_nitro_like(user):
|
nitro_status = await get_nitro_status_oauth(user)
|
||||||
continue
|
if nitro_status is None:
|
||||||
|
continue
|
||||||
|
if giveaway_data.get("is_nitro_giveaway") and not nitro_status:
|
||||||
|
continue
|
||||||
|
if giveaway_data.get("exclude_nitro_users") and nitro_status:
|
||||||
|
continue
|
||||||
entrants_users.append(user)
|
entrants_users.append(user)
|
||||||
|
|
||||||
if not entrants_users:
|
if not entrants_users:
|
||||||
@ -228,6 +265,7 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
# "creator_id": int,
|
# "creator_id": int,
|
||||||
# "participants": set(), # Store user_ids. Stored as list in JSON.
|
# "participants": set(), # Store user_ids. Stored as list in JSON.
|
||||||
# "is_nitro_giveaway": bool,
|
# "is_nitro_giveaway": bool,
|
||||||
|
# "exclude_nitro_users": bool,
|
||||||
# "ended": bool
|
# "ended": bool
|
||||||
# }
|
# }
|
||||||
# Ensure data directory exists before loading/saving
|
# Ensure data directory exists before loading/saving
|
||||||
@ -325,6 +363,7 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
)
|
)
|
||||||
gw_data["participants"] = set(gw_data.get("participants", []))
|
gw_data["participants"] = set(gw_data.get("participants", []))
|
||||||
gw_data.setdefault("is_nitro_giveaway", False)
|
gw_data.setdefault("is_nitro_giveaway", False)
|
||||||
|
gw_data.setdefault("exclude_nitro_users", False)
|
||||||
gw_data.setdefault(
|
gw_data.setdefault(
|
||||||
"ended", gw_data["end_time"] <= now
|
"ended", gw_data["end_time"] <= now
|
||||||
) # Set ended if time has passed
|
) # Set ended if time has passed
|
||||||
@ -432,7 +471,8 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
prize="What is the prize?",
|
prize="What is the prize?",
|
||||||
duration="How long should the giveaway last? (e.g., 10m, 1h, 2d, 1w)",
|
duration="How long should the giveaway last? (e.g., 10m, 1h, 2d, 1w)",
|
||||||
winners="How many winners? (default: 1)",
|
winners="How many winners? (default: 1)",
|
||||||
nitro_giveaway="Is this a Nitro-only giveaway? (checks for animated avatar/banner)",
|
nitro_giveaway="Is this a Nitro-only giveaway? (OAuth verification)",
|
||||||
|
exclude_nitro="Exclude Nitro users from entering?",
|
||||||
)
|
)
|
||||||
@app_commands.checks.has_permissions(manage_guild=True)
|
@app_commands.checks.has_permissions(manage_guild=True)
|
||||||
async def create_giveaway_slash(
|
async def create_giveaway_slash(
|
||||||
@ -442,6 +482,7 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
duration: str,
|
duration: str,
|
||||||
winners: int = 1,
|
winners: int = 1,
|
||||||
nitro_giveaway: bool = False,
|
nitro_giveaway: bool = False,
|
||||||
|
exclude_nitro: bool = False,
|
||||||
):
|
):
|
||||||
"""Slash command to create a giveaway using buttons."""
|
"""Slash command to create a giveaway using buttons."""
|
||||||
parsed_duration = self.parse_duration(duration)
|
parsed_duration = self.parse_duration(duration)
|
||||||
@ -469,6 +510,8 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
)
|
)
|
||||||
if nitro_giveaway:
|
if nitro_giveaway:
|
||||||
embed.description += "\n*This is a Nitro-exclusive giveaway!*"
|
embed.description += "\n*This is a Nitro-exclusive giveaway!*"
|
||||||
|
if exclude_nitro:
|
||||||
|
embed.description += "\n*Users with Nitro are excluded from entering.*"
|
||||||
embed.set_footer(
|
embed.set_footer(
|
||||||
text=f"Giveaway started by {interaction.user.display_name}. Entries: 0"
|
text=f"Giveaway started by {interaction.user.display_name}. Entries: 0"
|
||||||
) # Initial entry count
|
) # Initial entry count
|
||||||
@ -488,6 +531,7 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
"creator_id": interaction.user.id,
|
"creator_id": interaction.user.id,
|
||||||
"participants": set(),
|
"participants": set(),
|
||||||
"is_nitro_giveaway": nitro_giveaway,
|
"is_nitro_giveaway": nitro_giveaway,
|
||||||
|
"exclude_nitro_users": exclude_nitro,
|
||||||
"ended": False,
|
"ended": False,
|
||||||
}
|
}
|
||||||
self.active_giveaways.append(giveaway_data)
|
self.active_giveaways.append(giveaway_data)
|
||||||
@ -559,10 +603,16 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
if user_to_check.bot:
|
if user_to_check.bot:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if giveaway_data["is_nitro_giveaway"] and not is_user_nitro_like(
|
if giveaway_data["is_nitro_giveaway"] or giveaway_data.get(
|
||||||
user_to_check
|
"exclude_nitro_users"
|
||||||
):
|
):
|
||||||
continue # Skip non-nitro users for nitro giveaways
|
nitro_status = await get_nitro_status_oauth(user_to_check)
|
||||||
|
if nitro_status is None:
|
||||||
|
continue
|
||||||
|
if giveaway_data["is_nitro_giveaway"] and not nitro_status:
|
||||||
|
continue
|
||||||
|
if giveaway_data.get("exclude_nitro_users") and nitro_status:
|
||||||
|
continue
|
||||||
entrants_users.append(user_to_check)
|
entrants_users.append(user_to_check)
|
||||||
|
|
||||||
winners_list = []
|
winners_list = []
|
||||||
@ -685,10 +735,16 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
user_id
|
user_id
|
||||||
) or await self.bot.fetch_user(user_id)
|
) or await self.bot.fetch_user(user_id)
|
||||||
if user and not user.bot:
|
if user and not user.bot:
|
||||||
if giveaway_info.get(
|
if giveaway_info.get("is_nitro_giveaway") or giveaway_info.get(
|
||||||
"is_nitro_giveaway", False
|
"exclude_nitro_users"
|
||||||
) and not is_user_nitro_like(user):
|
):
|
||||||
continue
|
nitro_status = await get_nitro_status_oauth(user)
|
||||||
|
if nitro_status is None:
|
||||||
|
continue
|
||||||
|
if giveaway_info.get("is_nitro_giveaway") and not nitro_status:
|
||||||
|
continue
|
||||||
|
if giveaway_info.get("exclude_nitro_users") and nitro_status:
|
||||||
|
continue
|
||||||
entrants.add(user)
|
entrants.add(user)
|
||||||
if not entrants:
|
if not entrants:
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
@ -704,8 +760,23 @@ class GiveawaysCog(commands.Cog, name="Giveaways"):
|
|||||||
reaction_found = True
|
reaction_found = True
|
||||||
async for user in reaction.users():
|
async for user in reaction.users():
|
||||||
if not user.bot:
|
if not user.bot:
|
||||||
# For manual reaction roll, we might not know if it was nitro_giveaway
|
if giveaway_info and (
|
||||||
# Consider adding a parameter to manual_roll for this if needed
|
giveaway_info.get("is_nitro_giveaway")
|
||||||
|
or giveaway_info.get("exclude_nitro_users")
|
||||||
|
):
|
||||||
|
nitro_status = await get_nitro_status_oauth(user)
|
||||||
|
if nitro_status is None:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
giveaway_info.get("is_nitro_giveaway")
|
||||||
|
and not nitro_status
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
giveaway_info.get("exclude_nitro_users")
|
||||||
|
and nitro_status
|
||||||
|
):
|
||||||
|
continue
|
||||||
entrants.add(user)
|
entrants.add(user)
|
||||||
break
|
break
|
||||||
if not reaction_found:
|
if not reaction_found:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user