mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
PM-23761 use the auto-reply endpoint in freskdesk to add a reply to a note
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MarkDig" Version="0.41.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ public class BillingSettings
|
||||
public virtual string Region { get; set; }
|
||||
public virtual string UserFieldName { get; set; }
|
||||
public virtual string OrgFieldName { get; set; }
|
||||
|
||||
public virtual bool RemoveNewlinesInReplies { get; set; } = false;
|
||||
public virtual string AutoReplyFooter { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OnyxSettings
|
||||
|
||||
@@ -11,6 +11,7 @@ using Bit.Billing.Models;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Markdig;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -184,6 +185,52 @@ public class FreshdeskController : Controller
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("webhook-onyx-ai-reply")]
|
||||
public async Task<IActionResult> PostWebhookOnyxAiReply([FromQuery, Required] string key,
|
||||
[FromBody, Required] FreshdeskOnyxAiWebhookModel model)
|
||||
{
|
||||
// NOTE:
|
||||
// at this time, this endpoint is a duplicate of `webhook-onyx-ai`
|
||||
// eventually, we will merge both endpoints into one webhook for Freshdesk
|
||||
|
||||
// ensure that the key is from Freshdesk
|
||||
if (!IsValidRequestFromFreshdesk(key))
|
||||
{
|
||||
return new BadRequestResult();
|
||||
}
|
||||
|
||||
// if there is no description, then we don't send anything to onyx
|
||||
if (string.IsNullOrEmpty(model.TicketDescriptionText.Trim()))
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// create the onyx `answer-with-citation` request
|
||||
var onyxRequestModel = new OnyxAnswerWithCitationRequestModel(model.TicketDescriptionText, _billingSettings.Onyx.PersonaId);
|
||||
var onyxRequest = new HttpRequestMessage(HttpMethod.Post,
|
||||
string.Format("{0}/query/answer-with-citation", _billingSettings.Onyx.BaseUrl))
|
||||
{
|
||||
Content = JsonContent.Create(onyxRequestModel, mediaType: new MediaTypeHeaderValue("application/json")),
|
||||
};
|
||||
var (_, onyxJsonResponse) = await CallOnyxApi<OnyxAnswerWithCitationResponseModel>(onyxRequest);
|
||||
|
||||
// the CallOnyxApi will return a null if we have an error response
|
||||
if (onyxJsonResponse?.Answer == null || !string.IsNullOrEmpty(onyxJsonResponse?.ErrorMsg))
|
||||
{
|
||||
_logger.LogWarning("Error getting answer from Onyx AI. Freshdesk model: {model}\r\n Onyx query {query}\r\nresponse: {response}. ",
|
||||
JsonSerializer.Serialize(model),
|
||||
JsonSerializer.Serialize(onyxRequestModel),
|
||||
JsonSerializer.Serialize(onyxJsonResponse));
|
||||
|
||||
return Ok(); // return ok so we don't retry
|
||||
}
|
||||
|
||||
// add the reply to the ticket
|
||||
await AddReplyToTicketAsync(onyxJsonResponse.Answer, model.TicketId);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
private bool IsValidRequestFromFreshdesk(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)
|
||||
@@ -238,6 +285,43 @@ public class FreshdeskController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddReplyToTicketAsync(string note, string ticketId)
|
||||
{
|
||||
// if there is no content, then we don't need to add a note
|
||||
if (string.IsNullOrWhiteSpace(note))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// convert note from markdown to html
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
note = Markdig.Markdown.ToHtml(note, pipeline);
|
||||
|
||||
// clear out any new lines that Freshdesk doesn't like
|
||||
if (_billingSettings.FreshDesk.RemoveNewlinesInReplies)
|
||||
{
|
||||
note = note.Replace(Environment.NewLine, string.Empty);
|
||||
}
|
||||
|
||||
var replyBody = new FreshdeskReplyRequestModel
|
||||
{
|
||||
Body = $"{note}{_billingSettings.FreshDesk.AutoReplyFooter}",
|
||||
};
|
||||
|
||||
var replyRequest = new HttpRequestMessage(HttpMethod.Post,
|
||||
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}/reply", ticketId))
|
||||
{
|
||||
Content = JsonContent.Create(replyBody),
|
||||
};
|
||||
|
||||
var addReplyResponse = await CallFreshdeskApiAsync(replyRequest);
|
||||
if (addReplyResponse.StatusCode != System.Net.HttpStatusCode.Created)
|
||||
{
|
||||
_logger.LogError("Error adding reply to Freshdesk ticket. Ticket Id: {0}. Status: {1}",
|
||||
ticketId, addReplyResponse.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> CallFreshdeskApiAsync(HttpRequestMessage request, int retriedCount = 0)
|
||||
{
|
||||
try
|
||||
|
||||
9
src/Billing/Models/FreshdeskReplyRequestModel.cs
Normal file
9
src/Billing/Models/FreshdeskReplyRequestModel.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Bit.Billing.Models;
|
||||
|
||||
public class FreshdeskReplyRequestModel
|
||||
{
|
||||
[JsonPropertyName("body")]
|
||||
public required string Body { get; set; }
|
||||
}
|
||||
@@ -68,14 +68,16 @@
|
||||
"webhookKey": "SECRET"
|
||||
},
|
||||
"freshdesk": {
|
||||
"apiKey": "SECRET",
|
||||
"apiKey": "Jgqg5fmSsNMx6k5XUiD0",
|
||||
"webhookKey": "SECRET",
|
||||
"region": "US",
|
||||
"userFieldName": "cf_user",
|
||||
"orgFieldName": "cf_org"
|
||||
"orgFieldName": "cf_org",
|
||||
"removeNewlinesInReplies": true,
|
||||
"autoReplyFooter": "<br /><small><i>2024 Bitwarden</i></small>"
|
||||
},
|
||||
"onyx": {
|
||||
"apiKey": "SECRET",
|
||||
"apiKey": "on_tenant_i-16a83ec44869babc4.aDffNn2k4UvtRt_9KdlV_3lfjiLFdOe4mdAiE3jJb4thWUGURVETwFg1Xrtg5rwLvfqfcw_F6U0V87arVkARd7qHQxmL5vaC4k8gPxh3hTKJKEE7Rkc3BJHOSAiT-leINjWvj44hpIuTRcWazz_zafZi-_D6123wplCb3PG6UtiGlyxcK8FJCZKp9IEM4UTTYXUHc7nWUVZuhiREGPd5J7T8LIWoenfkSJ1Vhi2PoLDI6msMOLfSGuLT_Ma_pJZl",
|
||||
"baseUrl": "https://cloud.onyx.app/api",
|
||||
"personaId": 7
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user