using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace QXS.ChatBot { internal class DescComparer : IComparer { public int Compare(T x, T y) { return Comparer.Default.Compare(y, x); } } /// /// The Chatbot /// public class ChatBot { /// /// A conversation started /// public event Action OnConverationStarted; /// /// A conversation ended /// public event Action OnConverationEnded; /// /// The chatbot received a messsage /// public event Action OnMessageReceived; /// /// The chatbot replied to a message /// public event Action OnMessageSent; /// /// Sets the Exit Condition for an ending conversation /// public Func ExitCondition; /// /// Sets the default resonse in case no appropriate Rule was found /// public Func DefaultAnswer; protected Stack _commandHistory = new Stack(); protected SortedList> _botRules = new SortedList>(new DescComparer()); /// /// Creaates the Chatbot /// /// public ChatBot(IEnumerable Rules) { Dictionary ruleNames = new Dictionary(); foreach (BotRule rule in Rules) { if (rule.Process == null) { throw new ArgumentException("Process is null.", "Rules"); } if (rule.MessagePattern == null) { throw new ArgumentException("MessagePattern is null.", "Rules"); } if (ruleNames.ContainsKey(rule.Name)) { throw new ArgumentException("Names are not unique. Duplicate key found for rule name \"" + rule.Name + "\".", "Rules"); } ruleNames[rule.Name] = true; if (!this._botRules.ContainsKey(rule.Weight)) { this._botRules[rule.Weight] = new List(); } this._botRules[rule.Weight].Add(rule); } ExitCondition = this.isGoodBye; } /// /// Find a matching rule/reponse to a question/message /// /// The session, that should be used /// The message that came in /// the response string or null in case no answer was found protected string findAnswer(ChatSessionInterface session, string messageIn) { foreach (List rules in this._botRules.Values) { foreach (BotRule rule in rules) { Match match = rule.MessagePattern.Match(messageIn); if (match.Success) { string msg = rule.Process(match, session); if (msg != null) { session.AddResponseToHistory(new BotResponse(rule.Name, messageIn, msg)); return msg; } } } } return null; } protected void sendResponse(ChatSessionInterface session, string messageOut) { session.sendMessage(messageOut); if (OnMessageSent != null) { OnMessageSent(session, messageOut); } } /// /// Starts a conversation over a session /// /// public void talkWith(ChatSessionInterface session) { if (session == null) { return; } if (OnConverationStarted != null) { OnConverationStarted(session); } string messageIn = ""; string messageOut = ""; for (messageIn = session.readMessage(); !this.ExitCondition(messageIn); messageIn = session.readMessage()) { if (OnMessageReceived != null) { OnMessageReceived(session, messageIn); } messageOut = findAnswer(session, messageIn); if (messageOut == null) { // do we have a default answer? if (DefaultAnswer != null) { messageOut = DefaultAnswer(messageIn); } // still null? if (messageOut == null) { sendResponse(session, "What did you say?"); } else { sendResponse(session, messageOut); } } else { sendResponse(session, messageOut); } // still interactive? if (!session.IsInteractive) { break; } } if (OnConverationEnded != null) { OnConverationEnded(session); } } /// /// Sets the default Exit Condition for the conversation /// /// /// Message, that came in /// Returns true, in case the conversation should be ended public bool isGoodBye(string message) { switch (message.ToLower()) { case "quit": return true; case "exit": return true; case "goodbye": return true; case "good bye": return true; case "bye": return true; } return false; } } }