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
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
09726661f3
commit
5a5814bed2
1 changed files with 108 additions and 8 deletions
|
@ -1,4 +1,5 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import commands, Config
|
from redbot.core import commands, Config
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
|
@ -11,15 +12,21 @@ class RubyAPI(commands.Cog):
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.config = Config.get_conf(self, identifier=867530999, force_registration=True)
|
self.config = Config.get_conf(self, identifier=867530999, force_registration=True)
|
||||||
|
self.session = aiohttp.ClientSession()
|
||||||
|
|
||||||
default_guild = {
|
default_guild = {
|
||||||
"interaction_url": "https://ruby.valerie.lol/api/interactions",
|
"interaction_url": "https://ruby.valerie.lol/api/interactions",
|
||||||
"verify_url": "https://ruby.valerie.lol/verify-user",
|
"verify_url": "https://ruby.valerie.lol/verify-user",
|
||||||
"enabled": False
|
"enabled": False,
|
||||||
|
"api_key": None
|
||||||
}
|
}
|
||||||
|
|
||||||
self.config.register_guild(**default_guild)
|
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.group(name="rubyapi")
|
||||||
@commands.admin_or_permissions(manage_guild=True)
|
@commands.admin_or_permissions(manage_guild=True)
|
||||||
async def rubyapi(self, ctx: commands.Context):
|
async def rubyapi(self, ctx: commands.Context):
|
||||||
|
@ -33,6 +40,16 @@ class RubyAPI(commands.Cog):
|
||||||
status = "enabled" if toggle else "disabled"
|
status = "enabled" if toggle else "disabled"
|
||||||
await ctx.send(f"Ruby API integration has been {status}.")
|
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")
|
@rubyapi.command(name="setinteraction")
|
||||||
async def set_interaction_url(self, ctx: commands.Context, url: str):
|
async def set_interaction_url(self, ctx: commands.Context, url: str):
|
||||||
"""Set the interaction endpoint URL."""
|
"""Set the interaction endpoint URL."""
|
||||||
|
@ -59,16 +76,35 @@ class RubyAPI(commands.Cog):
|
||||||
enabled = "Yes" if guild_config["enabled"] else "No"
|
enabled = "Yes" if guild_config["enabled"] else "No"
|
||||||
interaction_url = guild_config["interaction_url"]
|
interaction_url = guild_config["interaction_url"]
|
||||||
verify_url = guild_config["verify_url"]
|
verify_url = guild_config["verify_url"]
|
||||||
|
has_api_key = "Yes" if guild_config["api_key"] else "No"
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
"Ruby API Settings:\n"
|
"Ruby API Settings:\n"
|
||||||
f"Enabled: {enabled}\n"
|
f"Enabled: {enabled}\n"
|
||||||
|
f"API Key Set: {has_api_key}\n"
|
||||||
f"Interaction URL: {interaction_url}\n"
|
f"Interaction URL: {interaction_url}\n"
|
||||||
f"Verify URL: {verify_url}"
|
f"Verify URL: {verify_url}"
|
||||||
)
|
)
|
||||||
|
|
||||||
await ctx.send(box(message))
|
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()
|
@commands.command()
|
||||||
async def verifyuser(self, ctx: commands.Context, user: Optional[discord.Member] = None):
|
async def verifyuser(self, ctx: commands.Context, user: Optional[discord.Member] = None):
|
||||||
"""Verify a user's linked roles."""
|
"""Verify a user's linked roles."""
|
||||||
|
@ -77,10 +113,44 @@ class RubyAPI(commands.Cog):
|
||||||
|
|
||||||
user = user or ctx.author
|
user = user or ctx.author
|
||||||
verify_url = await self.config.guild(ctx.guild).verify_url()
|
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
|
if not api_key:
|
||||||
# For demonstration, we'll just show a message
|
return await ctx.send("API key has not been set. Please set it using `[p]rubyapi setkey`")
|
||||||
await ctx.send(f"Verification would be performed for {user.mention} using {verify_url}")
|
|
||||||
|
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()
|
@commands.Cog.listener()
|
||||||
async def on_interaction(self, interaction: discord.Interaction):
|
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():
|
if not await self.config.guild(interaction.guild).enabled():
|
||||||
return
|
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()
|
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']}")
|
Loading…
Add table
Reference in a new issue