add trips permissions

This commit is contained in:
Logan
2026-06-21 20:00:48 -04:00
parent 981f03ac06
commit 6ae4d398f8
9 changed files with 425 additions and 10 deletions
+81 -3
View File
@@ -62,6 +62,16 @@ def _date_range(start_iso: str, end_iso: str):
# Cog
# ---------------------------------------------------------------------------
def _user_can_see_trip(trip: dict, discord_user_id: str) -> bool:
if trip.get("visibility", "public") == "public":
return True
if discord_user_id in trip.get("attendees", {}):
return True
if discord_user_id in trip.get("invited_discord_ids", []):
return True
return False
class TripCommands(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@@ -79,10 +89,11 @@ class TripCommands(commands.Cog):
self, interaction: discord.Interaction, current: str
) -> list[app_commands.Choice[str]]:
trips = await c2.get_trips()
user_id = str(interaction.user.id)
return [
app_commands.Choice(name=t["name"], value=t["trip_id"])
for t in trips
if current.lower() in t["name"].lower()
if current.lower() in t["name"].lower() and _user_can_see_trip(t, user_id)
][:25]
async def event_autocomplete(
@@ -172,6 +183,9 @@ class TripCommands(commands.Cog):
today = date.today().strftime("%Y-%m-%d")
trips.sort(key=lambda t: t.get("start_date", ""))
user_id = str(interaction.user.id)
trips = [t for t in trips if _user_can_see_trip(t, user_id)]
embed = discord.Embed(title="Trips", color=0x2b2d31)
for t in trips[:10]:
upcoming = t.get("start_date", "") >= today
@@ -204,6 +218,9 @@ class TripCommands(commands.Cog):
if not data:
await interaction.followup.send("Trip not found.")
return
if not _user_can_see_trip(data, str(interaction.user.id)):
await interaction.followup.send("This trip is private.", ephemeral=True)
return
attendee_names = list(data.get("attendees", {}).values())
desc_lines = [
@@ -302,9 +319,11 @@ class TripCommands(commands.Cog):
@app_commands.autocomplete(trip=trip_autocomplete)
async def trip_join(self, interaction: discord.Interaction, trip: str):
await interaction.response.defer(ephemeral=True)
ok = await c2.join_trip(trip, str(interaction.user.id), interaction.user.display_name)
if ok:
result = await c2.join_trip(trip, str(interaction.user.id), interaction.user.display_name)
if result is True:
await interaction.followup.send("You're on the trip!")
elif result == "private":
await interaction.followup.send("This trip is private — you need an invite to join.")
else:
await interaction.followup.send("Failed to join trip.")
@@ -433,5 +452,64 @@ class TripCommands(commands.Cog):
await interaction.followup.send("Failed to leave event.")
# ------------------------------------------------------------------
# /trip invite
# ------------------------------------------------------------------
@trip_group.command(name="invite", description="Invite a Discord user to a private trip.")
@app_commands.describe(trip="The trip.", user="The user to invite.")
@app_commands.autocomplete(trip=trip_autocomplete)
async def trip_invite(self, interaction: discord.Interaction, trip: str, user: discord.Member):
await interaction.response.defer(ephemeral=True)
ok = await c2.invite_to_trip(trip, str(user.id))
if ok:
await interaction.followup.send(f"Invited {user.display_name} to the trip.")
else:
await interaction.followup.send("Failed to send invite.")
# ------------------------------------------------------------------
# /trip privacy
# ------------------------------------------------------------------
@trip_group.command(name="privacy", description="Set a trip to public or private.")
@app_commands.describe(trip="The trip.", visibility="public or private")
@app_commands.autocomplete(trip=trip_autocomplete)
@app_commands.choices(visibility=[
app_commands.Choice(name="Public — anyone can see and join", value="public"),
app_commands.Choice(name="Private — invite only", value="private"),
])
async def trip_privacy(self, interaction: discord.Interaction, trip: str, visibility: str):
await interaction.response.defer(ephemeral=True)
ok = await c2.set_trip_visibility(trip, visibility)
if ok:
await interaction.followup.send(f"Trip is now **{visibility}**.")
else:
await interaction.followup.send("Failed to update trip privacy.")
# ------------------------------------------------------------------
# /link
# ------------------------------------------------------------------
@app_commands.command(name="link", description="Link your Discord account to your DRB web account.")
@app_commands.describe(code="The 6-character code from the web app (Settings → Link Discord).")
async def link_account(self, interaction: discord.Interaction, code: str):
await interaction.response.defer(ephemeral=True)
result = await c2.link_discord_account(
code.upper().strip(),
str(interaction.user.id),
interaction.user.display_name,
)
if "error" in result:
msgs = {
"invalid_code": "Invalid code. Generate a new one from the web app.",
"expired": "Code has expired. Generate a new one from the web app.",
"already_linked": "This Discord account is already linked to a different web account.",
"failed": "Something went wrong. Try again.",
}
await interaction.followup.send(msgs.get(result["error"], "Failed to link account."))
else:
await interaction.followup.send("Your Discord account is now linked to your DRB web account.")
async def setup(bot: commands.Bot):
await bot.add_cog(TripCommands(bot))
@@ -112,7 +112,55 @@ class C2Client:
logger.error(f"C2 delete_trip failed: {e}")
return False
async def join_trip(self, trip_id: str, user_id: str, username: str) -> bool:
async def invite_to_trip(self, trip_id: str, discord_user_id: str) -> bool:
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.post(
f"{self.base}/trips/{trip_id}/invite/{discord_user_id}",
headers=self._headers(),
)
r.raise_for_status()
return True
except Exception as e:
logger.error(f"C2 invite_to_trip failed: {e}")
return False
async def set_trip_visibility(self, trip_id: str, visibility: str) -> bool:
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.put(
f"{self.base}/trips/{trip_id}/visibility",
json={"visibility": visibility},
headers=self._headers(),
)
r.raise_for_status()
return True
except Exception as e:
logger.error(f"C2 set_trip_visibility failed: {e}")
return False
async def link_discord_account(self, code: str, discord_user_id: str, discord_username: str) -> dict:
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.post(
f"{self.base}/auth/link",
json={"code": code, "discord_user_id": discord_user_id, "discord_username": discord_username},
headers=self._headers(),
)
if r.status_code == 404:
return {"error": "invalid_code"}
if r.status_code == 410:
return {"error": "expired"}
if r.status_code == 409:
return {"error": "already_linked"}
r.raise_for_status()
return r.json()
except Exception as e:
logger.error(f"C2 link_discord_account failed: {e}")
return {"error": "failed"}
async def join_trip(self, trip_id: str, user_id: str, username: str) -> bool | str:
"""Returns True on success, 'private' on 403, False on other errors."""
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.post(
@@ -120,6 +168,8 @@ class C2Client:
json={"discord_user_id": user_id, "discord_username": username},
headers=self._headers(),
)
if r.status_code == 403:
return "private"
r.raise_for_status()
return True
except Exception as e: