├── BotDirectLine ├── BotDirectLineManager.cs ├── BotJsonProtocol.cs ├── BotResponseEventArgs.cs ├── ConversationState.cs ├── MessageActivity.cs └── SimpleJSON.cs ├── LICENSE └── README.md /BotDirectLine/BotDirectLineManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using UnityEngine.Networking; 5 | using Assets.BotDirectLine; 6 | using SimpleJSON; 7 | using System.Text; 8 | 9 | public class BotDirectLineManager 10 | { 11 | private const string DirectLineV3ApiUriPrefix = "https://directline.botframework.com/v3/directline"; 12 | private const string DirectLineConversationsApiUri = DirectLineV3ApiUriPrefix + "/conversations"; 13 | private const string DirectLineActivitiesApiUriPostfix = "activities"; 14 | private const string DirectLineChannelId = "directline"; 15 | 16 | private enum WebRequestMethods 17 | { 18 | Get, 19 | Post 20 | } 21 | 22 | public event EventHandler BotResponse; 23 | 24 | private static BotDirectLineManager _instance; 25 | public static BotDirectLineManager Instance 26 | { 27 | get 28 | { 29 | if (_instance == null) 30 | { 31 | _instance = new BotDirectLineManager(); 32 | } 33 | 34 | return _instance; 35 | } 36 | } 37 | 38 | public bool IsInitialized 39 | { 40 | get; 41 | private set; 42 | } 43 | 44 | private string SecretKey 45 | { 46 | get; 47 | set; 48 | } 49 | 50 | /// 51 | /// Constructor. 52 | /// 53 | private BotDirectLineManager() 54 | { 55 | IsInitialized = false; 56 | } 57 | 58 | /// 59 | /// Initializes this instance by setting the bot secret. 60 | /// 61 | /// The secret key of the bot. 62 | public static void Initialize(string secretKey) 63 | { 64 | if (string.IsNullOrEmpty(secretKey)) 65 | { 66 | throw new ArgumentException("Secret key cannot be null or empty"); 67 | } 68 | 69 | BotDirectLineManager instance = Instance; 70 | instance.SecretKey = secretKey; 71 | instance.IsInitialized = true; 72 | } 73 | 74 | /// 75 | /// Starts a new conversation with the bot. 76 | /// 77 | /// 78 | public IEnumerator StartConversationCoroutine() 79 | { 80 | if (IsInitialized) 81 | { 82 | UnityWebRequest webRequest = CreateWebRequest(WebRequestMethods.Post, DirectLineConversationsApiUri); 83 | 84 | yield return webRequest.Send(); 85 | 86 | if (webRequest.isError) 87 | { 88 | Debug.Log("Web request failed: " + webRequest.error); 89 | } 90 | else 91 | { 92 | string responseAsString = webRequest.downloadHandler.text; 93 | 94 | if (!string.IsNullOrEmpty(responseAsString)) 95 | { 96 | BotResponseEventArgs eventArgs = CreateBotResponseEventArgs(responseAsString); 97 | 98 | if (BotResponse != null) 99 | { 100 | BotResponse.Invoke(this, eventArgs); 101 | } 102 | } 103 | else 104 | { 105 | Debug.Log("Received an empty response"); 106 | } 107 | } 108 | } 109 | else 110 | { 111 | Debug.Log("Bot Direct Line manager is not initialized"); 112 | yield return null; 113 | } 114 | } 115 | 116 | /// 117 | /// Sends the given message to the given conversation. 118 | /// 119 | /// The conversation ID. 120 | /// The ID of the sender. 121 | /// The message to sent. 122 | /// The name of the sender (optional). 123 | /// 124 | public IEnumerator SendMessageCoroutine(string conversationId, string fromId, string message, string fromName = null) 125 | { 126 | if (string.IsNullOrEmpty(conversationId)) 127 | { 128 | throw new ArgumentException("Conversation ID cannot be null or empty"); 129 | } 130 | 131 | if (IsInitialized) 132 | { 133 | Debug.Log("SendMessageCoroutine: " + conversationId + "; " + message); 134 | 135 | UnityWebRequest webRequest = CreateWebRequest( 136 | WebRequestMethods.Post, 137 | DirectLineConversationsApiUri 138 | + "/" + conversationId 139 | + "/" + DirectLineActivitiesApiUriPostfix, 140 | new MessageActivity(fromId, message, DirectLineChannelId, null, fromName).ToJsonString()); 141 | 142 | yield return webRequest.Send(); 143 | 144 | if (webRequest.isError) 145 | { 146 | Debug.Log("Web request failed: " + webRequest.error); 147 | } 148 | else 149 | { 150 | string responseAsString = webRequest.downloadHandler.text; 151 | 152 | if (!string.IsNullOrEmpty(responseAsString)) 153 | { 154 | //Debug.Log("Received response:\n" + responseAsString); 155 | BotResponseEventArgs eventArgs = CreateBotResponseEventArgs(responseAsString); 156 | 157 | if (BotResponse != null) 158 | { 159 | BotResponse.Invoke(this, eventArgs); 160 | } 161 | } 162 | else 163 | { 164 | Debug.Log("Received an empty response"); 165 | } 166 | } 167 | } 168 | else 169 | { 170 | Debug.Log("Bot Direct Line manager is not initialized"); 171 | yield return null; 172 | } 173 | } 174 | 175 | /// 176 | /// Retrieves the activities of the given conversation. 177 | /// 178 | /// The conversation ID. 179 | /// Indicates the most recent message seen (optional). 180 | /// 181 | public IEnumerator GetMessagesCoroutine(string conversationId, string watermark = null) 182 | { 183 | if (string.IsNullOrEmpty(conversationId)) 184 | { 185 | throw new ArgumentException("Conversation ID cannot be null or empty"); 186 | } 187 | 188 | if (IsInitialized) 189 | { 190 | Debug.Log("GetMessagesCoroutine: " + conversationId); 191 | 192 | string uri = DirectLineConversationsApiUri 193 | + "/" + conversationId 194 | + "/" + DirectLineActivitiesApiUriPostfix; 195 | 196 | if (!string.IsNullOrEmpty(watermark)) 197 | { 198 | uri += "?" + BotJsonProtocol.KeyWatermark + "=" + watermark; 199 | } 200 | 201 | UnityWebRequest webRequest = CreateWebRequest(WebRequestMethods.Get, uri); 202 | yield return webRequest.Send(); 203 | 204 | if (webRequest.isError) 205 | { 206 | Debug.Log("Web request failed: " + webRequest.error); 207 | } 208 | else 209 | { 210 | string responseAsString = webRequest.downloadHandler.text; 211 | 212 | if (!string.IsNullOrEmpty(responseAsString)) 213 | { 214 | //Debug.Log("Received response:\n" + responseAsString); 215 | BotResponseEventArgs eventArgs = CreateBotResponseEventArgs(responseAsString); 216 | 217 | if (BotResponse != null) 218 | { 219 | BotResponse.Invoke(this, eventArgs); 220 | } 221 | } 222 | else 223 | { 224 | Debug.Log("Received an empty response"); 225 | } 226 | } 227 | } 228 | else 229 | { 230 | Debug.Log("Bot Direct Line manager is not initialized"); 231 | yield return null; 232 | } 233 | } 234 | 235 | /// 236 | /// Creates a new UnityWebRequest instance initialized with bot authentication and JSON content type. 237 | /// 238 | /// Defines whether to use GET or POST method. 239 | /// The request URI. 240 | /// The content to post (expecting JSON as UTF-8 encoded string or null). 241 | /// A newly created UnityWebRequest instance. 242 | private UnityWebRequest CreateWebRequest(WebRequestMethods webRequestMethod, string uri, string content = null) 243 | { 244 | Debug.Log("CreateWebRequest: " + webRequestMethod + "; " + uri + (string.IsNullOrEmpty(content) ? "" : ("; " + content))); 245 | 246 | UnityWebRequest webRequest = new UnityWebRequest(); 247 | 248 | webRequest.url = uri; 249 | webRequest.SetRequestHeader("Authorization", "Bearer " + SecretKey); 250 | 251 | if (webRequestMethod == WebRequestMethods.Get) 252 | { 253 | webRequest.method = "GET"; 254 | } 255 | else 256 | { 257 | webRequest.method = "POST"; 258 | } 259 | 260 | if (!string.IsNullOrEmpty(content)) 261 | { 262 | webRequest.uploadHandler = new UploadHandlerRaw(Utf8StringToByteArray(content)); 263 | } 264 | 265 | webRequest.downloadHandler = new DownloadHandlerBuffer(); 266 | webRequest.SetRequestHeader("Content-Type", "application/json"); 267 | 268 | return webRequest; 269 | } 270 | 271 | /// 272 | /// Creates a new BotResponseEventArgs instance based on the given response. 273 | /// 274 | /// 275 | /// 276 | private BotResponseEventArgs CreateBotResponseEventArgs(string responseAsString) 277 | { 278 | if (string.IsNullOrEmpty(responseAsString)) 279 | { 280 | throw new ArgumentException("Response cannot be null or empty"); 281 | } 282 | 283 | JSONNode responseJsonRootNode = JSONNode.Parse(responseAsString); 284 | JSONNode jsonNode = null; 285 | BotResponseEventArgs eventArgs = new BotResponseEventArgs(); 286 | 287 | if ((jsonNode = responseJsonRootNode[BotJsonProtocol.KeyError]) != null) 288 | { 289 | eventArgs.EventType = EventTypes.Error; 290 | eventArgs.Code = jsonNode[BotJsonProtocol.KeyCode]; 291 | string message = jsonNode[BotJsonProtocol.KeyMessage]; 292 | 293 | if (!string.IsNullOrEmpty(message)) 294 | { 295 | eventArgs.Message = message; 296 | } 297 | } 298 | else if (responseJsonRootNode[BotJsonProtocol.KeyConversationId] != null) 299 | { 300 | eventArgs.EventType = EventTypes.ConversationStarted; 301 | eventArgs.ConversationId = responseJsonRootNode[BotJsonProtocol.KeyConversationId]; 302 | } 303 | else if (responseJsonRootNode[BotJsonProtocol.KeyId] != null) 304 | { 305 | eventArgs.EventType = EventTypes.MessageSent; 306 | eventArgs.SentMessageId = responseJsonRootNode[BotJsonProtocol.KeyId]; 307 | } 308 | else if ((jsonNode = responseJsonRootNode[BotJsonProtocol.KeyActivities]) != null) 309 | { 310 | eventArgs.EventType = EventTypes.MessageReceived; 311 | eventArgs.Watermark = responseJsonRootNode[BotJsonProtocol.KeyWatermark]; 312 | JSONArray jsonArray = jsonNode.AsArray; 313 | 314 | foreach (JSONNode activityNode in jsonArray) 315 | { 316 | MessageActivity messageActivity = MessageActivity.FromJson(activityNode); 317 | eventArgs.Messages.Add(messageActivity); 318 | } 319 | } 320 | 321 | return eventArgs; 322 | } 323 | 324 | private byte[] Utf8StringToByteArray(string stringToBeConverted) 325 | { 326 | return Encoding.UTF8.GetBytes(stringToBeConverted); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /BotDirectLine/BotJsonProtocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Assets.BotDirectLine 4 | { 5 | public class BotJsonProtocol 6 | { 7 | // JSON keys in bot responses 8 | public const string KeyActivities = "activities"; 9 | public const string KeyActivityType = "type"; 10 | public const string KeyChannelId = "channelId"; 11 | public const string KeyCode = "code"; 12 | public const string KeyConversation = "conversation"; 13 | public const string KeyConversationId = "conversationId"; 14 | public const string KeyError = "error"; 15 | public const string KeyFrom = "from"; 16 | public const string KeyId = "id"; 17 | public const string KeyMessage = "message"; 18 | public const string KeyName = "name"; 19 | public const string KeyReplyToId = "replyToId"; 20 | public const string KeyText = "text"; 21 | public const string KeyTimestamp = "timestamp"; 22 | public const string KeyWatermark = "watermark"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BotDirectLine/BotResponseEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Assets.BotDirectLine 5 | { 6 | public enum EventTypes 7 | { 8 | None, 9 | ConversationStarted, 10 | MessageSent, 11 | MessageReceived, // Can be 1 or more messages 12 | Error 13 | } 14 | 15 | public class BotResponseEventArgs : EventArgs 16 | { 17 | public EventTypes EventType 18 | { 19 | get; 20 | set; 21 | } 22 | 23 | public string SentMessageId 24 | { 25 | get; 26 | set; 27 | } 28 | 29 | public string ConversationId 30 | { 31 | get; 32 | set; 33 | } 34 | 35 | /// 36 | /// Can contain e.g. an error code. 37 | /// 38 | public string Code 39 | { 40 | get; 41 | set; 42 | } 43 | 44 | /// 45 | /// Not an actual message but e.g. an error message. 46 | /// 47 | public string Message 48 | { 49 | get; 50 | set; 51 | } 52 | 53 | public string Watermark 54 | { 55 | get; 56 | set; 57 | } 58 | 59 | public IList Messages 60 | { 61 | get; 62 | private set; 63 | } 64 | 65 | public BotResponseEventArgs() 66 | { 67 | EventType = EventTypes.None; 68 | Messages = new List(); 69 | } 70 | 71 | public override string ToString() 72 | { 73 | string retval = "[Event type: " + EventType; 74 | 75 | if (!string.IsNullOrEmpty(SentMessageId)) 76 | { 77 | retval += "; ID of message sent: " + SentMessageId; 78 | } 79 | 80 | if (!string.IsNullOrEmpty(Code)) 81 | { 82 | retval += "; Code: " + Code; 83 | } 84 | 85 | if (!string.IsNullOrEmpty(Message)) 86 | { 87 | retval += "; Message: " + Message; 88 | } 89 | 90 | if (!string.IsNullOrEmpty(ConversationId)) 91 | { 92 | retval += "; Conversation ID: " + ConversationId; 93 | } 94 | 95 | if (!string.IsNullOrEmpty(Watermark)) 96 | { 97 | retval += "; Watermark: " + Watermark; 98 | } 99 | 100 | if (Messages.Count > 0) 101 | { 102 | retval += "; Messages: "; 103 | 104 | for (int i = 0; i < Messages.Count; ++i) 105 | { 106 | retval += "\"" + Messages[i] + "\""; 107 | 108 | if (i < Messages.Count - 1) 109 | { 110 | retval += ", "; 111 | } 112 | } 113 | } 114 | 115 | retval += "]"; 116 | return retval; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /BotDirectLine/ConversationState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Assets.BotDirectLine 7 | { 8 | public class ConversationState 9 | { 10 | public string ConversationId 11 | { 12 | get; 13 | set; 14 | } 15 | 16 | /// 17 | /// ID of the previously sent message. 18 | /// 19 | public string PreviouslySentMessageId 20 | { 21 | get; 22 | set; 23 | } 24 | 25 | public string Watermark 26 | { 27 | get; 28 | set; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BotDirectLine/MessageActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Assets.BotDirectLine 4 | { 5 | public class MessageActivity 6 | { 7 | public DateTime Timestamp 8 | { 9 | get; 10 | set; 11 | } 12 | 13 | public string Id 14 | { 15 | get; 16 | set; 17 | } 18 | 19 | public string ChannelId 20 | { 21 | get; 22 | set; 23 | } 24 | 25 | public string FromId 26 | { 27 | get; 28 | set; 29 | } 30 | 31 | public string FromName 32 | { 33 | get; 34 | set; 35 | } 36 | 37 | public string ConversationId 38 | { 39 | get; 40 | set; 41 | } 42 | 43 | public string Text 44 | { 45 | get; 46 | set; 47 | } 48 | 49 | public string ReplyToId 50 | { 51 | get; 52 | set; 53 | } 54 | 55 | /// 56 | /// Constructor. 57 | /// 58 | public MessageActivity() 59 | { 60 | } 61 | 62 | /// 63 | /// Constructor. 64 | /// 65 | /// 66 | /// 67 | /// 68 | /// 69 | /// 70 | /// 71 | /// 72 | public MessageActivity(string fromId, string text, string channelId, 73 | string timestampString = null, string fromName = null, string conversationId = null, string replyToId = null) 74 | { 75 | if (string.IsNullOrEmpty(timestampString)) 76 | { 77 | Timestamp = DateTime.Now; 78 | } 79 | else 80 | { 81 | Timestamp = Convert.ToDateTime(timestampString); 82 | } 83 | 84 | ChannelId = channelId; 85 | FromId = fromId; 86 | FromName = fromName; 87 | ConversationId = conversationId; 88 | Text = text; 89 | ReplyToId = replyToId; 90 | } 91 | 92 | public static MessageActivity FromJson(SimpleJSON.JSONNode activityJsonRootNode) 93 | { 94 | MessageActivity messageActivity = new MessageActivity(); 95 | messageActivity.Id = activityJsonRootNode[BotJsonProtocol.KeyId]; 96 | messageActivity.Timestamp = Convert.ToDateTime(activityJsonRootNode[BotJsonProtocol.KeyTimestamp]); 97 | messageActivity.ChannelId = activityJsonRootNode[BotJsonProtocol.KeyChannelId]; 98 | 99 | SimpleJSON.JSONNode fromJsonRootNode = activityJsonRootNode[BotJsonProtocol.KeyFrom]; 100 | 101 | if (fromJsonRootNode != null) 102 | { 103 | messageActivity.FromId = fromJsonRootNode[BotJsonProtocol.KeyId]; 104 | messageActivity.FromName = fromJsonRootNode[BotJsonProtocol.KeyName]; 105 | } 106 | 107 | SimpleJSON.JSONNode conversationJsonRootNode = activityJsonRootNode[BotJsonProtocol.KeyConversation]; 108 | 109 | if (conversationJsonRootNode != null) 110 | { 111 | messageActivity.ConversationId = fromJsonRootNode[BotJsonProtocol.KeyId]; 112 | } 113 | 114 | messageActivity.Text = activityJsonRootNode[BotJsonProtocol.KeyText]; 115 | messageActivity.ReplyToId = activityJsonRootNode[BotJsonProtocol.KeyReplyToId]; 116 | 117 | return messageActivity; 118 | } 119 | 120 | public string ToJsonString() 121 | { 122 | string asJsonString = 123 | "{ \"" + BotJsonProtocol.KeyActivityType + "\": \"" + BotJsonProtocol.KeyMessage + "\", \"" 124 | + BotJsonProtocol.KeyChannelId + "\": \"" + ChannelId + "\", \"" 125 | + BotJsonProtocol.KeyFrom + "\": { \"" 126 | + BotJsonProtocol.KeyId + "\": \"" + FromId 127 | + (string.IsNullOrEmpty(FromName) ? "" : ("\", \"" + BotJsonProtocol.KeyName + "\": \"" + FromName)) 128 | + "\" }, \"" 129 | + BotJsonProtocol.KeyText + "\": \"" + Text + "\" }"; 130 | 131 | return asJsonString; 132 | } 133 | 134 | public override string ToString() 135 | { 136 | return ToJsonString(); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /BotDirectLine/SimpleJSON.cs: -------------------------------------------------------------------------------- 1 | //#define USE_SharpZipLib 2 | #if !UNITY_WEBPLAYER 3 | #define USE_FileIO 4 | #endif 5 | 6 | /* * * * * 7 | * A simple JSON Parser / builder 8 | * ------------------------------ 9 | * 10 | * It mainly has been written as a simple JSON parser. It can build a JSON string 11 | * from the node-tree, or generate a node tree from any valid JSON string. 12 | * 13 | * If you want to use compression when saving to file / stream / B64 you have to include 14 | * SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) in your project and 15 | * define "USE_SharpZipLib" at the top of the file 16 | * 17 | * Written by Bunny83 18 | * 2012-06-09 19 | * 20 | * Features / attributes: 21 | * - provides strongly typed node classes and lists / dictionaries 22 | * - provides easy access to class members / array items / data values 23 | * - the parser ignores data types. Each value is a string. 24 | * - only double quotes (") are used for quoting strings. 25 | * - values and names are not restricted to quoted strings. They simply add up and are trimmed. 26 | * - There are only 3 types: arrays(JSONArray), objects(JSONClass) and values(JSONData) 27 | * - provides "casting" properties to easily convert to / from those types: 28 | * int / float / double / bool 29 | * - provides a common interface for each node so no explicit casting is required. 30 | * - the parser try to avoid errors, but if malformed JSON is parsed the result is undefined 31 | * 32 | * 33 | * 2012-12-17 Update: 34 | * - Added internal JSONLazyCreator class which simplifies the construction of a JSON tree 35 | * Now you can simple reference any item that doesn't exist yet and it will return a JSONLazyCreator 36 | * The class determines the required type by it's further use, creates the type and removes itself. 37 | * - Added binary serialization / deserialization. 38 | * - Added support for BZip2 zipped binary format. Requires the SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) 39 | * The usage of the SharpZipLib library can be disabled by removing or commenting out the USE_SharpZipLib define at the top 40 | * - The serializer uses different types when it comes to store the values. Since my data values 41 | * are all of type string, the serializer will "try" which format fits best. The order is: int, float, double, bool, string. 42 | * It's not the most efficient way but for a moderate amount of data it should work on all platforms. 43 | * 44 | * * * * */ 45 | using System; 46 | using System.Collections; 47 | using System.Collections.Generic; 48 | using System.Linq; 49 | 50 | 51 | namespace SimpleJSON 52 | { 53 | public enum JSONBinaryTag 54 | { 55 | Array = 1, 56 | Class = 2, 57 | Value = 3, 58 | IntValue = 4, 59 | DoubleValue = 5, 60 | BoolValue = 6, 61 | FloatValue = 7, 62 | } 63 | 64 | public class JSONNode 65 | { 66 | #region common interface 67 | public virtual void Add(string aKey, JSONNode aItem){ } 68 | public virtual JSONNode this[int aIndex] { get { return null; } set { } } 69 | public virtual JSONNode this[string aKey] { get { return null; } set { } } 70 | public virtual string Value { get { return ""; } set { } } 71 | public virtual int Count { get { return 0; } } 72 | 73 | public virtual void Add(JSONNode aItem) 74 | { 75 | Add("", aItem); 76 | } 77 | 78 | public virtual JSONNode Remove(string aKey) { return null; } 79 | public virtual JSONNode Remove(int aIndex) { return null; } 80 | public virtual JSONNode Remove(JSONNode aNode) { return aNode; } 81 | 82 | public virtual IEnumerable Childs { get { yield break;} } 83 | public IEnumerable DeepChilds 84 | { 85 | get 86 | { 87 | foreach (var C in Childs) 88 | foreach (var D in C.DeepChilds) 89 | yield return D; 90 | } 91 | } 92 | 93 | public override string ToString() 94 | { 95 | return "JSONNode"; 96 | } 97 | public virtual string ToString(string aPrefix) 98 | { 99 | return "JSONNode"; 100 | } 101 | 102 | #endregion common interface 103 | 104 | #region typecasting properties 105 | public virtual int AsInt 106 | { 107 | get 108 | { 109 | int v = 0; 110 | if (int.TryParse(Value,out v)) 111 | return v; 112 | return 0; 113 | } 114 | set 115 | { 116 | Value = value.ToString(); 117 | } 118 | } 119 | public virtual float AsFloat 120 | { 121 | get 122 | { 123 | float v = 0.0f; 124 | if (float.TryParse(Value,out v)) 125 | return v; 126 | return 0.0f; 127 | } 128 | set 129 | { 130 | Value = value.ToString(); 131 | } 132 | } 133 | public virtual double AsDouble 134 | { 135 | get 136 | { 137 | double v = 0.0; 138 | if (double.TryParse(Value,out v)) 139 | return v; 140 | return 0.0; 141 | } 142 | set 143 | { 144 | Value = value.ToString(); 145 | } 146 | } 147 | public virtual bool AsBool 148 | { 149 | get 150 | { 151 | bool v = false; 152 | if (bool.TryParse(Value,out v)) 153 | return v; 154 | return !string.IsNullOrEmpty(Value); 155 | } 156 | set 157 | { 158 | Value = (value)?"true":"false"; 159 | } 160 | } 161 | public virtual JSONArray AsArray 162 | { 163 | get 164 | { 165 | return this as JSONArray; 166 | } 167 | } 168 | public virtual JSONClass AsObject 169 | { 170 | get 171 | { 172 | return this as JSONClass; 173 | } 174 | } 175 | 176 | 177 | #endregion typecasting properties 178 | 179 | #region operators 180 | public static implicit operator JSONNode(string s) 181 | { 182 | return new JSONData(s); 183 | } 184 | public static implicit operator string(JSONNode d) 185 | { 186 | return (d == null)?null:d.Value; 187 | } 188 | public static bool operator ==(JSONNode a, object b) 189 | { 190 | if (b == null && a is JSONLazyCreator) 191 | return true; 192 | return System.Object.ReferenceEquals(a,b); 193 | } 194 | 195 | public static bool operator !=(JSONNode a, object b) 196 | { 197 | return !(a == b); 198 | } 199 | public override bool Equals (object obj) 200 | { 201 | return System.Object.ReferenceEquals(this, obj); 202 | } 203 | public override int GetHashCode () 204 | { 205 | return base.GetHashCode(); 206 | } 207 | 208 | 209 | #endregion operators 210 | 211 | internal static string Escape(string aText) 212 | { 213 | string result = ""; 214 | foreach(char c in aText) 215 | { 216 | switch(c) 217 | { 218 | case '\\' : result += "\\\\"; break; 219 | case '\"' : result += "\\\""; break; 220 | case '\n' : result += "\\n" ; break; 221 | case '\r' : result += "\\r" ; break; 222 | case '\t' : result += "\\t" ; break; 223 | case '\b' : result += "\\b" ; break; 224 | case '\f' : result += "\\f" ; break; 225 | default : result += c ; break; 226 | } 227 | } 228 | return result; 229 | } 230 | 231 | public static JSONNode Parse(string aJSON) 232 | { 233 | Stack stack = new Stack(); 234 | JSONNode ctx = null; 235 | int i = 0; 236 | string Token = ""; 237 | string TokenName = ""; 238 | bool QuoteMode = false; 239 | while (i < aJSON.Length) 240 | { 241 | switch (aJSON[i]) 242 | { 243 | case '{': 244 | if (QuoteMode) 245 | { 246 | Token += aJSON[i]; 247 | break; 248 | } 249 | stack.Push(new JSONClass()); 250 | if (ctx != null) 251 | { 252 | TokenName = TokenName.Trim(); 253 | if (ctx is JSONArray) 254 | ctx.Add(stack.Peek()); 255 | else if (TokenName != "") 256 | ctx.Add(TokenName,stack.Peek()); 257 | } 258 | TokenName = ""; 259 | Token = ""; 260 | ctx = stack.Peek(); 261 | break; 262 | 263 | case '[': 264 | if (QuoteMode) 265 | { 266 | Token += aJSON[i]; 267 | break; 268 | } 269 | 270 | stack.Push(new JSONArray()); 271 | if (ctx != null) 272 | { 273 | TokenName = TokenName.Trim(); 274 | if (ctx is JSONArray) 275 | ctx.Add(stack.Peek()); 276 | else if (TokenName != "") 277 | ctx.Add(TokenName,stack.Peek()); 278 | } 279 | TokenName = ""; 280 | Token = ""; 281 | ctx = stack.Peek(); 282 | break; 283 | 284 | case '}': 285 | case ']': 286 | if (QuoteMode) 287 | { 288 | Token += aJSON[i]; 289 | break; 290 | } 291 | if (stack.Count == 0) 292 | throw new Exception("JSON Parse: Too many closing brackets"); 293 | 294 | stack.Pop(); 295 | if (Token != "") 296 | { 297 | TokenName = TokenName.Trim(); 298 | if (ctx is JSONArray) 299 | ctx.Add(Token); 300 | else if (TokenName != "") 301 | ctx.Add(TokenName,Token); 302 | } 303 | TokenName = ""; 304 | Token = ""; 305 | if (stack.Count>0) 306 | ctx = stack.Peek(); 307 | break; 308 | 309 | case ':': 310 | if (QuoteMode) 311 | { 312 | Token += aJSON[i]; 313 | break; 314 | } 315 | TokenName = Token; 316 | Token = ""; 317 | break; 318 | 319 | case '"': 320 | QuoteMode ^= true; 321 | break; 322 | 323 | case ',': 324 | if (QuoteMode) 325 | { 326 | Token += aJSON[i]; 327 | break; 328 | } 329 | if (Token != "") 330 | { 331 | if (ctx is JSONArray) 332 | ctx.Add(Token); 333 | else if (TokenName != "") 334 | ctx.Add(TokenName, Token); 335 | } 336 | TokenName = ""; 337 | Token = ""; 338 | break; 339 | 340 | case '\r': 341 | case '\n': 342 | break; 343 | 344 | case ' ': 345 | case '\t': 346 | if (QuoteMode) 347 | Token += aJSON[i]; 348 | break; 349 | 350 | case '\\': 351 | ++i; 352 | if (QuoteMode) 353 | { 354 | char C = aJSON[i]; 355 | switch (C) 356 | { 357 | case 't' : Token += '\t'; break; 358 | case 'r' : Token += '\r'; break; 359 | case 'n' : Token += '\n'; break; 360 | case 'b' : Token += '\b'; break; 361 | case 'f' : Token += '\f'; break; 362 | case 'u': 363 | { 364 | string s = aJSON.Substring(i+1,4); 365 | Token += (char)int.Parse(s, System.Globalization.NumberStyles.AllowHexSpecifier); 366 | i += 4; 367 | break; 368 | } 369 | default : Token += C; break; 370 | } 371 | } 372 | break; 373 | 374 | default: 375 | Token += aJSON[i]; 376 | break; 377 | } 378 | ++i; 379 | } 380 | if (QuoteMode) 381 | { 382 | throw new Exception("JSON Parse: Quotation marks seems to be messed up."); 383 | } 384 | return ctx; 385 | } 386 | 387 | public virtual void Serialize(System.IO.BinaryWriter aWriter) {} 388 | 389 | public void SaveToStream(System.IO.Stream aData) 390 | { 391 | var W = new System.IO.BinaryWriter(aData); 392 | Serialize(W); 393 | } 394 | 395 | #if USE_SharpZipLib 396 | public void SaveToCompressedStream(System.IO.Stream aData) 397 | { 398 | using (var gzipOut = new ICSharpCode.SharpZipLib.BZip2.BZip2OutputStream(aData)) 399 | { 400 | gzipOut.IsStreamOwner = false; 401 | SaveToStream(gzipOut); 402 | gzipOut.Close(); 403 | } 404 | } 405 | 406 | public void SaveToCompressedFile(string aFileName) 407 | { 408 | #if USE_FileIO 409 | System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName); 410 | using(var F = System.IO.File.OpenWrite(aFileName)) 411 | { 412 | SaveToCompressedStream(F); 413 | } 414 | #else 415 | throw new Exception("Can't use File IO stuff in webplayer"); 416 | #endif 417 | } 418 | public string SaveToCompressedBase64() 419 | { 420 | using (var stream = new System.IO.MemoryStream()) 421 | { 422 | SaveToCompressedStream(stream); 423 | stream.Position = 0; 424 | return System.Convert.ToBase64String(stream.ToArray()); 425 | } 426 | } 427 | 428 | #else 429 | public void SaveToCompressedStream(System.IO.Stream aData) 430 | { 431 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 432 | } 433 | public void SaveToCompressedFile(string aFileName) 434 | { 435 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 436 | } 437 | public string SaveToCompressedBase64() 438 | { 439 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 440 | } 441 | #endif 442 | 443 | public void SaveToFile(string aFileName) 444 | { 445 | #if USE_FileIO 446 | System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName); 447 | using(var F = System.IO.File.OpenWrite(aFileName)) 448 | { 449 | SaveToStream(F); 450 | } 451 | #else 452 | throw new Exception("Can't use File IO stuff in webplayer"); 453 | #endif 454 | } 455 | public string SaveToBase64() 456 | { 457 | using (var stream = new System.IO.MemoryStream()) 458 | { 459 | SaveToStream(stream); 460 | stream.Position = 0; 461 | return System.Convert.ToBase64String(stream.ToArray()); 462 | } 463 | } 464 | public static JSONNode Deserialize(System.IO.BinaryReader aReader) 465 | { 466 | JSONBinaryTag type = (JSONBinaryTag)aReader.ReadByte(); 467 | switch(type) 468 | { 469 | case JSONBinaryTag.Array: 470 | { 471 | int count = aReader.ReadInt32(); 472 | JSONArray tmp = new JSONArray(); 473 | for(int i = 0; i < count; i++) 474 | tmp.Add(Deserialize(aReader)); 475 | return tmp; 476 | } 477 | case JSONBinaryTag.Class: 478 | { 479 | int count = aReader.ReadInt32(); 480 | JSONClass tmp = new JSONClass(); 481 | for(int i = 0; i < count; i++) 482 | { 483 | string key = aReader.ReadString(); 484 | var val = Deserialize(aReader); 485 | tmp.Add(key, val); 486 | } 487 | return tmp; 488 | } 489 | case JSONBinaryTag.Value: 490 | { 491 | return new JSONData(aReader.ReadString()); 492 | } 493 | case JSONBinaryTag.IntValue: 494 | { 495 | return new JSONData(aReader.ReadInt32()); 496 | } 497 | case JSONBinaryTag.DoubleValue: 498 | { 499 | return new JSONData(aReader.ReadDouble()); 500 | } 501 | case JSONBinaryTag.BoolValue: 502 | { 503 | return new JSONData(aReader.ReadBoolean()); 504 | } 505 | case JSONBinaryTag.FloatValue: 506 | { 507 | return new JSONData(aReader.ReadSingle()); 508 | } 509 | 510 | default: 511 | { 512 | throw new Exception("Error deserializing JSON. Unknown tag: " + type); 513 | } 514 | } 515 | } 516 | 517 | #if USE_SharpZipLib 518 | public static JSONNode LoadFromCompressedStream(System.IO.Stream aData) 519 | { 520 | var zin = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(aData); 521 | return LoadFromStream(zin); 522 | } 523 | public static JSONNode LoadFromCompressedFile(string aFileName) 524 | { 525 | #if USE_FileIO 526 | using(var F = System.IO.File.OpenRead(aFileName)) 527 | { 528 | return LoadFromCompressedStream(F); 529 | } 530 | #else 531 | throw new Exception("Can't use File IO stuff in webplayer"); 532 | #endif 533 | } 534 | public static JSONNode LoadFromCompressedBase64(string aBase64) 535 | { 536 | var tmp = System.Convert.FromBase64String(aBase64); 537 | var stream = new System.IO.MemoryStream(tmp); 538 | stream.Position = 0; 539 | return LoadFromCompressedStream(stream); 540 | } 541 | #else 542 | public static JSONNode LoadFromCompressedFile(string aFileName) 543 | { 544 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 545 | } 546 | public static JSONNode LoadFromCompressedStream(System.IO.Stream aData) 547 | { 548 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 549 | } 550 | public static JSONNode LoadFromCompressedBase64(string aBase64) 551 | { 552 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); 553 | } 554 | #endif 555 | 556 | public static JSONNode LoadFromStream(System.IO.Stream aData) 557 | { 558 | using(var R = new System.IO.BinaryReader(aData)) 559 | { 560 | return Deserialize(R); 561 | } 562 | } 563 | public static JSONNode LoadFromFile(string aFileName) 564 | { 565 | #if USE_FileIO 566 | using(var F = System.IO.File.OpenRead(aFileName)) 567 | { 568 | return LoadFromStream(F); 569 | } 570 | #else 571 | throw new Exception("Can't use File IO stuff in webplayer"); 572 | #endif 573 | } 574 | public static JSONNode LoadFromBase64(string aBase64) 575 | { 576 | var tmp = System.Convert.FromBase64String(aBase64); 577 | var stream = new System.IO.MemoryStream(tmp); 578 | stream.Position = 0; 579 | return LoadFromStream(stream); 580 | } 581 | } // End of JSONNode 582 | 583 | public class JSONArray : JSONNode, IEnumerable 584 | { 585 | private List m_List = new List(); 586 | public override JSONNode this[int aIndex] 587 | { 588 | get 589 | { 590 | if (aIndex<0 || aIndex >= m_List.Count) 591 | return new JSONLazyCreator(this); 592 | return m_List[aIndex]; 593 | } 594 | set 595 | { 596 | if (aIndex<0 || aIndex >= m_List.Count) 597 | m_List.Add(value); 598 | else 599 | m_List[aIndex] = value; 600 | } 601 | } 602 | public override JSONNode this[string aKey] 603 | { 604 | get{ return new JSONLazyCreator(this);} 605 | set{ m_List.Add(value); } 606 | } 607 | public override int Count 608 | { 609 | get { return m_List.Count; } 610 | } 611 | public override void Add(string aKey, JSONNode aItem) 612 | { 613 | m_List.Add(aItem); 614 | } 615 | public override JSONNode Remove(int aIndex) 616 | { 617 | if (aIndex < 0 || aIndex >= m_List.Count) 618 | return null; 619 | JSONNode tmp = m_List[aIndex]; 620 | m_List.RemoveAt(aIndex); 621 | return tmp; 622 | } 623 | public override JSONNode Remove(JSONNode aNode) 624 | { 625 | m_List.Remove(aNode); 626 | return aNode; 627 | } 628 | public override IEnumerable Childs 629 | { 630 | get 631 | { 632 | foreach(JSONNode N in m_List) 633 | yield return N; 634 | } 635 | } 636 | public IEnumerator GetEnumerator() 637 | { 638 | foreach(JSONNode N in m_List) 639 | yield return N; 640 | } 641 | public override string ToString() 642 | { 643 | string result = "[ "; 644 | foreach (JSONNode N in m_List) 645 | { 646 | if (result.Length > 2) 647 | result += ", "; 648 | result += N.ToString(); 649 | } 650 | result += " ]"; 651 | return result; 652 | } 653 | public override string ToString(string aPrefix) 654 | { 655 | string result = "[ "; 656 | foreach (JSONNode N in m_List) 657 | { 658 | if (result.Length > 3) 659 | result += ", "; 660 | result += "\n" + aPrefix + " "; 661 | result += N.ToString(aPrefix+" "); 662 | } 663 | result += "\n" + aPrefix + "]"; 664 | return result; 665 | } 666 | public override void Serialize (System.IO.BinaryWriter aWriter) 667 | { 668 | aWriter.Write((byte)JSONBinaryTag.Array); 669 | aWriter.Write(m_List.Count); 670 | for(int i = 0; i < m_List.Count; i++) 671 | { 672 | m_List[i].Serialize(aWriter); 673 | } 674 | } 675 | } // End of JSONArray 676 | 677 | public class JSONClass : JSONNode, IEnumerable 678 | { 679 | private Dictionary m_Dict = new Dictionary(); 680 | public override JSONNode this[string aKey] 681 | { 682 | get 683 | { 684 | if (m_Dict.ContainsKey(aKey)) 685 | return m_Dict[aKey]; 686 | else 687 | return new JSONLazyCreator(this, aKey); 688 | } 689 | set 690 | { 691 | if (m_Dict.ContainsKey(aKey)) 692 | m_Dict[aKey] = value; 693 | else 694 | m_Dict.Add(aKey,value); 695 | } 696 | } 697 | public override JSONNode this[int aIndex] 698 | { 699 | get 700 | { 701 | if (aIndex < 0 || aIndex >= m_Dict.Count) 702 | return null; 703 | return m_Dict.ElementAt(aIndex).Value; 704 | } 705 | set 706 | { 707 | if (aIndex < 0 || aIndex >= m_Dict.Count) 708 | return; 709 | string key = m_Dict.ElementAt(aIndex).Key; 710 | m_Dict[key] = value; 711 | } 712 | } 713 | public override int Count 714 | { 715 | get { return m_Dict.Count; } 716 | } 717 | 718 | 719 | public override void Add(string aKey, JSONNode aItem) 720 | { 721 | if (!string.IsNullOrEmpty(aKey)) 722 | { 723 | if (m_Dict.ContainsKey(aKey)) 724 | m_Dict[aKey] = aItem; 725 | else 726 | m_Dict.Add(aKey, aItem); 727 | } 728 | else 729 | m_Dict.Add(Guid.NewGuid().ToString(), aItem); 730 | } 731 | 732 | public override JSONNode Remove(string aKey) 733 | { 734 | if (!m_Dict.ContainsKey(aKey)) 735 | return null; 736 | JSONNode tmp = m_Dict[aKey]; 737 | m_Dict.Remove(aKey); 738 | return tmp; 739 | } 740 | public override JSONNode Remove(int aIndex) 741 | { 742 | if (aIndex < 0 || aIndex >= m_Dict.Count) 743 | return null; 744 | var item = m_Dict.ElementAt(aIndex); 745 | m_Dict.Remove(item.Key); 746 | return item.Value; 747 | } 748 | public override JSONNode Remove(JSONNode aNode) 749 | { 750 | try 751 | { 752 | var item = m_Dict.Where(k => k.Value == aNode).First(); 753 | m_Dict.Remove(item.Key); 754 | return aNode; 755 | } 756 | catch 757 | { 758 | return null; 759 | } 760 | } 761 | 762 | public override IEnumerable Childs 763 | { 764 | get 765 | { 766 | foreach(KeyValuePair N in m_Dict) 767 | yield return N.Value; 768 | } 769 | } 770 | 771 | public IEnumerator GetEnumerator() 772 | { 773 | foreach(KeyValuePair N in m_Dict) 774 | yield return N; 775 | } 776 | public override string ToString() 777 | { 778 | string result = "{"; 779 | foreach (KeyValuePair N in m_Dict) 780 | { 781 | if (result.Length > 2) 782 | result += ", "; 783 | result += "\"" + Escape(N.Key) + "\":" + N.Value.ToString(); 784 | } 785 | result += "}"; 786 | return result; 787 | } 788 | public override string ToString(string aPrefix) 789 | { 790 | string result = "{ "; 791 | foreach (KeyValuePair N in m_Dict) 792 | { 793 | if (result.Length > 3) 794 | result += ", "; 795 | result += "\n" + aPrefix + " "; 796 | result += "\"" + Escape(N.Key) + "\" : " + N.Value.ToString(aPrefix+" "); 797 | } 798 | result += "\n" + aPrefix + "}"; 799 | return result; 800 | } 801 | public override void Serialize (System.IO.BinaryWriter aWriter) 802 | { 803 | aWriter.Write((byte)JSONBinaryTag.Class); 804 | aWriter.Write(m_Dict.Count); 805 | foreach(string K in m_Dict.Keys) 806 | { 807 | aWriter.Write(K); 808 | m_Dict[K].Serialize(aWriter); 809 | } 810 | } 811 | } // End of JSONClass 812 | 813 | public class JSONData : JSONNode 814 | { 815 | private string m_Data; 816 | public override string Value 817 | { 818 | get { return m_Data; } 819 | set { m_Data = value; } 820 | } 821 | public JSONData(string aData) 822 | { 823 | m_Data = aData; 824 | } 825 | public JSONData(float aData) 826 | { 827 | AsFloat = aData; 828 | } 829 | public JSONData(double aData) 830 | { 831 | AsDouble = aData; 832 | } 833 | public JSONData(bool aData) 834 | { 835 | AsBool = aData; 836 | } 837 | public JSONData(int aData) 838 | { 839 | AsInt = aData; 840 | } 841 | 842 | public override string ToString() 843 | { 844 | return "\"" + Escape(m_Data) + "\""; 845 | } 846 | public override string ToString(string aPrefix) 847 | { 848 | return "\"" + Escape(m_Data) + "\""; 849 | } 850 | public override void Serialize (System.IO.BinaryWriter aWriter) 851 | { 852 | var tmp = new JSONData(""); 853 | 854 | tmp.AsInt = AsInt; 855 | if (tmp.m_Data == this.m_Data) 856 | { 857 | aWriter.Write((byte)JSONBinaryTag.IntValue); 858 | aWriter.Write(AsInt); 859 | return; 860 | } 861 | tmp.AsFloat = AsFloat; 862 | if (tmp.m_Data == this.m_Data) 863 | { 864 | aWriter.Write((byte)JSONBinaryTag.FloatValue); 865 | aWriter.Write(AsFloat); 866 | return; 867 | } 868 | tmp.AsDouble = AsDouble; 869 | if (tmp.m_Data == this.m_Data) 870 | { 871 | aWriter.Write((byte)JSONBinaryTag.DoubleValue); 872 | aWriter.Write(AsDouble); 873 | return; 874 | } 875 | 876 | tmp.AsBool = AsBool; 877 | if (tmp.m_Data == this.m_Data) 878 | { 879 | aWriter.Write((byte)JSONBinaryTag.BoolValue); 880 | aWriter.Write(AsBool); 881 | return; 882 | } 883 | aWriter.Write((byte)JSONBinaryTag.Value); 884 | aWriter.Write(m_Data); 885 | } 886 | } // End of JSONData 887 | 888 | internal class JSONLazyCreator : JSONNode 889 | { 890 | private JSONNode m_Node = null; 891 | private string m_Key = null; 892 | 893 | public JSONLazyCreator(JSONNode aNode) 894 | { 895 | m_Node = aNode; 896 | m_Key = null; 897 | } 898 | public JSONLazyCreator(JSONNode aNode, string aKey) 899 | { 900 | m_Node = aNode; 901 | m_Key = aKey; 902 | } 903 | 904 | private void Set(JSONNode aVal) 905 | { 906 | if (m_Key == null) 907 | { 908 | m_Node.Add(aVal); 909 | } 910 | else 911 | { 912 | m_Node.Add(m_Key, aVal); 913 | } 914 | m_Node = null; // Be GC friendly. 915 | } 916 | 917 | public override JSONNode this[int aIndex] 918 | { 919 | get 920 | { 921 | return new JSONLazyCreator(this); 922 | } 923 | set 924 | { 925 | var tmp = new JSONArray(); 926 | tmp.Add(value); 927 | Set(tmp); 928 | } 929 | } 930 | 931 | public override JSONNode this[string aKey] 932 | { 933 | get 934 | { 935 | return new JSONLazyCreator(this, aKey); 936 | } 937 | set 938 | { 939 | var tmp = new JSONClass(); 940 | tmp.Add(aKey, value); 941 | Set(tmp); 942 | } 943 | } 944 | public override void Add (JSONNode aItem) 945 | { 946 | var tmp = new JSONArray(); 947 | tmp.Add(aItem); 948 | Set(tmp); 949 | } 950 | public override void Add (string aKey, JSONNode aItem) 951 | { 952 | var tmp = new JSONClass(); 953 | tmp.Add(aKey, aItem); 954 | Set(tmp); 955 | } 956 | public static bool operator ==(JSONLazyCreator a, object b) 957 | { 958 | if (b == null) 959 | return true; 960 | return System.Object.ReferenceEquals(a,b); 961 | } 962 | 963 | public static bool operator !=(JSONLazyCreator a, object b) 964 | { 965 | return !(a == b); 966 | } 967 | public override bool Equals (object obj) 968 | { 969 | if (obj == null) 970 | return true; 971 | return System.Object.ReferenceEquals(this, obj); 972 | } 973 | public override int GetHashCode () 974 | { 975 | return base.GetHashCode(); 976 | } 977 | 978 | public override string ToString() 979 | { 980 | return ""; 981 | } 982 | public override string ToString(string aPrefix) 983 | { 984 | return ""; 985 | } 986 | 987 | public override int AsInt 988 | { 989 | get 990 | { 991 | JSONData tmp = new JSONData(0); 992 | Set(tmp); 993 | return 0; 994 | } 995 | set 996 | { 997 | JSONData tmp = new JSONData(value); 998 | Set(tmp); 999 | } 1000 | } 1001 | public override float AsFloat 1002 | { 1003 | get 1004 | { 1005 | JSONData tmp = new JSONData(0.0f); 1006 | Set(tmp); 1007 | return 0.0f; 1008 | } 1009 | set 1010 | { 1011 | JSONData tmp = new JSONData(value); 1012 | Set(tmp); 1013 | } 1014 | } 1015 | public override double AsDouble 1016 | { 1017 | get 1018 | { 1019 | JSONData tmp = new JSONData(0.0); 1020 | Set(tmp); 1021 | return 0.0; 1022 | } 1023 | set 1024 | { 1025 | JSONData tmp = new JSONData(value); 1026 | Set(tmp); 1027 | } 1028 | } 1029 | public override bool AsBool 1030 | { 1031 | get 1032 | { 1033 | JSONData tmp = new JSONData(false); 1034 | Set(tmp); 1035 | return false; 1036 | } 1037 | set 1038 | { 1039 | JSONData tmp = new JSONData(value); 1040 | Set(tmp); 1041 | } 1042 | } 1043 | public override JSONArray AsArray 1044 | { 1045 | get 1046 | { 1047 | JSONArray tmp = new JSONArray(); 1048 | Set(tmp); 1049 | return tmp; 1050 | } 1051 | } 1052 | public override JSONClass AsObject 1053 | { 1054 | get 1055 | { 1056 | JSONClass tmp = new JSONClass(); 1057 | Set(tmp); 1058 | return tmp; 1059 | } 1060 | } 1061 | } // End of JSONLazyCreator 1062 | 1063 | public static class JSON 1064 | { 1065 | public static JSONNode Parse(string aJSON) 1066 | { 1067 | return JSONNode.Parse(aJSON); 1068 | } 1069 | } 1070 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The following license applies to all code in this project except SimpleJSON.cs file: 2 | 3 | MIT License 4 | 5 | Copyright (c) 2016 Tomi Paananen 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | --- 26 | 27 | The license for the code in SimpleJSON.cs file: 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2012-2015 Markus Göbel 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Direct Line implementation for Unity # 2 | 3 | This is a simple [Microsoft Bot Framework](https://dev.botframework.com/) 4 | [Direct Line](https://docs.botframework.com/en-us/restapi/directline/) 5 | implementation for Unity. The current implementation enables: 6 | 7 | * Starting a new conversation with the bot 8 | * Sending and receiving simple messages 9 | 10 | ## Usage ## 11 | 12 | 1. First simply copy the content to the assets of your Unity app. 13 | 2. Initialize [the BotDirectLineManager class](/BotDirectLine/BotDirectLineManager.cs) 14 | with your secret key (this will bind the manager to your bot) and start 15 | listening to bot response events: 16 | 17 | ```csharp 18 | BotDirectLineManager.Initialize("INSERT YOUR BOT'S SECRET KEY HERE"); 19 | BotDirectLineManager.Instance.BotResponse += OnBotResponse; 20 | ``` 21 | 22 | 3. Start a new conversation (`OnBotResponse` will be called when the new 23 | conversation is started): 24 | 25 | ```csharp 26 | StartCoroutine(BotDirectLineManager.Instance.StartConversationCoroutine()); 27 | ``` 28 | 29 | 4. To send a message: 30 | 31 | ```csharp 32 | StartCoroutine(BotDirectLineManager.Instance.SendMessageCoroutine( 33 | _conversationState.ConversationId, "UnityUserId", "Hello bot!", "Unity User 1")); 34 | ``` 35 | 36 | Note that there are no return values, but your `BotResponse` event handler will 37 | be called for all results. Here's an example for the event handler 38 | implementation: 39 | 40 | ```csharp 41 | private void OnBotResponse(object sender, Assets.BotDirectLine.BotResponseEventArgs e) 42 | { 43 | Debug.Log("OnBotResponse: " + e.ToString()); 44 | 45 | switch (e.EventType) 46 | { 47 | case EventTypes.ConversationStarted: 48 | // Store the ID 49 | _conversationState.ConversationId = e.ConversationId; 50 | break; 51 | case EventTypes.MessageSent: 52 | if (!string.IsNullOrEmpty(_conversationState.ConversationId)) 53 | { 54 | // Get the bot's response(s) 55 | StartCoroutine(BotDirectLineManager.Instance.GetMessagesCoroutine(_conversationState.ConversationId)); 56 | } 57 | 58 | break; 59 | case EventTypes.MessageReceived: 60 | // Handle the received message(s) 61 | break; 62 | case EventTypes.Error: 63 | // Handle the error 64 | break; 65 | } 66 | } 67 | ``` 68 | 69 | 70 | ## Acknowledgements ## 71 | 72 | The implementation uses [SimpleJSON](http://wiki.unity3d.com/index.php/SimpleJSON) 73 | for parsing JSON. The original version of 74 | [the SimpleJSON project](https://github.com/Bunny83/SimpleJSON) is licensed 75 | under MIT 76 | (see [the license file](https://github.com/Bunny83/SimpleJSON/blob/master/LICENSE)) 77 | and was created by [Markus Göbel](https://github.com/Bunny83). 78 | --------------------------------------------------------------------------------