-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from takenet/master
Refresh
- Loading branch information
Showing
133 changed files
with
4,247 additions
and
1,127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
src/Samples/Builder.Console/BuilderDeskNotificationReceiver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Lime.Protocol; | ||
using Lime.Protocol.Serialization; | ||
using Take.Blip.Builder; | ||
using Take.Blip.Client; | ||
using Take.Blip.Client.Extensions.ArtificialIntelligence; | ||
using Take.Blip.Client.Extensions.HelpDesk; | ||
using Takenet.Iris.Messaging.Resources; | ||
|
||
namespace Builder.Console | ||
{ | ||
public class BuilderDeskNotificationReceiver : INotificationReceiver | ||
{ | ||
private readonly BuilderSettings _settings; | ||
private readonly IStateManager _stateManager; | ||
private readonly ISender _sender; | ||
private readonly IContextProvider _contextProvider; | ||
private readonly IHelpDeskExtension _helpDeskExtension; | ||
private readonly IUserOwnerResolver _userOwnerResolver; | ||
private readonly LazyInput _lazyInput; | ||
|
||
public BuilderDeskNotificationReceiver( | ||
BuilderSettings settings, | ||
IStateManager stateManager, | ||
ISender sender, | ||
IContextProvider contextProvider, | ||
IDocumentSerializer documentSerializer, | ||
IEnvelopeSerializer envelopeSerializer, | ||
IArtificialIntelligenceExtension artificialIntelligenceExtension, | ||
IHelpDeskExtension helpDeskExtension, | ||
IUserOwnerResolver userOwnerResolver) | ||
{ | ||
_settings = settings; | ||
_stateManager = stateManager; | ||
_sender = sender; | ||
_contextProvider = contextProvider; | ||
_helpDeskExtension = helpDeskExtension; | ||
_userOwnerResolver = userOwnerResolver; | ||
_lazyInput = new LazyInput( | ||
new Message(), | ||
new Identity(), | ||
_settings.Flow.BuilderConfiguration, | ||
documentSerializer, | ||
envelopeSerializer, | ||
artificialIntelligenceExtension, | ||
CancellationToken.None); | ||
} | ||
|
||
public async Task ReceiveAsync(Notification notification, CancellationToken cancellationToken) | ||
{ | ||
// Ignore intermediate notifications (Accepted, Dispatched) | ||
if (notification.Event == Event.Accepted || | ||
notification.Event == Event.Dispatched || | ||
notification.From?.Name == null || | ||
notification.From?.Domain == null || | ||
notification.From.Name == "postmaster") | ||
{ | ||
return; | ||
} | ||
|
||
// Check if there's an open ticket for the notification user | ||
var ticket = await GetTicketAsync(notification, cancellationToken); | ||
if (ticket == null) return; | ||
|
||
// Clone the notification and change the routing information | ||
var deskNotification = notification.ShallowCopy(); | ||
deskNotification.From = null; | ||
|
||
if (notification.From.Domain.Equals(HelpDeskExtension.DEFAULT_DESK_DOMAIN, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
deskNotification.To = (ticket.RoutingCustomerIdentity ?? ticket.CustomerIdentity).ToNode(); | ||
deskNotification.Metadata = new Dictionary<string, string> | ||
{ | ||
{"#message.to", deskNotification.From}, | ||
{"desk.ticketId", ticket.Id} | ||
}; | ||
} | ||
else | ||
{ | ||
deskNotification.To = Identity.Parse($"{ticket.Id}@{HelpDeskExtension.DEFAULT_DESK_DOMAIN}").ToNode(); | ||
deskNotification.Metadata = new Dictionary<string, string> | ||
{ | ||
{"#message.to", $"{ticket.Id}@{HelpDeskExtension.DEFAULT_DESK_DOMAIN}"} | ||
}; | ||
} | ||
|
||
await _sender.SendNotificationAsync(deskNotification, cancellationToken); | ||
} | ||
|
||
private async Task<Ticket> GetTicketAsync(Notification notification, CancellationToken cancellationToken) | ||
{ | ||
var (userIdentity, ownerIdentity) = await _userOwnerResolver.GetUserOwnerIdentitiesAsync( | ||
notification, _settings.Flow.BuilderConfiguration, cancellationToken); | ||
|
||
// If the notification is from 'desk.msging.net' domain, the user identity is encoded in the node 'name' property. | ||
if (notification.From.Domain.Equals(HelpDeskExtension.DEFAULT_DESK_DOMAIN, | ||
StringComparison.OrdinalIgnoreCase) && | ||
Identity.TryParse(Uri.UnescapeDataString(notification.From.Name), out var encodedUserIdentity)) | ||
{ | ||
userIdentity = encodedUserIdentity; | ||
} | ||
|
||
var context = _contextProvider.CreateContext(userIdentity, ownerIdentity, _lazyInput, _settings.Flow); | ||
|
||
// Check if the user is in a desk state | ||
var stateId = await _stateManager.GetStateIdAsync(context, cancellationToken); | ||
if (stateId == null || !stateId.StartsWith("desk:")) return null; | ||
|
||
var ticket = await GetCustomerActiveTicketAsync(userIdentity, cancellationToken); | ||
if (ticket == null && | ||
_settings.Flow.BuilderConfiguration?.UseTunnelOwnerContext == true) | ||
{ | ||
// This is just to support obsolete desk states with router context. | ||
// If you are seeing this and the current year is no longer 2019, you can safely remove this. | ||
var customerIdentity = notification.From.ToIdentity(); | ||
ticket = await GetCustomerActiveTicketAsync(customerIdentity, cancellationToken); | ||
} | ||
|
||
return ticket; | ||
} | ||
|
||
private Task<Ticket> GetCustomerActiveTicketAsync(Identity userIdentity, CancellationToken cancellationToken) | ||
{ | ||
return _helpDeskExtension.GetCustomerActiveTicketAsync(userIdentity, cancellationToken); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,150 @@ | ||
using System.Threading; | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Lime.Messaging.Contents; | ||
using Lime.Messaging.Resources; | ||
using Lime.Protocol; | ||
using Lime.Protocol.Serialization; | ||
using Take.Blip.Builder; | ||
using Take.Blip.Client; | ||
using Take.Blip.Client.Activation; | ||
using Take.Blip.Client.Extensions.ArtificialIntelligence; | ||
using Take.Blip.Client.Extensions.Contacts; | ||
using Take.Blip.Client.Extensions.Directory; | ||
using Take.Blip.Client.Extensions.HelpDesk; | ||
using Take.Blip.Client.Receivers; | ||
using Takenet.Iris.Messaging.Resources; | ||
|
||
namespace Builder.Console | ||
{ | ||
public class BuilderMessageReceiver : IMessageReceiver | ||
public class BuilderMessageReceiver : ContactMessageReceiverBase | ||
{ | ||
private readonly IFlowManager _flowManager; | ||
private readonly BuilderSettings _settings; | ||
private readonly Identity _applicationIdentity; | ||
private readonly ISender _sender; | ||
private readonly IStateManager _stateManager; | ||
private readonly IDocumentSerializer _documentSerializer; | ||
private readonly IEnvelopeSerializer _envelopeSerializer; | ||
private readonly IContextProvider _contextProvider; | ||
private readonly IUserOwnerResolver _userOwnerResolver; | ||
private readonly IArtificialIntelligenceExtension _artificialIntelligenceExtension; | ||
|
||
public BuilderMessageReceiver(IFlowManager flowManager, BuilderSettings settings, Application application) | ||
public BuilderMessageReceiver( | ||
IFlowManager flowManager, | ||
BuilderSettings settings, | ||
IContactExtension contactExtension, | ||
IDirectoryExtension directoryExtension, | ||
ISender sender, | ||
IStateManager stateManager, | ||
IDocumentSerializer documentSerializer, | ||
IEnvelopeSerializer envelopeSerializer, | ||
IArtificialIntelligenceExtension artificialIntelligenceExtension, | ||
IContextProvider contextProvider, | ||
IUserOwnerResolver userOwnerResolver) | ||
: base(contactExtension, directoryExtension) | ||
{ | ||
_flowManager = flowManager; | ||
_settings = settings; | ||
_applicationIdentity = $"{application.Identifier}@{application.Domain ?? Constants.DEFAULT_DOMAIN}"; | ||
_sender = sender; | ||
_stateManager = stateManager; | ||
_documentSerializer = documentSerializer; | ||
_envelopeSerializer = envelopeSerializer; | ||
_contextProvider = contextProvider; | ||
_userOwnerResolver = userOwnerResolver; | ||
_artificialIntelligenceExtension = artificialIntelligenceExtension; | ||
} | ||
|
||
public virtual Task ReceiveAsync(Message envelope, CancellationToken cancellationToken) | ||
=> _flowManager.ProcessInputAsync(envelope.Content, envelope.From.ToIdentity(), _applicationIdentity, _settings.Flow, cancellationToken); | ||
protected override async Task ReceiveAsync(Message message, Contact contact, CancellationToken cancellationToken = new CancellationToken()) | ||
{ | ||
var inputMessage = message; | ||
|
||
// Check if is a message from a desk agent to the customer | ||
if (message.From.Name != null && | ||
message.From.Domain != null && | ||
message.From.Domain.Equals(HelpDeskExtension.DEFAULT_DESK_DOMAIN, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
// Check for ticket transfer | ||
if (message.Content is Ticket ticket && | ||
ticket.Status == TicketStatusEnum.Transferred) | ||
{ | ||
return; | ||
} | ||
|
||
var originator = Node.Parse(Uri.UnescapeDataString(message.From.Name)); | ||
|
||
// If the content is a ticket or redirect, change the message originator | ||
if (message.Content is Ticket || | ||
message.Content is Redirect) | ||
{ | ||
inputMessage = inputMessage.ShallowCopy(); | ||
inputMessage.From = originator; | ||
} | ||
else | ||
{ | ||
// If not, just forward the message directly to the originator, which is the ticket customer | ||
await _sender.SendMessageAsync( | ||
new Message() | ||
{ | ||
Id = GetForwardId(message.Id), | ||
To = originator, | ||
Content = message.Content, | ||
Metadata = message.Metadata | ||
}, | ||
cancellationToken); | ||
|
||
return; | ||
} | ||
} | ||
|
||
// Check for redirects (from desk or a tunnel) | ||
if (inputMessage.Content is Redirect redirect) | ||
{ | ||
if (redirect.Context?.Value == null) | ||
{ | ||
return; | ||
} | ||
|
||
inputMessage = inputMessage.ShallowCopy(); | ||
inputMessage.Content = redirect.Context.Value; | ||
} | ||
|
||
// Ignore chatstate composing and paused messages when not on desk | ||
if (message.Content is ChatState chatState && | ||
(chatState.State == ChatStateEvent.Composing || chatState.State == ChatStateEvent.Paused)) | ||
{ | ||
// Determine if the current flow state is a desk state | ||
var state = await GetCurrentStateAsync(inputMessage, cancellationToken); | ||
if (state == null || | ||
!state.StartsWith("desk:", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
await _flowManager.ProcessInputAsync(inputMessage, _settings.Flow, cancellationToken); | ||
} | ||
|
||
private async Task<string> GetCurrentStateAsync(Message message, CancellationToken cancellationToken) | ||
{ | ||
var (userIdentity, ownerIdentity) = await _userOwnerResolver.GetUserOwnerIdentitiesAsync( | ||
message, _settings.Flow.BuilderConfiguration, cancellationToken); | ||
|
||
var lazyInput = new LazyInput( | ||
message, | ||
userIdentity, | ||
_settings.Flow.BuilderConfiguration, | ||
_documentSerializer, | ||
_envelopeSerializer, | ||
_artificialIntelligenceExtension, | ||
cancellationToken); | ||
|
||
var context = _contextProvider.CreateContext(userIdentity, ownerIdentity, lazyInput, _settings.Flow); | ||
return await _stateManager.GetStateIdAsync(context, cancellationToken); | ||
} | ||
|
||
private static string GetForwardId(string messageId) | ||
{ | ||
if (messageId == null) return null; | ||
return $"fwd:{messageId}"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.