Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@blebelo
Copy link
Owner

@blebelo blebelo commented Aug 8, 2025

Summary by CodeRabbit

  • New Features

    • Introduced WhatsApp chatbot integration for support ticket creation and management using AI-powered conversation.
    • Added support for sending and receiving WhatsApp messages via Twilio.
    • Enabled in-memory storage for ticket drafts during chat sessions.
    • Automated ticket creation from completed chat sessions.
  • Improvements

    • Enhanced configuration options for Twilio and OpenAI integration.
    • Broadened CORS policy to allow all origins for easier integration.
  • Chores

    • Added necessary Twilio and OpenAI package dependencies.

@blebelo blebelo self-assigned this Aug 8, 2025
@vercel
Copy link

vercel bot commented Aug 8, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
glass ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 8, 2025 1:57am

@coderabbitai
Copy link

coderabbitai bot commented Aug 8, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This change introduces a WhatsApp chatbot ticketing integration using Twilio and OpenAI. It adds new service classes, DTOs, and interfaces for ticket draft management, WhatsApp message handling, and AI-driven conversation. Project files and configuration are updated to include Twilio and OpenAI dependencies, relevant service registrations, and environment-based credentials.

Changes

Cohort / File(s) Change Summary
WhatsApp Chatbot Core Services
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs, .../Whatsapp/Dto/TicketDraftDto.cs, .../Whatsapp/IChatAppService.cs, .../Whatsapp/IMemoryDraftStore.cs, .../Whatsapp/ITwilioService.cs, .../Whatsapp/IWhatsappChatAppService.cs, .../Whatsapp/MemoryDraftStore.cs, .../Whatsapp/TwilioService.cs, .../Whatsapp/WhatsappChatAppService.cs
Introduced new classes and interfaces for WhatsApp ticketing: chat service using OpenAI, DTO for ticket drafts, in-memory draft store, Twilio messaging service, WhatsApp chat app service, and supporting interfaces.
Dependency Injection & Module Registration
aspnet-core/src/GlassTickets.Application/GlassTicketsApplicationModule.cs, aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs
Registered new WhatsApp and chatbot-related services (singleton and transient) in both application module and web host startup. Added HTTP client registration.
Twilio & OpenAI Integration
aspnet-core/src/GlassTickets.Application/GlassTickets.Application.csproj, aspnet-core/src/GlassTickets.Web.Host/GlassTickets.Web.Host.csproj
Added NuGet package references for Twilio and OpenAI libraries in both application and web host project files.
WhatsApp Webhook API Controller
aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs
Added new API controller for handling Twilio WhatsApp webhook events and status callbacks, integrating WhatsApp chat and Twilio services.
Configuration Updates
aspnet-core/src/GlassTickets.Web.Host/appsettings.json
Updated CORS policy, Twilio credentials (now using environment variables), added OpenAI configuration section, and removed unused phone number key.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Twilio
    participant WebhookController
    participant WhatsAppChatAppService
    participant MemoryDraftStore
    participant ChatAppService
    participant OpenAI
    participant TwilioService

    User->>Twilio: Sends WhatsApp message
    Twilio->>WebhookController: POST /api/TwilioWebhook/whatsapp
    WebhookController->>WhatsAppChatAppService: HandleIncomingMessageAsync(from, message)
    WhatsAppChatAppService->>MemoryDraftStore: GetDraft(sessionId)
    WhatsAppChatAppService->>ChatAppService: ProcessMessageAsync(userMessage, draft)
    ChatAppService->>OpenAI: POST chat completion (user message + draft)
    OpenAI-->>ChatAppService: AI response (friendly text + JSON)
    ChatAppService-->>WhatsAppChatAppService: (responseText, updatedDraft)
    WhatsAppChatAppService->>MemoryDraftStore: SaveDraft/ClearDraft(sessionId, updatedDraft)
    WhatsAppChatAppService-->>WebhookController: responseText
    WebhookController->>TwilioService: SendWhatsAppMessageAsync(to, responseText)
    TwilioService->>Twilio: Send WhatsApp message
    WebhookController-->>Twilio: Return TwiML response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • Feature/whatsapp chat service #42: Contains identical changes including Twilio package references, WhatsApp-related services and interfaces, ChatAppService implementation, in-memory draft storage, Twilio messaging service, WhatsApp chat handling service, webhook controller, and dependency injection registrations, indicating the same feature implementation.

Poem

🐇✨
In the warren, code hops anew,
WhatsApp and Twilio join the crew.
OpenAI whispers, "How may I assist?"
Drafts in memory, no ticket missed.
With webhooks and chat, support takes flight—
A rabbit’s delight, all through the night!
🌱📱

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 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 ccfb4d9 and cd81a14.

📒 Files selected for processing (1)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/WhatsappChatAppService.cs (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/WhatsappChatService

🪧 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.
  • 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.

Support

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

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 generate unit tests to generate unit tests for 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.

Copy link

@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: 19

🔭 Outside diff range comments (2)
aspnet-core/src/GlassTickets.Web.Host/appsettings.json (2)

8-8: Security risk: CORS set to allow all origins.

Setting CORS to "*" allows requests from any origin, which poses significant security risks in production environments. This should be restricted to specific trusted origins.

Consider using environment-specific configuration:

-"CorsOrigins": "*"
+"CorsOrigins": "${CORS_ORIGINS:https://glass-sage.vercel.app,http://localhost:3000}"

3-3: Remove hard-coded credentials from configuration

Scanning the repo uncovered hard-coded secrets in your JSON configs—both production and staging connection strings plus a static security key. These need to be moved to environment variables (or a secure vault) before merging.

• aspnet-core/src/GlassTickets.Web.Host/appsettings.json

  • Line 3: Replace the hard-coded connection string
  • Line 13: Replace the static SecurityKey

• aspnet-core/src/GlassTickets.Web.Host/appsettings.Staging.json

  • Line 3: Replace the staging connection string

Example diffs:

 aspnet-core/src/GlassTickets.Web.Host/appsettings.json
-   "Default": "Host=turntable.proxy.rlwy.net;Port=53335;Database=railway;Username=postgres;Password=oPvVfUbTBSGVHLAQeWHSOkVeuApVQWAa;"
+   "Default": "${CONNECTION_STRING}"
@@
-  "SecurityKey": "GlassTickets_19FA2860D298425C86C339DF905F033C",
+  "SecurityKey": "${SECURITY_KEY}",
 aspnet-core/src/GlassTickets.Web.Host/appsettings.Staging.json
-   "Default": "Server=10.0.75.1; Database=GlassTicketsDb; User=sa; Password=123qwe;"
+   "Default": "${STAGING_CONNECTION_STRING}"

Twilio and OpenAI keys are already using environment variables—please apply the same pattern to all other secrets.

🧹 Nitpick comments (10)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ITwilioService.cs (1)

5-8: Consider enhancing the interface design.

The interface is clean but could be improved:

  1. Return type: Consider returning Task<bool> or a result object to indicate success/failure
  2. Documentation: Add XML documentation to describe the method's purpose and parameters
  3. Parameter validation: Consider if parameters should be nullable or have validation attributes
+/// <summary>
+/// Service for sending WhatsApp messages via Twilio.
+/// </summary>
 public interface ITwilioService
 {
+    /// <summary>
+    /// Sends a WhatsApp message to the specified recipient.
+    /// </summary>
+    /// <param name="to">The recipient's WhatsApp number (e.g., "whatsapp:+1234567890")</param>
+    /// <param name="message">The message content to send</param>
+    /// <returns>A task that represents the asynchronous send operation</returns>
-    Task SendWhatsAppMessageAsync(string to, string message);
+    Task<bool> SendWhatsAppMessageAsync(string to, string message);
 }
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IWhatsappChatAppService.cs (1)

8-8: Remove redundant access modifier and improve documentation.

The method has issues with style and documentation:

  1. Redundant modifier: Interface methods are implicitly public, so the public modifier is unnecessary
  2. Missing documentation: Add XML documentation to describe the method's behavior
  3. Parameter naming: Consider more descriptive parameter names
+/// <summary>
+/// Handles incoming WhatsApp messages and generates appropriate responses.
+/// </summary>
+/// <param name="from">The sender's WhatsApp number</param>
+/// <param name="message">The incoming message content</param>
+/// <returns>A task containing the response message to send back</returns>
-public Task<string> HandleIncomingMessageAsync(string from, string message);
+Task<string> HandleIncomingMessageAsync(string from, string message);
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IMemoryDraftStore.cs (1)

5-10: Interface naming leans on implementation detail

IMemoryDraftStore couples the abstraction to a specific storage medium. Consider a storage-agnostic name (e.g., IDraftStore) to allow swapping to distributed stores (Redis/IDistributedCache) without API churn.

aspnet-core/src/GlassTickets.Application/Services/Whatsapp/MemoryDraftStore.cs (1)

7-9: Optional: Seal the store and consider TTL/distributed storage

  • Mark the class sealed to prevent subclassing of a stateful store.
  • Consider using IMemoryCache with expiration or IDistributedCache/Redis to avoid unbounded growth and enable multi-instance deployments.
-    public class MemoryDraftStore : IMemoryDraftStore
+    public sealed class MemoryDraftStore : IMemoryDraftStore
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/Dto/TicketDraftDto.cs (1)

5-15: Optional: clarify intent with XML docs and tighten DTO semantics

Add XML comments for properties the AI is expected to fill, and consider normalizing CustomerNumber (digits only) upstream. Not mandatory but improves maintainability.

aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs (2)

23-44: Avoid long blocking work in webhook path (OpenAI call + ticket creation)

Processing + replying within the webhook may exceed Twilio’s ~15s timeout under load. Consider acknowledging immediately and enqueueing processing (e.g., background job/queue), then send the outbound message via API.


46-54: Leverage status callbacks

You’re reading MessageSid and MessageStatus but not acting on them. Persist delivery/read/failure for observability and retries.

aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs (3)

22-53: Harden the OpenAI request: add basic controls and propagate cancellation

  • Optional: accept a CancellationToken to abort on client disconnects/timeouts.
  • Optional: set temperature and max_tokens to reduce variance and bound output size.

Apply this diff (interface change required):

- public async Task<(string responseText, TicketDraftDto updatedDraft)> ProcessMessageAsync(string userMessage, TicketDraftDto draft)
+ public async Task<(string responseText, TicketDraftDto updatedDraft)> ProcessMessageAsync(string userMessage, TicketDraftDto draft/*, CancellationToken ct = default*/)
 {
     var prompt = BuildPrompt(userMessage, draft);
 
     var requestBody = new
     {
         model = "gpt-4o-mini",
+        temperature = 0.2,
+        max_tokens = 350,
         messages = new[]
         {
             new { role = "system", content = "You are a helpful support assistant gathering ticket info." },
             new { role = "user", content = prompt }
         }
     };
@@
-    var response = await _httpClient.SendAsync(request);
+    var response = await _httpClient.SendAsync(request/*, ct*/);
     response.EnsureSuccessStatusCode();

76-92: Don’t silently swallow parsing errors; at least log or annotate

Currently, any exception returns oldDraft with no signal. Consider logging and adding a short note to the user reply when parsing fails (or a retry hint). If you prefer not to log here, bubble up a typed exception.


55-73: Prompting: make JSON emission explicit and consistent

Minor: you already request a friendly message plus JSON. Consider requiring the JSON be placed in a fenced block labeled json to simplify parsing and reduce ambiguity (your extractor supports this).

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d0b7077 and d41db55.

📒 Files selected for processing (15)
  • aspnet-core/src/GlassTickets.Application/GlassTickets.Application.csproj (1 hunks)
  • aspnet-core/src/GlassTickets.Application/GlassTicketsApplicationModule.cs (2 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/Dto/TicketDraftDto.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IChatAppService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IMemoryDraftStore.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ITwilioService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IWhatsappChatAppService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/MemoryDraftStore.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/TwilioService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/WhatsappChatAppService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Web.Host/GlassTickets.Web.Host.csproj (1 hunks)
  • aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs (2 hunks)
  • aspnet-core/src/GlassTickets.Web.Host/appsettings.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/TwilioService.cs (6)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ITwilioService.cs (1)
  • Task (7-7)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs (1)
  • Task (22-53)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/WhatsappChatAppService.cs (1)
  • Task (28-58)
aspnet-core/src/GlassTickets.Application/Services/Tickets/ITicketAppService.cs (3)
  • ITicketAppService (10-19)
  • GlassTickets (8-20)
  • Task (13-13)
aspnet-core/src/GlassTickets.Application/Services/Tickets/TicketAppService.cs (2)
  • TicketAppService (17-113)
  • GlassTickets (15-114)
aspnet-core/src/GlassTickets.Core/Domain/Tickets/Ticket.cs (1)
  • GlassTickets (6-24)
🪛 GitHub Check: Build Stage
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/TwilioService.cs

[warning] 37-37:
The variable 'ex' is declared but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Stage
🔇 Additional comments (5)
aspnet-core/src/GlassTickets.Application/GlassTickets.Application.csproj (1)

15-15: Twilio dependency approved at latest version
The Twilio NuGet package reference (<PackageReference Include="Twilio" Version="7.12.0" />) is already pinned to the current stable release as of August 2025, and there are no publicly known security advisories for 7.12.0. No changes are required.

  • Optional best practice: run
    dotnet list package --vulnerable
    or use NuGet’s built-in auditing before production deployment to catch any newly disclosed issues.
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/IChatAppService.cs (1)

8-8: Clarify nullability for parameters and return fields

If draft can be absent or cleared in the flow, annotate accordingly (TicketDraftDto?) and document behavior. Otherwise, enforce non-null with validations in implementation.

Would you like me to scan ChatAppService to confirm whether it ever returns a null updatedDraft and adjust signatures accordingly?

aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs (1)

73-79: Verify ChatAppService presence & HttpClient‐compatible constructor

I wasn’t able to locate a ChatAppService implementation in the codebase. Please ensure:

  • A ChatAppService class exists (e.g. in aspnet-core/src/GlassTickets.Web.Host/…/ChatAppService.cs).

  • It exposes a public constructor accepting HttpClient, for example:

    public ChatAppService(HttpClient httpClient, /* other dependencies */)
    {}

This constructor must align with your services.AddHttpClient<IChatAppService, ChatAppService>() registration.

aspnet-core/src/GlassTickets.Application/Services/Whatsapp/TwilioService.cs (1)

37-40: Remove unused variable warning and avoid losing diagnostic info

Static analysis flags ex as unused because it wasn’t passed through. The proposed fix above uses it as the inner exception, preserving stack/context.

If you prefer to rethrow the original exception, replace the catch block with catch { throw; } instead.

aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs (1)

56-59: TwiML response helper is fine

Minimal TwiML is acceptable when sending the actual reply via REST. LGTM.

Comment on lines +33 to +38
IocManager.IocContainer.Register(
Castle.MicroKernel.Registration.Component
.For<IMemoryDraftStore>()
.ImplementedBy<MemoryDraftStore>()
.LifestyleSingleton()
);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Avoid duplicate registrations of IMemoryDraftStore across containers

IMemoryDraftStore is also registered in Startup. Keep it in one place to avoid ambiguous resolutions. Recommendation: keep it here (module) and remove from Startup.

To audit duplicates across the repo:


🏁 Script executed:

#!/bin/bash
rg -n "IMemoryDraftStore|IChatAppService|AddHttpClient|ChatAppService|HttpClient\\(\\)" -S

Length of output: 2941


Remove duplicate IMemoryDraftStore registration

IMemoryDraftStore is registered both in GlassTicketsApplicationModule (via IocManager) and again in Startup.cs. To prevent ambiguous resolutions, keep the module registration and remove the one from Startup.

Files/locations to update:

  • aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs:76

Suggested change:

- services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/GlassTicketsApplicationModule.cs
lines 33 to 38, IMemoryDraftStore is correctly registered via IocManager. To fix
the duplicate registration issue, remove the IMemoryDraftStore registration from
aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs at line 76. This will
prevent ambiguous dependency injection resolutions by keeping only the module
registration.

Comment on lines +39 to +44
IocManager.IocContainer.Register(
Castle.MicroKernel.Registration.Component
.For<IChatAppService>()
.ImplementedBy<ChatAppService>()
.LifestyleSingleton()
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Do not register IChatAppService as singleton here; conflicts with typed HttpClient in Startup

IChatAppService is registered as a singleton in the module, but also registered as a typed HttpClient in Startup. This causes ambiguity and lifetime mismatch. Moreover, ChatAppService likely uses HttpClient; singletons may capture stale DNS and state.

Remove this block and rely on the typed client registration in Startup:

-            IocManager.IocContainer.Register(
-                Castle.MicroKernel.Registration.Component
-                .For<IChatAppService>()
-                .ImplementedBy<ChatAppService>()
-                .LifestyleSingleton()
-            );
📝 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
IocManager.IocContainer.Register(
Castle.MicroKernel.Registration.Component
.For<IChatAppService>()
.ImplementedBy<ChatAppService>()
.LifestyleSingleton()
);
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/GlassTicketsApplicationModule.cs
around lines 39 to 44, remove the registration of IChatAppService as a singleton
using IocManager.IocContainer.Register. This conflicts with the typed HttpClient
registration in Startup and causes lifetime mismatches. Instead, rely solely on
the typed HttpClient registration in Startup to manage the service's lifetime
correctly.

Comment on lines +45 to +49
IocManager.IocContainer.Register(
Castle.MicroKernel.Registration.Component
.For<HttpClient>()
.LifestyleSingleton()
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Remove HttpClient singleton; use IHttpClientFactory

Registering HttpClient as a singleton is an anti-pattern (DNS/stale sockets). Use AddHttpClient in Startup to create typed/named clients.

-            IocManager.IocContainer.Register(
-                Castle.MicroKernel.Registration.Component
-                .For<HttpClient>()
-                .LifestyleSingleton()
-            );
📝 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
IocManager.IocContainer.Register(
Castle.MicroKernel.Registration.Component
.For<HttpClient>()
.LifestyleSingleton()
);
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/GlassTicketsApplicationModule.cs
around lines 45 to 49, remove the registration of HttpClient as a singleton from
the IoC container because it causes DNS and socket issues. Instead, configure
HttpClient instances using AddHttpClient in the Startup class to create typed or
named clients properly. This involves deleting the current HttpClient
registration code and moving HttpClient setup to the Startup configuration using
the IHttpClientFactory pattern.

Comment on lines +110 to +121
private string ExtractJson(string aiReply)
{
var jsonMatch = Regex.Match(aiReply, @"```json\s*(\{.*?\})\s*```", RegexOptions.Singleline);
if (jsonMatch.Success)
return jsonMatch.Groups[1].Value;

var braceMatch = Regex.Match(aiReply, @"\{.*\}", RegexOptions.Singleline);
if (braceMatch.Success)
return braceMatch.Value;

return null;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve JSON extraction robustness

Regex for {.*} is brittle with nested braces. Use a simple brace counter to extract the first well-formed JSON object.

Apply this diff:

-    private string ExtractJson(string aiReply)
-    {
-        var jsonMatch = Regex.Match(aiReply, @"```json\s*(\{.*?\})\s*```", RegexOptions.Singleline);
-        if (jsonMatch.Success)
-            return jsonMatch.Groups[1].Value;
-
-        var braceMatch = Regex.Match(aiReply, @"\{.*\}", RegexOptions.Singleline);
-        if (braceMatch.Success)
-            return braceMatch.Value;
-
-        return null;
-    }
+    private string ExtractJson(string aiReply)
+    {
+        if (string.IsNullOrWhiteSpace(aiReply)) return null;
+
+        var jsonBlock = Regex.Match(aiReply, @"```json\s*(\{[\s\S]*?\})\s*```", RegexOptions.Singleline);
+        if (jsonBlock.Success) return jsonBlock.Groups[1].Value;
+
+        // Fallback: scan for first balanced JSON object
+        int depth = 0, start = -1;
+        for (int i = 0; i < aiReply.Length; i++)
+        {
+            var c = aiReply[i];
+            if (c == '{')
+            {
+                if (depth == 0) start = i;
+                depth++;
+            }
+            else if (c == '}')
+            {
+                depth--;
+                if (depth == 0 && start >= 0)
+                {
+                    return aiReply.Substring(start, i - start + 1);
+                }
+            }
+        }
+        return null;
+    }
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
around lines 110 to 121, the current JSON extraction uses a brittle regex for
matching braces that fails with nested JSON objects. Replace the fallback regex
with a brace counting approach: first try to extract JSON inside ```json blocks
using regex, and if that fails, scan the string character-by-character to find
the first balanced JSON object by tracking opening and closing braces, then
return that substring. Also, handle null or whitespace input by returning null
immediately.

Comment on lines +17 to +24
public bool IsReadyForSubmission =>
!string.IsNullOrWhiteSpace(ReferenceNumber) &&
PriorityLevel.HasValue &&
!string.IsNullOrWhiteSpace(Location) &&
!string.IsNullOrWhiteSpace(Category) &&
!string.IsNullOrWhiteSpace(Description) &&
SendUpdates.HasValue &&
!string.IsNullOrWhiteSpace(CustomerNumber);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mismatch: readiness requires ReferenceNumber, but it’s generated at submission time

Requiring ReferenceNumber here prevents IsComplete from ever becoming true unless the AI supplies it. Since you generate a reference in WhatsappChatAppService when missing, drop ReferenceNumber from readiness.

Apply this diff:

 public bool IsReadyForSubmission =>
-    !string.IsNullOrWhiteSpace(ReferenceNumber) &&
-    PriorityLevel.HasValue &&
+    PriorityLevel.HasValue &&
     !string.IsNullOrWhiteSpace(Location) &&
     !string.IsNullOrWhiteSpace(Category) &&
     !string.IsNullOrWhiteSpace(Description) &&
     SendUpdates.HasValue &&
     !string.IsNullOrWhiteSpace(CustomerNumber);
📝 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
public bool IsReadyForSubmission =>
!string.IsNullOrWhiteSpace(ReferenceNumber) &&
PriorityLevel.HasValue &&
!string.IsNullOrWhiteSpace(Location) &&
!string.IsNullOrWhiteSpace(Category) &&
!string.IsNullOrWhiteSpace(Description) &&
SendUpdates.HasValue &&
!string.IsNullOrWhiteSpace(CustomerNumber);
public bool IsReadyForSubmission =>
PriorityLevel.HasValue &&
!string.IsNullOrWhiteSpace(Location) &&
!string.IsNullOrWhiteSpace(Category) &&
!string.IsNullOrWhiteSpace(Description) &&
SendUpdates.HasValue &&
!string.IsNullOrWhiteSpace(CustomerNumber);
🤖 Prompt for AI Agents
In
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/Dto/TicketDraftDto.cs
between lines 17 and 24, the IsReadyForSubmission property incorrectly requires
ReferenceNumber to be non-empty, but ReferenceNumber is generated later during
submission. Remove the check for ReferenceNumber from the IsReadyForSubmission
condition so readiness does not depend on it.

Comment on lines +42 to +44
"AccountSid": "${TWILIO_ACCOUNTSID}",
"AuthToken": "${TWILIO_AUTHTOKEN}",
"WhatsAppNumber": "whatsapp:+14155238886"
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify environment variable syntax and configuration security.

Two concerns with the Twilio configuration:

  1. Environment variable syntax: The ${VARIABLE} syntax may not work in .NET Core. Standard .NET typically expects environment variables to be resolved at runtime through IConfiguration.

  2. Hardcoded phone number: The WhatsApp number is hardcoded, which reduces flexibility across environments.

Consider making the phone number configurable:

-"WhatsAppNumber": "whatsapp:+14155238886"
+"WhatsAppNumber": "${TWILIO_WHATSAPP_NUMBER:whatsapp:+14155238886}"

🌐 Web query:

What is the correct syntax for environment variable substitution in ASP.NET Core appsettings.json files?

💡 Result:

In ASP.NET Core, environment variable substitution in appsettings.json files isn't done directly within the JSON file itself. Instead, ASP.NET Core uses a configuration system that supports multiple configuration providers, including environment variables, and merges them at runtime.

However, if you want to use environment variables to override or substitute values defined in appsettings.json, here is how it works:

How Environment Variable Overrides Work

  • You define default values in appsettings.json.
  • You set environment variables that follow a naming convention to override values.

Naming Convention for Environment Variables

  • Use double underscore __ as a delimiter to represent nested sections.
  • Use uppercase letters with underscores for key names.

Example:

Given this appsettings.json file:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=myServer;Database=myDb;User=myUser;Password=myPass;"
  }
}

To override DefaultConnection using environment variables:

Set an environment variable named:

ConnectionStrings__DefaultConnection

with the value you want to substitute.

Summary:

  • No special syntax inside the JSON file for placeholders.
  • Set environment variables externally following the naming convention with __.
  • ASP.NET Core configuration automatically merges them at runtime.

If you want to do value substitution with placeholders inside appsettings.json, that's not natively supported by ASP.NET Core configuration.

Would you like a concrete example or instructions on how to set this up in code?


Replace ${…} placeholders with proper ASP.NET Core environment-variable overrides

  • File: aspnet-core/src/GlassTickets.Web.Host/appsettings.json (lines 42–44)
  • Remove the unsupported ${TWILIO_…} syntax in JSON—ASP.NET Core merges environment variables at runtime via IConfiguration.
  • Populate AccountSid, AuthToken, and WhatsAppNumber through env vars named Twilio__AccountSid, Twilio__AuthToken, and Twilio__WhatsAppNumber.

Proposed diff:

@@ -42,7 +42,7
-    "AccountSid": "${TWILIO_ACCOUNTSID}",
-    "AuthToken": "${TWILIO_AUTHTOKEN}",
-    "WhatsAppNumber": "whatsapp:+14155238886"
+    "AccountSid": "",
+    "AuthToken": "",
+    "WhatsAppNumber": "whatsapp:+14155238886"

Then set in your host/CI:

export Twilio__AccountSid=<your-account-sid>
export Twilio__AuthToken=<your-auth-token>
export Twilio__WhatsAppNumber=whatsapp:+14155238886

This leverages the built-in IConfiguration override mechanism for secure, environment-specific configuration.

🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Web.Host/appsettings.json lines 42 to 44, remove
the unsupported `${TWILIO_ACCOUNTSID}` and `${TWILIO_AUTHTOKEN}` placeholders
from the JSON file. Instead, leave these keys empty or with default values and
rely on ASP.NET Core's environment variable overrides by setting environment
variables named `Twilio__AccountSid`, `Twilio__AuthToken`, and
`Twilio__WhatsAppNumber` in your host or CI environment. This ensures secure and
environment-specific configuration using the built-in IConfiguration mechanism.

Comment on lines +23 to +44
[HttpPost("whatsapp")]
public async Task<IActionResult> HandleWhatsAppMessage()
{
try
{
var form = await Request.ReadFormAsync();

var from = form["From"].ToString();
var body = form["Body"].ToString();
var messageId = form["MessageSid"].ToString();

var responseText = await _whatsAppChatService.HandleIncomingMessageAsync(from, body);
await _twilioService.SendWhatsAppMessageAsync(from, responseText);
var twimlResponse = GenerateTwiMLResponse();

return Content(twimlResponse, "application/xml");
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error {ex.Message}");
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Validate Twilio signature and avoid leaking exception messages

  • Missing validation of X-Twilio-Signature allows anyone to POST to your webhook. Validate requests with Twilio.Security.RequestValidator.
  • Don’t include ex.Message in responses; it leaks internals. Return a generic TwiML ack and log server-side.

Apply this diff (adds signature validation and sanitizes error response; requires injecting IConfiguration or AuthToken via options):

 using GlassTickets.Services.Whatsapp;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Twilio.Security;
@@
     public class TwilioWebhookController : ControllerBase
     {
         private readonly IWhatsAppChatAppService _whatsAppChatService;
         private readonly ITwilioService _twilioService;
+        private readonly IConfiguration _config;
@@
-        public TwilioWebhookController(
-            IWhatsAppChatAppService whatsAppChatService,
-            ITwilioService twilioService)
+        public TwilioWebhookController(
+            IWhatsAppChatAppService whatsAppChatService,
+            ITwilioService twilioService,
+            IConfiguration config)
         {
             _whatsAppChatService = whatsAppChatService;
             _twilioService = twilioService;
+            _config = config;
         }
@@
         [HttpPost("whatsapp")]
+        [Consumes("application/x-www-form-urlencoded")]
         public async Task<IActionResult> HandleWhatsAppMessage()
         {
             try
             {
                 var form = await Request.ReadFormAsync();
 
+                // Twilio signature validation
+                var authToken = _config["Twilio:AuthToken"];
+                var validator = new RequestValidator(authToken);
+                var url = $"{Request.Scheme}://{Request.Host}{Request.Path}{Request.QueryString}";
+                var signature = Request.Headers["X-Twilio-Signature"].ToString();
+                if (!validator.Validate(url, form, signature))
+                {
+                    return Unauthorized();
+                }
+
                 var from = form["From"].ToString();
                 var body = form["Body"].ToString();
                 var messageId = form["MessageSid"].ToString();
 
                 var responseText = await _whatsAppChatService.HandleIncomingMessageAsync(from, body);
                 await _twilioService.SendWhatsAppMessageAsync(from, responseText);
                 var twimlResponse = GenerateTwiMLResponse();
 
                 return Content(twimlResponse, "application/xml");
             }
-            catch (Exception ex)
+            catch (Exception)
             {
-                return StatusCode(500, $"Internal server error {ex.Message}");
+                // Return minimal TwiML to avoid retries; log server-side instead of exposing details
+                return Content(GenerateTwiMLResponse(), "application/xml");
             }
         }
🤖 Prompt for AI Agents
In
aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs
around lines 23 to 44, the HandleWhatsAppMessage method lacks validation of the
X-Twilio-Signature header, which is necessary to ensure requests are genuinely
from Twilio. Inject the Twilio AuthToken via configuration or options, then use
Twilio.Security.RequestValidator to validate the incoming request's signature
against the full request URL and form parameters. Also, modify the catch block
to avoid returning the exception message in the response; instead, return a
generic TwiML acknowledgment and log the detailed error server-side for
debugging.

Comment on lines +39 to +41
<PackageReference Include="Twilio" Version="7.12.0" />
<PackageReference Include="Twilio.AspNet.Core" Version="8.1.1" />
<PackageReference Include="Twilio.AspNet.Mvc" Version="8.1.1" />
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Review package version consistency and necessity.

There are potential concerns with the Twilio package references:

  1. Version mismatch: The core Twilio package is at version 7.12.0 while the ASP.NET packages are at 8.1.1, which may cause compatibility issues.

  2. Package redundancy: Both Twilio.AspNet.Core and Twilio.AspNet.Mvc are included. Typically, for ASP.NET Core projects, you would only need Twilio.AspNet.Core.

Please verify:

  • Compatible versions across all Twilio packages
  • Whether both ASP.NET packages are necessary for this project

🌐 Web query:

What are the compatible versions of Twilio, Twilio.AspNet.Core, and Twilio.AspNet.Mvc packages, and what's the difference between Twilio.AspNet.Core and Twilio.AspNet.Mvc?

💡 Result:

The Twilio.AspNet.Core and Twilio.AspNet.Mvc packages are designed for different .NET frameworks and have distinct functionalities.

Twilio.AspNet.Core:

  • Target Framework: .NET 7.0 and higher.
  • Key Features:
    • Provides integration for ASP.NET Core applications.
    • Includes request validation filters and middleware for securing endpoints.
    • Supports Minimal APIs and endpoint filters in ASP.NET Core.
    • Allows adding the Twilio REST API client to the ASP.NET Core dependency injection container.
  • Installation:
    • Use the following command to install the latest version:
      dotnet add package Twilio.AspNet.Core --version 8.1.1
    • This package is compatible with .NET 7.0 and higher. (nuget.org)

Twilio.AspNet.Mvc:

  • Target Framework: .NET Framework 4.6.2 and higher.
  • Key Features:
    • Designed for ASP.NET MVC applications.
    • Provides model binding for webhook requests to strongly typed .NET objects.
    • Includes the [ValidateRequest] attribute for request validation.
    • Supports adding the Twilio REST API client to the ASP.NET MVC dependency injection container.
  • Installation:
    • Use the following command to install the latest version:
      dotnet add package Twilio.AspNet.Mvc --version 8.1.1
    • This package is compatible with .NET Framework 4.6.2 and higher. (nuget.org)

Key Differences:

  • Target Framework:
    • Twilio.AspNet.Core is intended for ASP.NET Core applications.
    • Twilio.AspNet.Mvc is intended for ASP.NET MVC applications on the .NET Framework.
  • Request Validation:
    • In Twilio.AspNet.Core, request validation is configured using middleware or filters.
    • In Twilio.AspNet.Mvc, request validation is achieved using the [ValidateRequest] attribute.
  • Model Binding:
    • Twilio.AspNet.Mvc offers model binding for webhook requests to strongly typed .NET objects, which is particularly useful in MVC controllers.
    • Twilio.AspNet.Core provides similar functionality but is tailored for the ASP.NET Core environment.

In summary, choose Twilio.AspNet.Core for ASP.NET Core applications and Twilio.AspNet.Mvc for ASP.NET MVC applications on the .NET Framework, based on your project's requirements.

Citations:


Align Twilio package versions and remove unnecessary MVC package

The Twilio dependencies in GlassTickets.Web.Host.csproj currently have mismatched major versions and include an MVC library that’s not needed in an ASP.NET Core project:

  • Synchronize major versions:
    Twilio is at 7.12.0 while the ASP.NET extensions are at 8.1.1. This mismatch can lead to runtime or binding conflicts. Update Twilio to 8.1.1 (or the latest 8.x) to match.
  • Remove Twilio.AspNet.Mvc:
    • This package targets ASP.NET MVC on .NET Framework 4.6.2+ and is not applicable to ASP.NET Core on .NET 7+. In ASP.NET Core projects, you only need Twilio.AspNet.Core.

Proposed change in aspnet-core/src/GlassTickets.Web.Host/GlassTickets.Web.Host.csproj:

-  <PackageReference Include="Twilio" Version="7.12.0" />
+  <PackageReference Include="Twilio" Version="8.1.1" />
   <PackageReference Include="Twilio.AspNet.Core" Version="8.1.1" />
-  <PackageReference Include="Twilio.AspNet.Mvc" Version="8.1.1" />
📝 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
<PackageReference Include="Twilio" Version="7.12.0" />
<PackageReference Include="Twilio.AspNet.Core" Version="8.1.1" />
<PackageReference Include="Twilio.AspNet.Mvc" Version="8.1.1" />
<PackageReference Include="Twilio" Version="8.1.1" />
<PackageReference Include="Twilio.AspNet.Core" Version="8.1.1" />
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Web.Host/GlassTickets.Web.Host.csproj around
lines 39 to 41, the Twilio package versions are mismatched and the MVC package
is unnecessary. Update the Twilio package version from 7.12.0 to 8.1.1 (or the
latest 8.x) to align with the ASP.NET Core extensions, and remove the
Twilio.AspNet.Mvc package reference entirely since it is not needed in an
ASP.NET Core project.

Comment on lines +73 to +79
//Chatbot Service
services.AddTransient<IWhatsAppChatAppService, WhatsappChatAppService>();
services.AddTransient<IChatAppService, ChatAppService>();
services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();
services.AddTransient<ITwilioService, TwilioService>();
services.AddHttpClient<IChatAppService, ChatAppService>();

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Duplicate/conflicting registrations for IChatAppService; keep typed HttpClient only

You register IChatAppService twice: as transient and as a typed HttpClient. Keep the typed client; remove the transient registration to avoid ambiguity and ensure IHttpClientFactory is used.

-            services.AddTransient<IChatAppService, ChatAppService>();
             services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();
             services.AddTransient<ITwilioService, TwilioService>();
             services.AddHttpClient<IChatAppService, ChatAppService>();
📝 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
//Chatbot Service
services.AddTransient<IWhatsAppChatAppService, WhatsappChatAppService>();
services.AddTransient<IChatAppService, ChatAppService>();
services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();
services.AddTransient<ITwilioService, TwilioService>();
services.AddHttpClient<IChatAppService, ChatAppService>();
//Chatbot Service
services.AddTransient<IWhatsAppChatAppService, WhatsappChatAppService>();
services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();
services.AddTransient<ITwilioService, TwilioService>();
services.AddHttpClient<IChatAppService, ChatAppService>();
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs around lines 73 to
79, remove the transient registration of IChatAppService and keep only the
AddHttpClient registration for IChatAppService to avoid duplicate/conflicting
service registrations and ensure the typed HttpClient is used properly.

🛠️ Refactor suggestion

⚠️ Potential issue

Avoid double-registering IMemoryDraftStore across containers

IMemoryDraftStore is also registered in GlassTicketsApplicationModule as a singleton via Castle Windsor. Pick one place to register to prevent resolution ambiguity. Recommended: keep it in the module and remove from Startup.

-            services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>();

If you prefer to keep it in Startup, remove the corresponding registration from GlassTicketsApplicationModule (see suggested diff in that file).

🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Web.Host/Startup/Startup.cs around lines 73 to
79, IMemoryDraftStore is registered as a singleton, but it is also registered in
GlassTicketsApplicationModule, causing double registration and potential
resolution conflicts. To fix this, remove the AddSingleton<IMemoryDraftStore,
MemoryDraftStore>() line from Startup.cs and keep the registration only in
GlassTicketsApplicationModule to avoid ambiguity.

@blebelo blebelo merged commit 744b939 into main Aug 8, 2025
6 checks passed
Copy link

@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: 5

♻️ Duplicate comments (1)
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs (1)

166-177: Improve JSON extraction robustness

The regex pattern @"\{.*?\}" is non-greedy and will fail with nested JSON objects. The pattern @"\{.*\}" is greedy but still brittle with complex nesting. Consider using a brace-counting approach for more reliable extraction.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d41db55 and ccfb4d9.

📒 Files selected for processing (2)
  • aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs (1 hunks)
  • aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • aspnet-core/src/GlassTickets.Web.Host/Controllers/WhatsappWebHookController.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Stage

Comment on lines +14 to +15
private readonly HttpClient _httpClient;
private readonly string _apiKey;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding timeout and retry policies.

External API calls should have appropriate timeouts and retry logic for transient failures. Consider using Polly for resilience.

Configure the HttpClient with timeout in Startup.cs and consider adding Polly for retry logic:

services.AddHttpClient<ChatAppService>(client =>
{
    client.Timeout = TimeSpan.FromSeconds(30);
})
.AddPolicyHandler(GetRetryPolicy());

private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
around lines 14 to 15, the HttpClient is declared without timeout or retry
policies. To fix this, configure the HttpClient in Startup.cs by setting a
timeout (e.g., 30 seconds) and add a Polly retry policy for transient errors
with exponential backoff. Implement a method returning an
IAsyncPolicy<HttpResponseMessage> that retries 3 times with increasing delays,
and register this policy with AddPolicyHandler when adding the HttpClient
service.

public ChatAppService(HttpClient httpClient, IConfiguration config)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_apiKey = config["OpenAI:ApiKey"] ?? throw new ArgumentNullException("API key missing");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use appropriate exception type for missing configuration.

When the API key is missing from configuration, throwing ArgumentNullException is misleading since this exception is meant for null method arguments. Consider using InvalidOperationException or a custom configuration exception.

-        _apiKey = config["OpenAI:ApiKey"] ?? throw new ArgumentNullException("API key missing");
+        _apiKey = config["OpenAI:ApiKey"] ?? throw new InvalidOperationException("OpenAI API key is not configured");
📝 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
_apiKey = config["OpenAI:ApiKey"] ?? throw new ArgumentNullException("API key missing");
_apiKey = config["OpenAI:ApiKey"] ?? throw new InvalidOperationException("OpenAI API key is not configured");
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
at line 20, replace the ArgumentNullException thrown for a missing API key with
InvalidOperationException or a custom configuration exception, as
ArgumentNullException is intended for null method arguments and not
configuration errors.

Comment on lines +37 to +41
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey);
request.Content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

var response = await _httpClient.SendAsync(request);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Dispose HttpRequestMessage after use.

HttpRequestMessage implements IDisposable and should be properly disposed to release resources.

-        var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
-        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey);
-        request.Content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");
-
-        var response = await _httpClient.SendAsync(request);
+        using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
+        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey);
+        request.Content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");
+
+        var response = await _httpClient.SendAsync(request);
📝 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
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey);
request.Content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey);
request.Content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
around lines 37 to 41, the HttpRequestMessage instance is not disposed after
use. Wrap the creation and usage of HttpRequestMessage in a using statement or
use a try-finally block to ensure it is properly disposed after the SendAsync
call to release resources.

Comment on lines +41 to +46
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();

var json = await response.Content.ReadAsStringAsync();
var openAiResponse = JsonConvert.DeserializeObject<OpenAiResponse>(json);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for API calls.

The OpenAI API call lacks error handling. Network failures, rate limiting, or API errors will cause unhandled exceptions. Consider wrapping the API call in try-catch and providing meaningful error messages.

-        var response = await _httpClient.SendAsync(request);
-        response.EnsureSuccessStatusCode();
-
-        var json = await response.Content.ReadAsStringAsync();
-        var openAiResponse = JsonConvert.DeserializeObject<OpenAiResponse>(json);
+        try
+        {
+            var response = await _httpClient.SendAsync(request);
+            response.EnsureSuccessStatusCode();
+
+            var json = await response.Content.ReadAsStringAsync();
+            var openAiResponse = JsonConvert.DeserializeObject<OpenAiResponse>(json);
+        }
+        catch (HttpRequestException ex)
+        {
+            // Log the error and return a user-friendly message
+            throw new InvalidOperationException("Failed to communicate with AI service. Please try again later.", ex);
+        }
+        catch (JsonException ex)
+        {
+            // Log the error
+            throw new InvalidOperationException("Invalid response from AI service.", ex);
+        }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
around lines 41 to 46, the OpenAI API call lacks error handling which can cause
unhandled exceptions on network failures or API errors. Wrap the API call
including SendAsync, EnsureSuccessStatusCode, and deserialization in a try-catch
block. In the catch block, log or handle exceptions with meaningful error
messages to improve reliability and debugging.

Comment on lines +144 to +147
catch
{
return oldDraft;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Log exceptions instead of silently swallowing them.

The catch-all exception handler silently returns the old draft without any logging. This makes debugging difficult when JSON parsing fails.

Consider injecting an ILogger and logging the exception:

-        catch
+        catch (Exception ex)
         {
+            // _logger.LogWarning(ex, "Failed to parse AI response JSON");
             return oldDraft;
         }
🤖 Prompt for AI Agents
In aspnet-core/src/GlassTickets.Application/Services/Whatsapp/ChatAppService.cs
around lines 144 to 147, the catch block silently swallows exceptions without
logging, which hinders debugging. Modify the catch block to inject an ILogger
instance into the class and log the caught exception before returning oldDraft.
This will ensure that JSON parsing errors are recorded for troubleshooting.

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