Skip to content

Commit

Permalink
Merge pull request #2 from takenet/master
Browse files Browse the repository at this point in the history
Refresh
  • Loading branch information
lfmundim authored Oct 4, 2019
2 parents b38ee37 + 67742d0 commit 7b3108b
Show file tree
Hide file tree
Showing 133 changed files with 4,247 additions and 1,127 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

The **BLiP C# SDK** is a set of Nuget packages that allow the creation of [BLiP](https://blip.ai/) chatbots.

<a href="https://www.nuget.org/packages/Take.Blip.Client/" rel="Take.Blip.Client">![NuGet](https://img.shields.io/nuget/v/Take.Blip.Client.svg)</a>
<a href="https://www.nuget.org/packages/Take.Blip.Client/" rel="Take.Blip.Client">![NuGet](https://img.shields.io/nuget/v/Take.Blip.Client.svg)</a> [![Build status](https://ci.appveyor.com/api/projects/status/xj8p4kqk2d5nw17h?svg=true)](https://ci.appveyor.com/project/Take/blip-sdk-csharp)

## Requirements

Expand Down
4 changes: 2 additions & 2 deletions src/Samples/Builder.Console/Builder.Console.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Builder.Console/BuilderConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Builder.Console
{
public class BuilderConfiguration : ConventionsConfiguration
{
public override string ContextType => nameof(StorageContext);

}
}
130 changes: 130 additions & 0 deletions src/Samples/Builder.Console/BuilderDeskNotificationReceiver.cs
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);
}
}
}
140 changes: 132 additions & 8 deletions src/Samples/Builder.Console/BuilderMessageReceiver.cs
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}";
}
}
}
5 changes: 4 additions & 1 deletion src/Samples/Builder.Console/BuilderServiceProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using Serilog;
using SimpleInjector;
using Take.Blip.Builder.Hosting;
using Take.Blip.Client;
using Take.Blip.Client.Activation;

namespace Builder.Console
Expand All @@ -11,7 +13,8 @@ public BuilderServiceProvider()
{
Options.AllowOverridingRegistrations = true;
this.RegisterBuilder();
this.RegisterSingleton<IConfiguration, BuilderConfiguration>();
RegisterSingleton<IConfiguration, BuilderConfiguration>();
RegisterSingleton<ILogger>(new LoggerConfiguration().WriteTo.Trace().CreateLogger());
}

public void RegisterService(Type serviceType, object instance) => RegisterSingleton(serviceType, instance);
Expand Down
Loading

0 comments on commit 7b3108b

Please sign in to comment.