Chat with us if you have any questions!

Virtual Agent for Sessions Timing Out

Super simple virtual agent for sending an email when a session times out.

Note: The Virtual Agent can be downloaded here (VirtualAgentSDK\Samples\CS-ScriptVirtualAgents\EmailDispatcher.cs) » Virtual Agent SDK (zip)

Virtual Agent - Send Email To Timed Out Sessions (zip)

Overview

Sometimes sessions time-out before routing to an agent in the queue and many Help Desks require alerting when this occurs. Although, Chime doesn’t currently have a native feature for responding to this scenario, a virtual agent plugin has been designed to handle this. Virtual agents are easy to deploy to new and existing Chime instances. This Virtual Agent responds to sessions that have timed-out by sending two e-mails: a follow-up e-mail to the client and a notification e-mail to a manger.

This Virtual Agent is written as a C# script file, so the subject and body of both these e-mails it easy to change with a text editor. To get started, replace the following SMTP variables in the virtual agent file:

_managerRecipientAddress This will be the manager's address or DL that receives a notification that a session has timed out (including the client's email address, question, and time they entered the queue)
_senderEmail This is the SMTP account that will be sending both e-mails
_senderPassword This is the Base 64 UTF-8 encoded password for the above account
_host This is the SMTP host for the sender
_port Change if necessary
_useSsl Change if necessary

How to Deploy:

To deploy the virtual agent, copy the virtual agent file to "C:\Program Files\Instant Technologies\Chime For Lync\Plugins" on the Chime server. Go to the Admin page in Chime and then the Virtual Agents section. First "Enable Virtual Agent Manager", then "Enable" the virtual agent named "Sends notification emails for timed-out sessions". Finally, assign this virtual agent to a queue in queue settings, under the People tab, then Virtual Agents. Select the Post-Conversation virtual agent and save your changes.

Code Samples

In the IncomingSeekerOffer method, the virtual agent only accepts sessions that are in the 'timed out state'.

public bool IncomingSeekerOffer(int sessionId, string queueName, int queueID, string email, string question, PreviousSessionState? prevState)
{
    if (prevState == PreviousSessionState.TimedOut)
    {
        return true;
    }
    return false;
}

    
Then the e-mails are dispatched from the SeekerConnected method.

public bool SeekerConnected(int sessionId)
{
    va_state = VirtualAgentState.Busy;
    PostChatData chatData = _pluginManager.PostChatEventClean(sessionId);
    DispatchEmail(chatData);
    bool keepAlive = true;
    _pluginManager.DisconnectVirtualAgent(sessionId, keepAlive);
    va_state = VirtualAgentState.Online;
    return true;
}

    

Full Source Code


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Xml.Linq;


/* © Instant Technologies 2016
 *
 * This is a simple virtual agent script file that will respond to sessions that have timed-out before routing to an agent.
 * The virtual agent will dispatch two e-mails
 *      1) a follow-up e-mail to the client
 *      2) a notification e-mail to a manager
 *
 *  In the IncomingSeekerOffer method the virtual agent only accepts sessions that are in the 'timed out state'.
 *  Then the e-mails are dispatched from the SeekerConnected method.
 *
 *  To get started, replace the following variables below:
 *      1) _managerRecipientAddress
 *          This will be the manager's address or DL that receives a notification that a session has timed out
 *          (including the client's email address, question, and time they entered the queue)
 *      2) _senderEmail
 *          This is the SMTP account that will be sending both e-mails
 *      3) _senderPassword
 *          This is the Base 64 UTF-8 encoded password for the above account
 *      4) _host
 *          This is the SMTP host for the sender
 *      5) _port
 *          Change if necessary
 *      6) _useSsl
 *          Change if necessary
 *
 *  To deploy the virtual agent, copy this file to "C:\Program Files\Instant Technologies\Chime For Lync\Plugins" on the Chime server.
 *  Go to the Admin page in Chime and then the Virtual Agents section.
 *  First "Enable Virtual Agent Manager", then "Enable" the virtual agent named "Sends notification emails for timed-out sessions"
 *  Finally, assign this virtual agent to a queue in queue settings, under the People tab, then Virtual Agents.
 *  Select the Post-Conversation virtual agent and save your changes.
 *
 */
public class SimpleVA_SendEmailToTimedOutSessions : IVirtualAgent
{
    // text for client's follow-up e-mail
    private string _clientEmailSubject = "We're sorry we didn't reach you.";
    private string _clientEmailBody = "We see you didn't reach an agent at our chat helpdesk. Please try again soon! Contact XXX-XXX-XXXX for more help immediately.";

    // recipient and text for manager's notification e-mail
    private string _managerRecipientAddress = "ENTER MANAGER'S EMAIL ADDRESS HERE";
    private string _managerEmailSubject = "Timed-out request in Chime";
    private string _managerEmailBody = "Client {0} timed out waiting to reach an agent in Chime at {1}. Their question was: {2}";

    // sender account settings
    private string _senderEmail = "ENTER EMAIL FOR ACCOUNT THAT WILL BE SENDING THE EMAILS";
    private string _senderPassword = "PASSWORD HERE"; //must be Base64 UTF-8 encoded
    private string _host = "smtp.MY-EMAIL-HOST.com";
    private int _port = 587;
    private bool _useSsl = true;

    private void DispatchEmail(PostChatData chatData)
    {
        try
        {
            var seekerEmail = "";
            if (chatData.seekerData.ContainsKey("seeker[email]"))
            {
                seekerEmail = chatData.seekerData["seeker[email]"];
            }
            else if (chatData.seekerData.ContainsKey("email"))
            {
                seekerEmail = chatData.seekerData["email"];
            }
            else
            {
                _pluginManager.LogMessageInChime(LoggingLevel.Debug,
                    "Unable to send follow-up email, seeker does not have an email address.");
                return;
            }
            //get the seeker's e-mail address
            _pluginManager.LogMessageInChime(LoggingLevel.Debug,
                "Seeker email: " + seekerEmail);

            //decode sender password
            var data = Convert.FromBase64String(_senderPassword);
            var pw = Encoding.UTF8.GetString(data);
            var Credential = new NetworkCredential(_senderEmail, pw);

            var _client = new SmtpClient(_host, _port)
            {
                EnableSsl = _useSsl,
                Credentials = Credential,
            };

            //send e-mail to client
            var msg = new MailMessage(_senderEmail, seekerEmail, _clientEmailSubject, _clientEmailBody);
            _client.Send(msg);
            _pluginManager.LogMessageInChime(LoggingLevel.Debug,
                "Success dispatching e-mail to client mailbox " + seekerEmail);

            //send e-mail to manager
            msg = new MailMessage(_senderEmail, _managerRecipientAddress, _managerEmailSubject, string.Format(_managerEmailBody, seekerEmail, chatData.sessionStartTime, chatData.question));
            _client.Send(msg);
            _pluginManager.LogMessageInChime(LoggingLevel.Debug,
                "Success dispatching e-mail to manager mailbox " + _managerRecipientAddress);
        }
        catch (Exception e)
        {
            _pluginManager.LogMessageInChime(LoggingLevel.Error, "Error dispatching e-mail:\n" + e.TargetSite + ", " + e.Message);
        }
    }

    IPluginManager _pluginManager; //This is how you make calls to Chime.
    VirtualAgentState va_state;

    /// <summary>
    /// Method called by the PluginManager when Chime starts, when an Office 365 queue starts,
    /// when global setting for virtual agents is turned on, and when the Plugin Manager is reloaded.
    /// </summary>
    /// <returns>The boolean represents whether this virtual agent is ready to be routed to. A virtual agent that returns false will be unavailable.
    /// The VirtualAgentProps is the properties that describe this virtual agent and is the data that will be displayed in Chime</returns>
    public Tuple<bool, VirtualAgentProps> Load()
    {
        va_state = VirtualAgentState.Online;
        bool isLoaded = true;
        var vaData = new VirtualAgentProps("Sends notification emails for timed-out sessions", "1", VirtualAgentType.PostConversation, "Sends a follow-up email to sessions that time-out before routing to an agent, and sends a notification e-mail to a manager as well.", "Instant Technologies"); //name, version, VirtualAgentType, description, author
        return new Tuple<bool, VirtualAgentProps>(isLoaded, vaData);
    }

    /// <summary>
    /// This method is called when the global setting for virtual agents is turned off. 
    /// This gives a chance for the virtual agent to do any necessary house keeping before it is unloaded by the system.
    /// </summary>
    /// <returns>A bool representing whether this virtual agent was able to "shut down" correctly</returns>
    public bool UnLoad()
    {
        return true;
    }

    /// <summary>
    /// If the virtual agent returns true in the Tuple it returns from the Load method, then the Plugin Manager will call this method and give this virtual agent a reference to itself. 
    /// This allows the virtual agent to have a reference to call the IPluginManager methods on.
    /// </summary>
    /// <param name="pm">Instance provided by the PluginManager after succesfully loading</param>
    public void SetPluginManager(IPluginManager pm)
    {
        _pluginManager = pm;
    }

    /// <summary>
    /// The Plugin Manager asks the virtual agent for its state.
    /// The virtual agent state must be Online to receive a seeker offer, to be connected with a seeker,
    /// to receive a message from the seeker, and to be notified that the seeker has left the session.
    /// </summary>
    /// <returns>The current state of the virtual agent</returns>
    public VirtualAgentState GetState()
    {
        return va_state;
    }

    /// <summary>
    /// The Plugin Manager will call this method when a seeker enters a queue and state that this virtual agent is associated with.
    /// These parameters are designed for certain virtual agents that can quickly tell if they want to be connected with the session or not,
    /// for example, only e-mail addresses from the @acme.com domain, or only questions that contain the word 'password'.
    /// </summary>
    /// <param name="sessionId">The Chime session ID associated with the session being offered</param>
    /// <param name="queueName">Name of the queue the seeker is coming from</param>
    /// <param name="queueID">ID of the queue the seeker is coming from</param>
    /// <param name="email">The email address of the seeker that is being offered</param>
    /// <param name="question">The question of the seeker that is being offered</param>
    /// <param name="prevState">The previous state of the session that is being offered</param>
    /// <returns>This boolean value represents whether the virtual agent will be connected with the session,
    /// if false, the Plugin Manager will move this session along to the next state in the session life-cycle</returns>
    public bool IncomingSeekerOffer(int sessionId, string queueName, int queueID, string email, string question, PreviousSessionState? prevState)
    {
        if (prevState == PreviousSessionState.TimedOut)
        {
            return true;
        }
        return false;
    }

    /// <summary>
    /// This method will be called by the Plugin Manager if true was returned from IncomingSeekerOffer for this session ID.
    /// </summary>
    /// <param name="sessionId">The Chime session ID associated with the session being connected</param>
    /// <returns>The bool value represents if the virtual agent was able to connect successfully, if the virtual agent returns false the session will move to the next state in the session lifecycle.
    /// If the virtual agent returns true, the session will stay connected until either the virtual agent or the seeker disconnect (virtual agents disconnect by called the PluginManager.DisconnectVirtualAgent method).</returns>
    public bool SeekerConnected(int sessionId)
    {
        va_state = VirtualAgentState.Busy;
        PostChatData chatData = _pluginManager.PostChatEventClean(sessionId);
        DispatchEmail(chatData);
        bool keepAlive = true;
        _pluginManager.DisconnectVirtualAgent(sessionId, keepAlive);
        va_state = VirtualAgentState.Online;
        return true;
    }

    /// <summary>
    /// The Plugin Manager will call this method to notify the virtual agent if the seeker closes their chat window, i.e., leaves the session.
    /// </summary>
    /// <param name="sessionId">The Chime session ID associated with the session being disconnected</param>
    public void SeekerDisconnected(int sessionId)
    {
    }

    /// <summary>
    /// The Plugin Manager will call this method to broker a message it received from the seeker while connected with the virtual agent.
    /// </summary>
    /// <param name="sessionId">The Chime session ID associated with the seeker sending the message</param>
    /// <param name="email">The email address of the sender</param>
    /// <param name="msg">The text of the message</param>
    /// <returns></returns>
    public bool ReceiveMessage(int sessionId, string email, string msg)
    {
        return true;
    }

}

    

Questions?

Have a question about Chime virtual agents? Ask one of our developers at InstantDev@instant-tech.com.