-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/whatsapp chat service #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Caution Review failedThe pull request is closed. WalkthroughThis 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Poem
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 detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 configurationScanning 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:
- Return type: Consider returning
Task<bool>or a result object to indicate success/failure- Documentation: Add XML documentation to describe the method's purpose and parameters
- 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:
- Redundant modifier: Interface methods are implicitly public, so the
publicmodifier is unnecessary- Missing documentation: Add XML documentation to describe the method's behavior
- 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 detailIMemoryDraftStore 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 : IMemoryDraftStoreaspnet-core/src/GlassTickets.Application/Services/Whatsapp/Dto/TicketDraftDto.cs (1)
5-15: Optional: clarify intent with XML docs and tighten DTO semanticsAdd 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 callbacksYou’re reading
MessageSidandMessageStatusbut 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
CancellationTokento abort on client disconnects/timeouts.- Optional: set
temperatureandmax_tokensto 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 annotateCurrently, any exception returns
oldDraftwith 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 consistentMinor: you already request a friendly message plus JSON. Consider requiring the JSON be placed in a fenced block labeled
jsonto simplify parsing and reduce ambiguity (your extractor supports this).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 fieldsIf 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 constructorI wasn’t able to locate a
ChatAppServiceimplementation in the codebase. Please ensure:
A
ChatAppServiceclass exists (e.g. inaspnet-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 infoStatic analysis flags
exas 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 fineMinimal TwiML is acceptable when sending the actual reply via REST. LGTM.
| IocManager.IocContainer.Register( | ||
| Castle.MicroKernel.Registration.Component | ||
| .For<IMemoryDraftStore>() | ||
| .ImplementedBy<MemoryDraftStore>() | ||
| .LifestyleSingleton() | ||
| ); |
There was a problem hiding this comment.
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\\(\\)" -SLength 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.
| IocManager.IocContainer.Register( | ||
| Castle.MicroKernel.Registration.Component | ||
| .For<IChatAppService>() | ||
| .ImplementedBy<ChatAppService>() | ||
| .LifestyleSingleton() | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| 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.
| IocManager.IocContainer.Register( | ||
| Castle.MicroKernel.Registration.Component | ||
| .For<HttpClient>() | ||
| .LifestyleSingleton() | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| 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.
| 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; | ||
| } |
There was a problem hiding this comment.
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.
| public bool IsReadyForSubmission => | ||
| !string.IsNullOrWhiteSpace(ReferenceNumber) && | ||
| PriorityLevel.HasValue && | ||
| !string.IsNullOrWhiteSpace(Location) && | ||
| !string.IsNullOrWhiteSpace(Category) && | ||
| !string.IsNullOrWhiteSpace(Description) && | ||
| SendUpdates.HasValue && | ||
| !string.IsNullOrWhiteSpace(CustomerNumber); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
aspnet-core/src/GlassTickets.Application/Services/Whatsapp/WhatsappChatAppService.cs
Show resolved
Hide resolved
| "AccountSid": "${TWILIO_ACCOUNTSID}", | ||
| "AuthToken": "${TWILIO_AUTHTOKEN}", | ||
| "WhatsAppNumber": "whatsapp:+14155238886" |
There was a problem hiding this comment.
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:
-
Environment variable syntax: The
${VARIABLE}syntax may not work in .NET Core. Standard .NET typically expects environment variables to be resolved at runtime throughIConfiguration. -
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 viaIConfiguration. - Populate
AccountSid,AuthToken, andWhatsAppNumberthrough env vars namedTwilio__AccountSid,Twilio__AuthToken, andTwilio__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:+14155238886This 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.
| [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}"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate Twilio signature and avoid leaking exception messages
- Missing validation of
X-Twilio-Signatureallows anyone to POST to your webhook. Validate requests withTwilio.Security.RequestValidator. - Don’t include
ex.Messagein 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.
| <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" /> |
There was a problem hiding this comment.
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:
-
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.
-
Package redundancy: Both
Twilio.AspNet.CoreandTwilio.AspNet.Mvcare included. Typically, for ASP.NET Core projects, you would only needTwilio.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)
- Use the following command to install the latest version:
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)
- Use the following command to install the latest version:
Key Differences:
- Target Framework:
Twilio.AspNet.Coreis intended for ASP.NET Core applications.Twilio.AspNet.Mvcis 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.
- In
- Model Binding:
Twilio.AspNet.Mvcoffers model binding for webhook requests to strongly typed .NET objects, which is particularly useful in MVC controllers.Twilio.AspNet.Coreprovides 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:
- 1: https://www.nuget.org/packages/Twilio.AspNet.Core/?utm_source=openai
- 2: https://www.nuget.org/packages/Twilio.AspNet.Mvc?utm_source=openai
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:
•Twiliois at 7.12.0 while the ASP.NET extensions are at 8.1.1. This mismatch can lead to runtime or binding conflicts. UpdateTwilioto 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 needTwilio.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.
| <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.
| //Chatbot Service | ||
| services.AddTransient<IWhatsAppChatAppService, WhatsappChatAppService>(); | ||
| services.AddTransient<IChatAppService, ChatAppService>(); | ||
| services.AddSingleton<IMemoryDraftStore, MemoryDraftStore>(); | ||
| services.AddTransient<ITwilioService, TwilioService>(); | ||
| services.AddHttpClient<IChatAppService, ChatAppService>(); | ||
|
|
There was a problem hiding this comment.
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.
| //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
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.
There was a problem hiding this 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 robustnessThe 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
📒 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
| private readonly HttpClient _httpClient; | ||
| private readonly string _apiKey; |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| _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.
| 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| var response = await _httpClient.SendAsync(request); | ||
| response.EnsureSuccessStatusCode(); | ||
|
|
||
| var json = await response.Content.ReadAsStringAsync(); | ||
| var openAiResponse = JsonConvert.DeserializeObject<OpenAiResponse>(json); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| catch | ||
| { | ||
| return oldDraft; | ||
| } |
There was a problem hiding this comment.
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.
Summary by CodeRabbit
New Features
Improvements
Chores