Add API key management and session handling to RubyAPI cog. Implement commands to set the API key and verify users, enhancing interaction with external API. Introduce error handling for API requests and ensure proper session closure on cog unload.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-25 04:18:39 -04:00
parent 09726661f3
commit 5a5814bed2

View file

@ -1,4 +1,5 @@
from typing import Optional
import aiohttp
import discord
from redbot.core import commands, Config
from redbot.core.bot import Red
@ -11,15 +12,21 @@ class RubyAPI(commands.Cog):
def __init__(self, bot: Red):
self.bot = bot
self.config = Config.get_conf(self, identifier=867530999, force_registration=True)
self.session = aiohttp.ClientSession()
default_guild = {
"interaction_url": "https://ruby.valerie.lol/api/interactions",
"verify_url": "https://ruby.valerie.lol/verify-user",
"enabled": False
"enabled": False,
"api_key": None
}
self.config.register_guild(**default_guild)
def cog_unload(self):
if self.session:
self.bot.loop.create_task(self.session.close())
@commands.group(name="rubyapi")
@commands.admin_or_permissions(manage_guild=True)
async def rubyapi(self, ctx: commands.Context):
@ -33,6 +40,16 @@ class RubyAPI(commands.Cog):
status = "enabled" if toggle else "disabled"
await ctx.send(f"Ruby API integration has been {status}.")
@rubyapi.command(name="setkey")
async def set_api_key(self, ctx: commands.Context, api_key: str):
"""Set the API key for authentication."""
await self.config.guild(ctx.guild).api_key.set(api_key)
await ctx.send("API key has been set. I'll delete your message for security.")
try:
await ctx.message.delete()
except discord.HTTPException:
pass
@rubyapi.command(name="setinteraction")
async def set_interaction_url(self, ctx: commands.Context, url: str):
"""Set the interaction endpoint URL."""
@ -59,16 +76,35 @@ class RubyAPI(commands.Cog):
enabled = "Yes" if guild_config["enabled"] else "No"
interaction_url = guild_config["interaction_url"]
verify_url = guild_config["verify_url"]
has_api_key = "Yes" if guild_config["api_key"] else "No"
message = (
"Ruby API Settings:\n"
f"Enabled: {enabled}\n"
f"API Key Set: {has_api_key}\n"
f"Interaction URL: {interaction_url}\n"
f"Verify URL: {verify_url}"
)
await ctx.send(box(message))
async def _make_api_request(self, url: str, method: str = "GET", **kwargs):
"""Make an API request with proper headers and error handling."""
headers = kwargs.pop("headers", {})
if api_key := kwargs.pop("api_key", None):
headers["Authorization"] = f"Bearer {api_key}"
try:
async with self.session.request(method, url, headers=headers, **kwargs) as resp:
if resp.status == 429: # Rate limited
retry_after = float(resp.headers.get("Retry-After", 5))
return {"error": "rate_limited", "retry_after": retry_after}
data = await resp.json()
return data
except aiohttp.ClientError as e:
return {"error": f"API request failed: {str(e)}"}
@commands.command()
async def verifyuser(self, ctx: commands.Context, user: Optional[discord.Member] = None):
"""Verify a user's linked roles."""
@ -77,10 +113,44 @@ class RubyAPI(commands.Cog):
user = user or ctx.author
verify_url = await self.config.guild(ctx.guild).verify_url()
api_key = await self.config.guild(ctx.guild).api_key()
# Here you would implement the actual API call to verify the user
# For demonstration, we'll just show a message
await ctx.send(f"Verification would be performed for {user.mention} using {verify_url}")
if not api_key:
return await ctx.send("API key has not been set. Please set it using `[p]rubyapi setkey`")
payload = {
"user_id": str(user.id),
"guild_id": str(ctx.guild.id)
}
async with ctx.typing():
result = await self._make_api_request(
verify_url,
method="POST",
json=payload,
api_key=api_key
)
if "error" in result:
return await ctx.send(f"Error verifying user: {result['error']}")
if result.get("verified", False):
roles_to_add = []
for role_id in result.get("roles", []):
if role := ctx.guild.get_role(int(role_id)):
roles_to_add.append(role)
if roles_to_add:
try:
await user.add_roles(*roles_to_add, reason="Linked roles verification")
role_names = ", ".join(role.name for role in roles_to_add)
await ctx.send(f"Verified {user.mention}! Added roles: {role_names}")
except discord.HTTPException as e:
await ctx.send(f"Error adding roles: {str(e)}")
else:
await ctx.send(f"Verified {user.mention}, but no roles needed to be added.")
else:
await ctx.send(f"Could not verify {user.mention}. Please make sure their accounts are properly linked.")
@commands.Cog.listener()
async def on_interaction(self, interaction: discord.Interaction):
@ -90,8 +160,38 @@ class RubyAPI(commands.Cog):
if not await self.config.guild(interaction.guild).enabled():
return
# Here you would implement the actual interaction handling
# For demonstration, we'll just log the interaction
interaction_url = await self.config.guild(interaction.guild).interaction_url()
# You would typically make an API request to the interaction_url here
api_key = await self.config.guild(interaction.guild).api_key()
if not api_key:
return # Silent return if API key is not set
# Forward the interaction to the API
payload = {
"type": interaction.type.value,
"guild_id": str(interaction.guild_id),
"channel_id": str(interaction.channel_id),
"data": interaction.data,
"member": {
"user": {
"id": str(interaction.user.id),
"username": interaction.user.name,
"discriminator": interaction.user.discriminator,
},
"roles": [str(role.id) for role in interaction.user.roles],
} if interaction.user else None
}
result = await self._make_api_request(
interaction_url,
method="POST",
json=payload,
api_key=api_key
)
# Handle the API response if needed
if "error" in result:
# Log the error but don't respond to the interaction
# as it might have been handled elsewhere
print(f"Error forwarding interaction: {result['error']}")