Skip to content

Conversation

@smokeyScraper
Copy link
Contributor

@smokeyScraper smokeyScraper commented Jun 21, 2025

Interactions

Discord

Screenshot 2025-06-21 123359

Screenshot 2025-06-21 123405

Screenshot 2025-06-21 123413

Screenshot 2025-06-21 123427

GitHub

Screenshot 2025-06-21 121138

Callback

Screenshot 2025-06-21 121158

image

Supabase

Screenshot 2025-06-21 121630

Screenshot 2025-06-21 121655

Summary by CodeRabbit

  • New Features

    • Introduced GitHub OAuth verification flow and session status endpoints for user verification.
    • Added Discord bot commands for GitHub verification, verification status checking, and improved help messaging.
    • Added periodic cleanup of expired verification tokens and enhanced error handling in the Discord bot.
    • Added support for direct messages (DMs) in the Discord bot.
  • Improvements

    • Updated database schema and models, including new conversation context tracking and enhanced user/repository fields.
    • Refined environment configuration with new backend URL support for easier setup.
    • Improved OAuth login parameterization and error logging.
    • Enhanced application lifecycle management and FastAPI integration for smoother startup and shutdown.
  • Bug Fixes

    • Improved error handling and user feedback throughout the verification process.
  • Chores

    • Updated example environment file and SQL scripts to match new schema and data structure.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 21, 2025

Walkthrough

This update introduces a comprehensive GitHub verification flow integrated with Supabase and Discord. It adds new FastAPI endpoints for OAuth callbacks and session status, refactors Supabase authentication to support asynchronous operations and better error handling, updates database models and schema, and enhances the Discord bot with verification commands, periodic cleanup, and improved user feedback.

Changes

Files / Areas Change Summary
backend/.env.example, backend/app/core/config.py Added BACKEND_URL environment variable and config setting; uncommented Supabase variables.
backend/app/api/v1/auth.py Added FastAPI router with endpoints for GitHub OAuth callback and verification session status, including styled HTML responses and logging.
backend/app/db/supabase/auth.py, backend/app/db/supabase/supabase_client.py Refactored to use asynchronous Supabase client, improved error handling, parameterized OAuth login functions, and unified client acquisition.
backend/app/db/supabase/users_service.py New module for managing users and verification sessions, including session creation, verification, expiry, and cleanup, with logging and DB operations.
backend/app/model/supabase/models.py Updated models: made fields optional, changed types, removed and added fields, introduced ConversationContext, removed CodeChunk, and simplified structures.
backend/app/scripts/supabase/create_db.sql, backend/app/scripts/supabase/populate_db.sql Updated database schema: new/modified tables (users, repositories, interactions, conversation_context), dropped/added fields, changed data types, removed code_chunks, and provided realistic sample data insertions.
backend/bots/discord/discord_bot.py Enabled DM intent for Discord bot to handle direct messages.
backend/bots/discord/discord_cogs.py Enhanced DevRelCommands cog: added periodic cleanup, verification status command, improved verification flow, error handling, DM fallback, and updated help.
backend/main.py Refactored app lifecycle: modularized background tasks, integrated FastAPI lifespan, improved error handling/logging, removed manual event loop, and added FastAPI endpoints for auth and favicon.

Sequence Diagram(s)

sequenceDiagram
    participant DiscordUser
    participant DiscordBot
    participant BackendAPI
    participant Supabase
    participant GitHub

    DiscordUser->>DiscordBot: !verify_github command
    DiscordBot->>BackendAPI: Create verification session
    BackendAPI->>Supabase: Get or create user/session
    BackendAPI-->>DiscordBot: Session ID
    DiscordBot->>DiscordUser: DM with OAuth link (includes session ID)
    DiscordUser->>GitHub: Authorize via OAuth
    GitHub->>BackendAPI: Redirect with code & session ID (/callback)
    BackendAPI->>Supabase: Exchange code for user info
    BackendAPI->>Supabase: Link GitHub to user, verify session
    BackendAPI-->>DiscordUser: HTML success/failure page
    DiscordUser->>DiscordBot: !verification_status command
    DiscordBot->>BackendAPI: Query session/user status
    BackendAPI-->>DiscordBot: Status info
    DiscordBot-->>DiscordUser: Status embed
Loading

Possibly related PRs

  • AOSSIE-Org/Devr.AI#80: Introduces initial GitHub OAuth verification flow, which this PR extends with session management and backend integration.
  • AOSSIE-Org/Devr.AI#74: Adds initial Supabase OAuth login functions and client setup, which are refactored and extended here for verification sessions.
  • AOSSIE-Org/Devr.AI#76: Refactors Discord DevRel commands into a cog, which is further enhanced in this PR with verification and status commands.

Suggested labels

enhancement

Suggested reviewers

  • chandansgowda

Poem

In the warren of code, a new path appears,
OAuth and Discord, now working as peers.
Supabase sessions, with tokens that last,
Verification flows—no more stuck in the past!
Rabbits rejoice, as users unite,
With GitHub and Discord, the future is bright.
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a35c8a and 4a05d78.

📒 Files selected for processing (2)
  • backend/app/db/supabase/users_service.py (1 hunks)
  • backend/app/scripts/supabase/create_db.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/app/db/supabase/users_service.py
  • backend/app/scripts/supabase/create_db.sql
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@smokeyScraper smokeyScraper self-assigned this Jun 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
backend/.env.example (1)

5-6: Consider reordering environment variables alphabetically

The static analysis tool correctly identifies that SUPABASE_SERVICE_ROLE_KEY should come before SUPABASE_URL for alphabetical ordering.

-SUPABASE_URL=
-SUPABASE_SERVICE_ROLE_KEY=
+SUPABASE_SERVICE_ROLE_KEY=
+SUPABASE_URL=
backend/app/api/v1/auth.py (1)

16-17: Consider masking sensitive data in logs

While session IDs are temporary, consider partially masking them in logs for better security hygiene.

-    logger.info(
-        f"OAuth callback received with code: {'[PRESENT]' if code else '[MISSING]'}, session: {'[PRESENT]' if session else '[MISSING]'}")
+    logger.info(
+        f"OAuth callback received with code: {'[PRESENT]' if code else '[MISSING]'}, session: {'[PRESENT]' if session else '[MISSING]'}")

For sensitive session logging elsewhere in the file, consider:

masked_session = f"{session[:8]}..." if session and len(session) > 8 else session
logger.info(f"Verifying user with session ID: {masked_session}")
backend/app/db/supabase/users_service.py (3)

10-11: Consider using persistent storage for verification sessions in production.

The in-memory _verification_sessions dictionary won't be shared across multiple worker processes, which could lead to session validation failures in a multi-worker deployment. Consider using Redis or storing sessions in the database for better scalability.


22-40: Remove unnecessary else block after return statement.

The else block is redundant since the function returns early when a user is found.

     if existing_user_res.data:
         logger.info(f"Found existing user for Discord ID: {discord_id}")
         return User(**existing_user_res.data[0])
-    else:
-        logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
-        new_user_data = {
-            "id": str(uuid.uuid4()),
-            "discord_id": discord_id,
-            "display_name": display_name,
-            "discord_username": discord_username,
-            "avatar_url": avatar_url,
-            "preferred_languages": [],
-            "created_at": datetime.now().isoformat(),
-            "updated_at": datetime.now().isoformat()
-        }
-        insert_res = await supabase.table("users").insert(new_user_data).execute()
-        if not insert_res.data:
-            raise Exception("Failed to create new user in database.")
-        return User(**insert_res.data[0])
+    
+    logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
+    new_user_data = {
+        "id": str(uuid.uuid4()),
+        "discord_id": discord_id,
+        "display_name": display_name,
+        "discord_username": discord_username,
+        "avatar_url": avatar_url,
+        "preferred_languages": [],
+        "created_at": datetime.now().isoformat(),
+        "updated_at": datetime.now().isoformat()
+    }
+    insert_res = await supabase.table("users").insert(new_user_data).execute()
+    if not insert_res.data:
+        raise Exception("Failed to create new user in database.")
+    return User(**insert_res.data[0])

79-86: Remove unnecessary else block after return statement.

Similar to the previous function, the else block is redundant.

         if update_res.data:
             _verification_sessions[session_id] = (discord_id, expiry_time)
             logger.info(
                 f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
             return session_id
-        else:
-            logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
-            return None
+        
+        logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
+        return None
backend/app/scripts/supabase/populate_db.sql (2)

108-113: Consider using clearly fake Discord IDs in test data.

The static analysis detected what appears to be Discord client IDs. While these seem to be test data, consider using clearly fake IDs (e.g., 'test-discord-thread-1') to avoid any confusion.


122-127: Consider using clearly fake Discord IDs in test data.

Same as above - consider using clearly fake IDs for test data.

backend/bots/discord/discord_cogs.py (1)

186-191: Consider adding more context to OAuth URL generation errors.

When the OAuth URL generation fails, it might be helpful to log the response data for debugging.

auth_url_data = await login_with_github(redirect_to=callback_url)
auth_url = auth_url_data.get("url")

if not auth_url:
+    logger.error(f"OAuth URL generation failed. Response: {auth_url_data}")
    raise Exception("Failed to generate OAuth URL.")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d877d2 and 09d7027.

📒 Files selected for processing (12)
  • backend/.env.example (2 hunks)
  • backend/app/api/v1/auth.py (1 hunks)
  • backend/app/core/config.py (1 hunks)
  • backend/app/db/supabase/auth.py (1 hunks)
  • backend/app/db/supabase/supabase_client.py (1 hunks)
  • backend/app/db/supabase/users_service.py (1 hunks)
  • backend/app/model/supabase/models.py (4 hunks)
  • backend/app/scripts/supabase/create_db.sql (1 hunks)
  • backend/app/scripts/supabase/populate_db.sql (1 hunks)
  • backend/bots/discord/discord_bot.py (1 hunks)
  • backend/bots/discord/discord_cogs.py (2 hunks)
  • backend/main.py (1 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
backend/.env.example

[warning] 6-6: [UnorderedKey] The SUPABASE_SERVICE_ROLE_KEY key should go before the SUPABASE_URL key

🪛 Pylint (3.3.7)
backend/app/db/supabase/users_service.py

[refactor] 22-40: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)


[refactor] 79-86: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)

backend/app/api/v1/auth.py

[refactor] 12-12: Too many return statements (9/6)

(R0911)

backend/main.py

[error] 74-74: Instance of 'DevRAIApplication' has no 'weaviate_client' member

(E1101)


[error] 75-75: Instance of 'DevRAIApplication' has no 'weaviate_client' member

(E1101)

backend/app/model/supabase/models.py

[refactor] 159-159: Too few public methods (0/2)

(R0903)

🪛 Gitleaks (8.26.0)
backend/app/scripts/supabase/populate_db.sql

109-109: Identified a Discord client ID, which may lead to unauthorized integrations and data exposure in Discord applications.

(discord-client-id)


123-123: Identified a Discord client ID, which may lead to unauthorized integrations and data exposure in Discord applications.

(discord-client-id)

🔇 Additional comments (24)
backend/bots/discord/discord_bot.py (1)

18-18: LGTM!

The addition of dm_messages intent is necessary for the verification flow to send OAuth links and status updates via DMs.

backend/app/core/config.py (1)

35-37: LGTM!

The backend URL configuration follows the existing pattern for optional settings.

backend/app/db/supabase/supabase_client.py (1)

2-13: LGTM!

The transition to AsyncClient is well-implemented with proper type annotations and clear documentation.

backend/app/api/v1/auth.py (3)

11-81: Multiple return statements are justified for OAuth flow

The static analysis flagged 9 return statements, but each handles a specific error case with appropriate user messaging. This is acceptable for an OAuth callback handler.


183-188: Good UX with auto-close functionality

The success response includes a 5-second auto-close feature with clear user notification, providing excellent user experience.


36-44: Verify Supabase async client response structure

The code expects session_response.user, but the Supabase async client might return the user data in a different structure.

#!/bin/bash
# Search for other usages of exchange_code_for_session to verify the response structure
ast-grep --pattern 'exchange_code_for_session($_)'
#!/bin/bash
# Look for Supabase auth response handling patterns
rg -A 5 "exchange_code_for_session|auth\.exchange" --type py
backend/app/db/supabase/users_service.py (1)

176-196: Well-implemented session info retrieval.

The function properly handles session cleanup and expiry checks before returning session information.

backend/app/scripts/supabase/create_db.sql (1)

46-54: Well-designed indexes for verification queries.

The indexes on verification_token_expires_at and the composite index on (discord_id, verification_token) will significantly improve query performance for the verification flow.

backend/app/db/supabase/auth.py (1)

7-26: Excellent OAuth flow enhancement.

The addition of redirect_to and state parameters properly supports the GitHub verification flow, and the error handling with stack traces will help with debugging.

backend/main.py (2)

88-96: Clean implementation of FastAPI lifespan management.

The use of asynccontextmanager for lifespan events properly handles startup and shutdown, ensuring background tasks are managed correctly.


109-117: Comprehensive environment variable validation.

Good addition of BACKEND_URL to the required variables list, which is essential for the OAuth callback flow.

backend/bots/discord/discord_cogs.py (6)

2-2: LGTM! Well-structured imports for the new verification features.

The imports are properly organized and include all necessary components for the GitHub verification flow.

Also applies to: 6-10, 13-13


21-26: Excellent implementation of task lifecycle management.

The cleanup task is properly started on initialization and cancelled on unload, preventing resource leaks and ensuring clean shutdown.


27-38: Well-implemented background cleanup task.

The task properly handles errors and waits for the bot to be ready before starting. The 5-minute interval aligns perfectly with the verification token expiry time mentioned in the user flow.


54-82: Clear and comprehensive help command update.

The help command now properly lists all available commands including the new verification commands, and provides a clear features overview.


84-132: Excellent implementation of the verification status command.

The command provides clear feedback about the user's verification status with proper error handling. The use of Discord's relative timestamp formatting enhances the user experience.


133-268: Comprehensive refactoring with excellent error handling and user feedback.

The command now handles multiple edge cases gracefully:

  • Already verified users
  • Pending verifications
  • DM permission issues
  • Backend configuration problems

The fallback from DM to channel with privacy notice is a thoughtful UX improvement.

backend/app/model/supabase/models.py (7)

15-15: Appropriate change to make email optional.

Making the email field optional aligns with the GitHub-first verification flow where users might not have an email initially.

Also applies to: 42-42


27-28: Good addition of verification token expiry tracking.

The verification_token_expires_at field properly supports the token expiry management implemented in the cleanup task.

Also applies to: 58-58


80-81: Good change to make github_id required.

Making github_id a required field ensures data integrity for GitHub repositories.

Also applies to: 99-99


88-89: Appropriate change from single language to languages list.

The change from language to languages_used as a list better represents repositories that use multiple programming languages.

Also applies to: 109-109


159-189: Well-designed ConversationContext model.

The new model properly captures conversation history with all necessary fields for tracking user interactions across platforms.

Ignoring the static analysis hint about too few public methods as Pydantic models don't require methods


126-127: Verify the impact of making repository_id optional.

Making repository_id optional in the Interaction model might affect queries that expect this field to always be present.

#!/bin/bash
# Description: Check for code that might assume repository_id is always present

# Search for interaction queries that might expect repository_id
rg "interaction.*repository_id" --type py -A 3

# Search for joins or filters on repository_id
ast-grep --pattern 'interaction.repository_id'

Also applies to: 142-142


30-31: I didn’t find any list‐specific usage of skills with the previous search. Let’s enumerate all occurrences and specifically look for list operations or iterations:

#!/bin/bash
# 1. List every reference to 'skills' in Python files
rg -n "skills" --type py

# 2. Look for dot‐access list methods on skills
rg -n "skills\." --type py | grep -E "(append|extend|insert|pop|remove)"

# 3. Search for iteration patterns over skills
rg -n "for .* in .*skills" --type py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
backend/app/db/supabase/users_service.py (1)

93-164: Address the race condition issue flagged in previous reviews.

This is a duplicate of the previously identified race condition. The function checks session expiry in-memory (line 109) but then queries the database with a separate expiry check (lines 116-118). This can lead to inconsistent behavior.

Remove the in-memory expiry check and rely solely on the database query for consistency:

-        session_data = _verification_sessions.get(session_id)
-        if not session_data:
-            logger.warning(f"No verification session found for session ID: {session_id}")
-            return None
-
-        discord_id, expiry_time = session_data
+        session_data = _verification_sessions.get(session_id)
+        if not session_data:
+            logger.warning(f"No verification session found for session ID: {session_id}")
+            return None
+
+        discord_id, _ = session_data  # Don't use expiry_time for validation

Additionally, consider making the Exception more specific on line 140:

-            raise Exception(f"GitHub account {github_username} is already linked to another Discord user")
+            raise ValueError(f"GitHub account {github_username} is already linked to another Discord user")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09d7027 and 0a35c8a.

📒 Files selected for processing (2)
  • backend/app/db/supabase/users_service.py (1 hunks)
  • backend/app/scripts/supabase/create_db.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/scripts/supabase/create_db.sql
🧰 Additional context used
🪛 Pylint (3.3.7)
backend/app/db/supabase/users_service.py

[refactor] 24-42: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)


[refactor] 81-88: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)

🔇 Additional comments (4)
backend/app/db/supabase/users_service.py (4)

1-14: LGTM! Clean module setup with appropriate imports.

The imports are well-organized and the global session storage approach is reasonable for this use case. The session expiry constant is clearly defined.


44-61: LGTM! Efficient in-memory cleanup implementation.

The cleanup logic is well-implemented with proper logging and handles expired sessions correctly.


166-183: LGTM! Effective database cleanup implementation.

The cleanup function properly handles expired tokens in the database with appropriate error handling and logging.


185-205: LGTM! Well-implemented session info retrieval.

The function correctly handles session validation and provides useful information about verification sessions.

Comment on lines 62 to 89
async def create_verification_session(discord_id: str) -> Optional[str]:
"""
Create a verification session with expiry and return session ID.
"""
supabase = get_supabase_client()

_cleanup_expired_sessions()

token = str(uuid.uuid4())
session_id = str(uuid.uuid4())
expiry_time = datetime.now() + timedelta(minutes=SESSION_EXPIRY_MINUTES)

try:
update_res = await supabase.table("users").update({
"verification_token": token,
"verification_token_expires_at": expiry_time.isoformat(),
"updated_at": datetime.now().isoformat()
}).eq("discord_id", discord_id).execute()

if update_res.data:
_verification_sessions[session_id] = (discord_id, expiry_time)
logger.info(
f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
return session_id
else:
logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
return None
except Exception as e:
logger.error(f"Error creating verification session for Discord ID {discord_id}: {str(e)}")
return None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary else clause and improve error specificity.

The function logic is correct but needs the same style improvements as the previous function.

Apply this diff:

-        if update_res.data:
+        if update_res.data:
             _verification_sessions[session_id] = (discord_id, expiry_time)
             logger.info(
                 f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
             return session_id
-        else:
-            logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
-            return None
+        
+        logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
+        return None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def create_verification_session(discord_id: str) -> Optional[str]:
"""
Create a verification session with expiry and return session ID.
"""
supabase = get_supabase_client()
_cleanup_expired_sessions()
token = str(uuid.uuid4())
session_id = str(uuid.uuid4())
expiry_time = datetime.now() + timedelta(minutes=SESSION_EXPIRY_MINUTES)
try:
update_res = await supabase.table("users").update({
"verification_token": token,
"verification_token_expires_at": expiry_time.isoformat(),
"updated_at": datetime.now().isoformat()
}).eq("discord_id", discord_id).execute()
if update_res.data:
_verification_sessions[session_id] = (discord_id, expiry_time)
logger.info(
f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
return session_id
else:
logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
return None
except Exception as e:
logger.error(f"Error creating verification session for Discord ID {discord_id}: {str(e)}")
return None
async def create_verification_session(discord_id: str) -> Optional[str]:
"""
Create a verification session with expiry and return session ID.
"""
supabase = get_supabase_client()
_cleanup_expired_sessions()
token = str(uuid.uuid4())
session_id = str(uuid.uuid4())
expiry_time = datetime.now() + timedelta(minutes=SESSION_EXPIRY_MINUTES)
try:
update_res = await supabase.table("users").update({
"verification_token": token,
"verification_token_expires_at": expiry_time.isoformat(),
"updated_at": datetime.now().isoformat()
}).eq("discord_id", discord_id).execute()
if update_res.data:
_verification_sessions[session_id] = (discord_id, expiry_time)
logger.info(
f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
return session_id
logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
return None
except Exception as e:
logger.error(f"Error creating verification session for Discord ID {discord_id}: {str(e)}")
return None
🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 81-88: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)

🤖 Prompt for AI Agents
In backend/app/db/supabase/users_service.py between lines 62 and 91, remove the
unnecessary else clause after checking update_res.data by returning None
directly after logging the error for user not found. Also, improve error
handling by catching more specific exceptions instead of a general Exception to
provide clearer error context. Adjust the try-except block accordingly to
reflect these changes.

Comment on lines 15 to 42
async def get_or_create_user_by_discord(
discord_id: str, display_name: str, discord_username: str, avatar_url: Optional[str]
) -> User:
"""
Get or create a user by Discord ID.
"""
supabase = get_supabase_client()
existing_user_res = await supabase.table("users").select("*").eq("discord_id", discord_id).limit(1).execute()

if existing_user_res.data:
logger.info(f"Found existing user for Discord ID: {discord_id}")
return User(**existing_user_res.data[0])
else:
logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
new_user_data = {
"id": str(uuid.uuid4()),
"discord_id": discord_id,
"display_name": display_name,
"discord_username": discord_username,
"avatar_url": avatar_url,
"preferred_languages": [],
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat()
}
insert_res = await supabase.table("users").insert(new_user_data).execute()
if not insert_res.data:
raise Exception("Failed to create new user in database.")
return User(**insert_res.data[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary else clause and improve error handling.

The logic is sound but there are two improvements needed:

  1. Static analysis correctly identifies an unnecessary else clause (pylint R1705)
  2. The generic Exception should be more specific

Apply this diff to address the issues:

-    if existing_user_res.data:
+    if existing_user_res.data:
         logger.info(f"Found existing user for Discord ID: {discord_id}")
         return User(**existing_user_res.data[0])
-    else:
-        logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
-        new_user_data = {
-            "id": str(uuid.uuid4()),
-            "discord_id": discord_id,
-            "display_name": display_name,
-            "discord_username": discord_username,
-            "avatar_url": avatar_url,
-            "preferred_languages": [],
-            "created_at": datetime.now().isoformat(),
-            "updated_at": datetime.now().isoformat()
-        }
-        insert_res = await supabase.table("users").insert(new_user_data).execute()
-        if not insert_res.data:
-            raise Exception("Failed to create new user in database.")
-        return User(**insert_res.data[0])
+    
+    logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
+    new_user_data = {
+        "id": str(uuid.uuid4()),
+        "discord_id": discord_id,
+        "display_name": display_name,
+        "discord_username": discord_username,
+        "avatar_url": avatar_url,
+        "preferred_languages": [],
+        "created_at": datetime.now().isoformat(),
+        "updated_at": datetime.now().isoformat()
+    }
+    insert_res = await supabase.table("users").insert(new_user_data).execute()
+    if not insert_res.data:
+        raise RuntimeError("Failed to create new user in database.")
+    return User(**insert_res.data[0])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def get_or_create_user_by_discord(
discord_id: str, display_name: str, discord_username: str, avatar_url: Optional[str]
) -> User:
"""
Get or create a user by Discord ID.
"""
supabase = get_supabase_client()
existing_user_res = await supabase.table("users").select("*").eq("discord_id", discord_id).limit(1).execute()
if existing_user_res.data:
logger.info(f"Found existing user for Discord ID: {discord_id}")
return User(**existing_user_res.data[0])
else:
logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
new_user_data = {
"id": str(uuid.uuid4()),
"discord_id": discord_id,
"display_name": display_name,
"discord_username": discord_username,
"avatar_url": avatar_url,
"preferred_languages": [],
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat()
}
insert_res = await supabase.table("users").insert(new_user_data).execute()
if not insert_res.data:
raise Exception("Failed to create new user in database.")
return User(**insert_res.data[0])
async def get_or_create_user_by_discord(
discord_id: str, display_name: str, discord_username: str, avatar_url: Optional[str]
) -> User:
"""
Get or create a user by Discord ID.
"""
supabase = get_supabase_client()
existing_user_res = await supabase.table("users") \
.select("*") \
.eq("discord_id", discord_id) \
.limit(1) \
.execute()
if existing_user_res.data:
logger.info(f"Found existing user for Discord ID: {discord_id}")
return User(**existing_user_res.data[0])
logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
new_user_data = {
"id": str(uuid.uuid4()),
"discord_id": discord_id,
"display_name": display_name,
"discord_username": discord_username,
"avatar_url": avatar_url,
"preferred_languages": [],
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat()
}
insert_res = await supabase.table("users").insert(new_user_data).execute()
if not insert_res.data:
raise RuntimeError("Failed to create new user in database.")
return User(**insert_res.data[0])
🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 24-42: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)

🤖 Prompt for AI Agents
In backend/app/db/supabase/users_service.py around lines 15 to 42, remove the
unnecessary else clause after checking if existing_user_res.data, by returning
early when the user is found. Also, replace the generic Exception raised on
insert failure with a more specific exception type, such as a custom
DatabaseError or a built-in exception like RuntimeError, to improve error
clarity and handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants