How to access the chat transcript using the Genesys PSDK 8.5
Genesys stores the chat transcript in the UCS database in XML format, so the easiest way to get it is simply read the database, right? Wrong! If you want access to User Data or other info stored with the transcript, you have to use the Contacts SDK. Perhaps more important is that the only way to find out when a chat ends is to connect to the Interaction Server – Neither the Contacts nor the eServices/Chat SDKs have any way to monitor for chats ending.
Let’s deal with the Interaction server first.
Interaction Server
/Declare global variables
PropertyConfiguration ixnConfig = null;
WarmStandbyConfiguration warmStandbyConfig = null;
//Build Interaction Server connection
ixnConfig = new PropertyConfiguration();
ixnConfig.UseAddp = true;
ixnConfig.AddpServerTimeout = 10;
ixnConfig.AddpClientTimeout = 10;
ixnConfig.AddpTraceMode = AddpTraceMode.Both;
var ixnPrimaryEndpoint = new Endpoint("IXN", primaryHost, primaryPort, ixnConfig);
var ixnBackupEndpoint = new Endpoint("IXN", backupHost, backupPort, ixnConfig);
//Initialize the Interaction Protocol
ixnProtocol = new InteractionServerProtocol(ixnPrimaryEndpoint);
ixnProtocol.ClientName = ApplicationName;
//This is critical
ixnProtocol.ClientType = Genesyslab.Platform.OpenMedia.Protocols.InteractionServer.InteractionClient. ReportingEngine;
ixnProtocol.Received += OnIXNMessageReceived;
//Build the Warm Standby connection object
warmStandbyConfig = new WarmStandbyConfiguration(ixnPrimaryEndpoint, ixnBackupEndpoint);
warmStandbyConfig.Timeout = 1500;
warmStandbyConfig.Attempts = 2;
warmStandby = new WarmStandbyService(ixnProtocol);
warmStandby.ApplyConfiguration(warmStandbyConfig);
warmStandby.Channel.Opened += new EventHandler(OnChannelOpened);
warmStandby.Channel.Closed += new EventHandler(OnChannelClosed);
warmStandby.StateChanged += new EventHandler(OnWarmStandbyStateChanged);
warmStandby.SwitchedOver += new EventHandler(OnWarmStandbySwitchedOver);
warmStandby.Channel.Error += new EventHandler(OnChannelError);
warmStandby.Start();
//Subscribe for agent level reporting – This is where the real magic happens
Genesyslab.Platform.Commons.Collections.KeyValueCollection tenantList = new Genesyslab.Platform.Commons.Collections.KeyValueCollection();
tenantList.Add("tenant", "Resources");
RequestStartPlaceAgentStateReportingAll requestAll = RequestStartPlaceAgentStateReportingAll.Create(tenantList);
ixnProtocol.Send(requestAll);
Code language: C# (cs)
The RequestStartPlaceAgentStateReportingAll request subscribes to all agent/place state changes, which opens up a huge door to the interaction server. You are now effectively drinking from a fire hose, because all events will be coming across to your service. What we need to focus on is EventProcessingStopped, with a media type of “chat”.
void OnIXNMessageReceived(object sender, EventArgs e)
{
IMessage message = ((MessageEventArgs)e).Message;
if (message.Id == 161) //EventProcessingStopped
{
EventProcessingStopped eps = (EventProcessingStopped)message;
string ixnID = eps.Interaction.InteractionId;
string mediaType = eps.Interaction.InteractionMediatype;
//Get the SR number from User Data
string srNumber = eps.Interaction.InteractionUserData.GetAsString("SRNumber");
if (mediaType == "chat")
ProcessChatInteraction(ixnID, srNumber);
}
}
Code language: C# (cs)
Finally to verify the connection or close out the connection when you’re done:
public bool StopIXNMonitor()
{
ixnProtocol.BeginClose();
warmStandby.Stop();
ixnProtocol.Dispose();
return true;
}
public bool IsIXNConnected()
{
return (ixnProtocol.State == ChannelState.Opened);
}
Code language: C# (cs)
Universal Contact Server
Once we have a completed interaction, we can pull the data from the Contact Server. First we have to deal with the connection:
private UniversalContactServerProtocol ucsProtocol { get; set; }
private WarmStandbyService HaService { get; set; }</code>
public override bool IsConnected
{
get { return (ucsProtocol != null && ucsProtocol.State == ChannelState.Opened); }
}
public override void Connect(Endpoint primaryEndpoint, Endpoint backupEndpoint, params string[] args)
{
Application = args[0];
var userName = args[1];
var password = args[2];
if (IsConnected)
WaitForDisconnect();
ucsProtocol = new UniversalContactServerProtocol(primaryEndpoint);
ucsProtocol.ClientName = Application;
var haConfig = new WarmStandbyConfiguration(primaryEndpoint, backupEndpoint)
{
Timeout = ConnectionTimeout,
Attempts = ConnectionAttempts
};
HaService = new WarmStandbyService(ucsProtocol);
HaService.ApplyConfiguration(haConfig);
HaService.Start();
ucsProtocol.Open();
}
public override void Disconnect()
{
ucsProtocol.Close();
ucsProtocol.Dispose();
if (HaService.State != WarmStandbyState.Off)
HaService.Stop();
HaService.Dispose();
}
Code language: JavaScript (javascript)
Then we can actually access the data. First I built a class to hold what I care about seeing:
public class Interaction
{
public string StatusCode { get; set; }
public string StatusText { get; set; }
public string ID { get; set; }
public int Status { get; set; }
public string StructuredText { get; set; }
public string FormattedText { get; set; }
public bool IsComplete { get; set; }
public string SRNumber { get; set; }
public string UserId { get; set; }
public string SiteId { get; set; }
}
Code language: C# (cs)
Then I get the interaction and process it
public Interaction GetInteraction(string ID, string SRNumber)
{
Interaction interaction = new Interaction();
try
{
RequestGetInteractionContent request = new RequestGetInteractionContent();
request.InteractionId = ID;
request.IncludeAttachments = true;
var msg = ucsProtocol.Request(request);
if (msg == null)// || msg.Count <= 0)
{
interaction.StatusCode = "FAILED";
interaction.StatusText = "Endpoints not found";
throw new ApplicationException(interaction.StatusText);
}
else if (msg.Id == 100)
{
var errorDoc = ((EventError)msg).FaultString;
interaction.StatusCode = "FAILED";
interaction.StatusText = msg.Name + ":" + errorDoc.ToString();
throw new ApplicationException(interaction.StatusText);</code>
}
else
{
var respDoc = ((EventGetInteractionContent)msg).InteractionContent;
interaction.ID = ID;
interaction.StructuredText = respDoc.StructuredText.ToString();
interaction.FormattedText = ProcessChat(interaction.StructuredText);
var att = ((EventGetInteractionContent)msg).InteractionAttributes;
//SRNumber is attached by routing but is not available from UCS
//interaction.SRNumber = att.AllAttributes.GetAsString("SRNumber");
interaction.SRNumber = SRNumber;
interaction.SiteId = att.AllAttributes.GetAsString("Site_ID");
interaction.UserId = att.AllAttributes.GetAsString("oracle_id");
}
}
catch (Exception ex)
{
LogError(ex);
}
return interaction;
}
Code language: C# (cs)
Finally, I am almost ready to build the transcript…first I need a dictionary so I can manage the Chat parties:
[Serializable]
public class Party
{
public string UserID { get; set; }
public string UserName { get; set; }
}</code>
[Serializable]
public class PartyList : List {
}
Code language: C# (cs)
And a function to look them up by id:
private string GetUserName(PartyList parties, string userId)
{
foreach (Party party in parties)
{
if (party.UserID == userId)
return party.UserName;
}
return "Error";
}
Code language: PHP (php)
With that in place, I can parse the XML and build the transcript file:
private string ProcessChat(string structuredText)
{
string timeStamp="";
StringBuilder sb = new StringBuilder();
string ContactReason = "";</code>
//Load XML Document Object from String
XmlDocument doc = new XmlDocument();
doc.LoadXml(structuredText);
//Extract TimeStamp & Transcript (Attribute)
XmlNodeList timeStampNodes = doc.DocumentElement.SelectNodes("/chatTranscript");
XmlNodeList chatNodes = doc.DocumentElement.ChildNodes;
foreach (XmlNode node in timeStampNodes)
{
timeStamp = node.Attributes["startAt"].Value;
}
PartyList parties = new PartyList();
foreach (XmlNode node in chatNodes)
{
if (node.Name.Equals("newParty"))
{
Party party = new Party();
party.UserID = node.Attributes["userId"].Value;
if (node.FirstChild.Name.Equals("userInfo") && node.FirstChild.Attributes["userType"].Value.Equals("EXTERNAL"))
{
party.UserName = "(SYSTEM) ";
}
else if (node.FirstChild.Name.Equals("userInfo") && node.FirstChild.Attributes["userType"].Value.Equals("AGENT"))
{
party.UserName = "(AGENT) " + node.FirstChild.Attributes["userNick"].Value;
}
else if (node.FirstChild.Name.Equals("userInfo") && node.FirstChild.Attributes["userType"].Value.Equals("CLIENT"))
{
party.UserName = "(MEMBER) " + node.FirstChild.Attributes["userNick"].Value;
XmlNode userData = node.LastChild;
if (userData.Name == "userData")
foreach (XmlNode n in userData.ChildNodes)
{
if (n.Name == "item")
if (n.Attributes["key"].Value == "Contact_Reason")
{
ContactReason = n.InnerText;
break;
}
}
}
parties.Add(party);
}
else if (node.Name.Equals("message"))
{
if (node.FirstChild.Name.Equals("msgText"))
{
sb.Append(GetUserName(parties, node.Attributes["userId"].Value) + ": " + node.FirstChild.InnerText + "\r\n");
}
}
}
string transcript = timeStamp + "\r\n";
if (ContactReason != "")
transcript = transcript + "Contact Reason: " + ContactReason + "\r\n" + sb.ToString();
else
transcript = transcript + sb.ToString();
return transcript;
}
Code language: C# (cs)
That’s pretty much it. Good Luck!