diff --git a/interop.sln b/interop.sln
new file mode 100644
index 0000000..48e856b
--- /dev/null
+++ b/interop.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.6
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "interop", "interop\interop.csproj", "{E67C0218-5911-4ACC-8449-E4A791200B78}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E67C0218-5911-4ACC-8449-E4A791200B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E67C0218-5911-4ACC-8449-E4A791200B78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E67C0218-5911-4ACC-8449-E4A791200B78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E67C0218-5911-4ACC-8449-E4A791200B78}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/interop/App.config b/interop/App.config
new file mode 100644
index 0000000..4543795
--- /dev/null
+++ b/interop/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/interop/Program.cs b/interop/Program.cs
new file mode 100644
index 0000000..9bcae44
--- /dev/null
+++ b/interop/Program.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Timers;
+using WebSocketSharp;
+using Newtonsoft.Json;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+
+namespace interop {
+ class Program {
+ const Int32 SW_MINIMIZE = 6;
+
+ [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
+ private static extern IntPtr GetConsoleWindow();
+
+ [DllImport("User32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool ShowWindow([In] IntPtr hWnd, [In] Int32 nCmdShow);
+
+ private static int UserId = -1;
+ private static object IOLock = new object();
+ private static List Logs = new List();
+ private static Dictionary Users = new Dictionary();
+ private static WebSocket Sock;
+ private static bool Shown = true;
+ private static Timer pingTimer;
+
+ private static Dictionary BotDefs = new Dictionary();
+ private static Dictionary BotErrorDefs = new Dictionary();
+
+ private static string Username;
+ private static string Password;
+
+ static void MoveCursor(int x, int y) {
+ Console.CursorLeft = x;
+ Console.CursorTop = y + (Shown ? 0 : Console.WindowHeight);
+ }
+
+ static string Pack(int id, params string[] data) {
+ var newData = data.ToList();
+ newData.Insert(0, id.ToString());
+ return String.Join("\t", newData);
+ }
+
+ static Packet Unpack(string data) {
+ var parts = data.Split('\t');
+ return new Packet {
+ Id = Int32.Parse(parts[0]),
+ Data = parts.Skip(1).ToList()
+ };
+ }
+
+ static string ShortenName(string name) {
+ return name.Length < 8 ? name.PadLeft(8, ' ') : name.Substring(0, 8);
+ }
+
+ static int GetLineCount(Message message) {
+ var lines = message.Contents.Split(new[] { "\r\n" }, StringSplitOptions.None);
+ int lineCount = 0;
+ for(int i = 0; i < lines.Length; ++i) {
+ int lineLength = lines[i].Length + (i == 0 ? 15 : 0);
+ lineCount += (int)Math.Ceiling((double)lineLength / (double)Console.WindowWidth);
+ }
+
+ return lineCount + (lines.Length > 1 ? 1 : 0);
+ }
+
+ static void CorrectMessage(ref Message message) {
+ message.Sender = ShortenName(message.Sender);
+ message.Contents = Regex.Replace(message.Contents, @"\[.*?\]", "");
+ message.Contents = message.Contents.Replace("\r", "");
+ message.Contents = message.Contents.Replace("
", "\r\n");
+ message.Contents = message.Contents.Replace("<", "<");
+ message.Contents = message.Contents.Replace(">", ">");
+ }
+
+ static void LoadLanguageFile(string raw) {
+ var data = JsonConvert.DeserializeObject>>(raw);
+
+ foreach(var pair in data["botText"])
+ BotDefs.Add(pair.Key, pair.Value);
+ foreach(var pair in data["botErrText"])
+ BotErrorDefs.Add(pair.Key, pair.Value);
+ }
+
+ static string ParseBotMessage(string raw) {
+ var parts = raw.Split('\f');
+
+ bool isErr = parts[0] == "1";
+ var outMsg = raw;
+ if((isErr ? BotErrorDefs : BotDefs).ContainsKey(parts[1])) {
+ outMsg = (isErr ? BotErrorDefs : BotDefs)[parts[1]];
+ for(int i = 2; i < parts.Length; ++i)
+ outMsg = outMsg.Replace("{"+ (i - 2) +"}", parts[i]);
+ }
+
+ return outMsg;
+ }
+
+ static void WriteMessage(Message message) {
+ CorrectMessage(ref message);
+
+ lock(IOLock) {
+ int cx = Console.CursorLeft, cy = Console.CursorTop;
+
+ var lineCount = GetLineCount(message);
+ if(lineCount > Console.WindowHeight - 3)
+ message.Contents = "This message was too long to show.";
+
+ if(Shown)
+ Console.MoveBufferArea(0, lineCount, Console.WindowWidth, Console.WindowHeight - lineCount - 3, 0, 0);
+ else
+ Console.MoveBufferArea(0, Console.WindowHeight + lineCount, Console.WindowWidth, Console.WindowHeight - lineCount - 3, 0, Console.WindowHeight);
+ MoveCursor(0, Console.WindowHeight - 3 - lineCount);
+
+ Console.Write(message.Sent.ToString("HHmm"));
+ Console.Write(" ");
+ Console.Write(message.Sender);
+ Console.Write(": ");
+ Console.Write(message.Contents);
+
+ Console.CursorLeft = cx;
+ Console.CursorTop = cy;
+ }
+ }
+
+ static string HashToString(byte[] hash) {
+ var sb = new StringBuilder(hash.Length * 2);
+ foreach(var b in hash)
+ sb.Append(b.ToString("x2"));
+
+ return sb.ToString();
+ }
+
+ static void Main(string[] args) {
+ Console.Write("Enter username: ");
+ Username = Console.ReadLine();
+
+ using(var sha = new SHA1Managed()) {
+ Console.Write("Enter password: ");
+ Password = HashToString(sha.ComputeHash(Encoding.UTF8.GetBytes(HashToString(sha.ComputeHash(Encoding.UTF8.GetBytes(Console.ReadLine()))))));
+ }
+
+ LoadLanguageFile(Properties.Resources.CommonLang);
+ LoadLanguageFile(Properties.Resources.CoreLang);
+
+ Console.CursorLeft = 0;
+ Console.CursorTop = Console.WindowHeight - 3;
+ Console.Write(new String('~', Console.WindowWidth));
+ Console.Write("> ");
+
+ Console.SetBufferSize(Console.WindowWidth, Console.WindowHeight * 2);
+ using(Sock = new WebSocket(Properties.Resources.ConnectionAddress)) {
+ Users.Add(-1, "ChatBot");
+ Sock.OnOpen += OnOpen;
+ Sock.OnMessage += OnMessage;
+ Sock.OnClose += Sock_OnClose;
+ Sock.Connect();
+
+ string message = "";
+ while(true) {
+ var key = Console.ReadKey(true);
+
+ lock(IOLock) {
+ switch(key.KeyChar) {
+ case '\x08':
+ if(!Shown)
+ break;
+ if(message.Length == 0)
+ break;
+
+ message = message.Substring(0, message.Length - 1);
+ if(Console.CursorLeft == 0) {
+ Console.CursorTop--;
+ Console.CursorLeft = Console.WindowWidth;
+ Console.Write(' ');
+
+ Console.CursorTop--;
+ Console.CursorLeft = Console.WindowWidth;
+ } else {
+ Console.CursorLeft--;
+ Console.Write(' ');
+ Console.CursorLeft--;
+ }
+ break;
+ case '\x0A':
+ case '\x0D':
+ if(!Shown)
+ break;
+
+ Console.CursorTop = Console.WindowHeight - 2;
+ Console.CursorLeft = 0;
+
+ Console.Write(">");
+ Console.Write(new String(' ', Console.WindowWidth * 2 - 5));
+ Console.CursorTop = Console.WindowHeight - 2;
+ Console.CursorLeft = 2;
+
+ //message = message.Trim() + " de geso~";
+ Sock.Send(Pack(2, UserId.ToString(), message));
+ message = "";
+ break;
+ case '\x1B':
+ Sock.Send(Pack(2, UserId.ToString(), "brb (auto)"));
+ var hwndCli = GetConsoleWindow();
+ ShowWindow(hwndCli, SW_MINIMIZE);
+ break;
+ default:
+ if(!Shown)
+ break;
+
+ message += key.KeyChar;
+ Console.Write(key.KeyChar);
+ break;
+ }
+ }
+ }
+
+ Sock.Close();
+ }
+ }
+
+ private static void Sock_OnClose(object sender, CloseEventArgs e) {
+ Environment.Exit(0);
+ }
+
+ static void OnOpen(object sender, EventArgs e) {
+ Sock.Send(Pack(1, Username, Password));
+
+ /*
+#if DEBUG
+ Sock.Send(Pack(1, Properties.Resources.ConnectionUserDebug, Properties.Resources.ConnectionPwd));
+#else
+ Sock.Send(Pack(1, Properties.Resources.ConnectionUserRelease, Properties.Resources.ConnectionPwd));
+#endif
+ */
+ }
+
+ private static void PingTimer_Elapsed(object sender, ElapsedEventArgs e) {
+ Sock.Send(Pack(0, UserId.ToString()));
+ }
+
+ static void OnMessage(object sender, MessageEventArgs e) {
+ var msg = Unpack(e.Data);
+ Message message;
+
+ switch(msg.Id) {
+ case 1:
+ if(UserId == -1) {
+ UserId = Int32.Parse(msg.Data[1]);
+ Users.Add(UserId, msg.Data[2]);
+
+ pingTimer = new Timer(30000);
+ pingTimer.AutoReset = true;
+ pingTimer.Elapsed += PingTimer_Elapsed;
+ pingTimer.Enabled = true;
+ } else {
+ var userId = Int32.Parse(msg.Data[1]);
+ try {
+ Users.Add(userId, msg.Data[2]);
+ } catch { }
+
+ message = new Message {
+ Sent = DateTime.Now,
+ Sender = Users[-1],
+ Contents = $"{Users[userId]} joined."
+ };
+ Logs.Add(message);
+ WriteMessage(message);
+ }
+ break;
+ case 2:
+ var userIdd = Int32.Parse(msg.Data[1]);
+
+ message = new Message {
+ Sent = DateTimeOffset.FromUnixTimeSeconds(Int64.Parse(msg.Data[0])).ToLocalTime().DateTime,
+ Sender = Users[userIdd],
+ Contents = userIdd == -1
+ ? ParseBotMessage(msg.Data[2])
+ : msg.Data[2]
+ };
+
+ Logs.Add(message);
+ WriteMessage(message);
+ break;
+ case 3:
+ message = new Message {
+ Sent = DateTime.Now,
+ Sender = Users[-1],
+ Contents = $"{msg.Data[1]} " + (msg.Data[2] == "leave" ? "left." : "was kicked.")
+ };
+
+ Logs.Add(message);
+ WriteMessage(message);
+ break;
+ case 7:
+ if(msg.Data[0] == "0") {
+ int count = Int32.Parse(msg.Data[1]);
+
+ for(int i = 0; i < count; ++i) {
+ int ix = 2 + 5*i;
+ try {
+ Users.Add(Int32.Parse(msg.Data[ix]), msg.Data[ix + 1]);
+ } catch { }
+ }
+ } else {
+ message = new Message {
+ Sent = DateTimeOffset.FromUnixTimeSeconds(Int64.Parse(msg.Data[1])).ToLocalTime().DateTime,
+ Sender = msg.Data[3],
+ Contents = msg.Data[2] == "-1"
+ ? ParseBotMessage(msg.Data[6])
+ : msg.Data[6]
+ };
+
+ Logs.Add(message);
+ WriteMessage(message);
+ }
+ break;
+ case 10:
+ Users[Int32.Parse(msg.Data[0])] = msg.Data[1];
+ break;
+ }
+ }
+
+ class Message {
+ public DateTime Sent { get; set; }
+ public string Sender { get; set; }
+ public string Contents { get; set; }
+ }
+
+ class Packet {
+ public int Id { get; set; }
+ public List Data { get; set; }
+ }
+ }
+}
diff --git a/interop/Properties/AssemblyInfo.cs b/interop/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ae18c7f
--- /dev/null
+++ b/interop/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("interop")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("interop")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("e67c0218-5911-4acc-8449-e4a791200b78")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/interop/Properties/Resources.Designer.cs b/interop/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..6f51a5b
--- /dev/null
+++ b/interop/Properties/Resources.Designer.cs
@@ -0,0 +1,141 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace interop.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("interop.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {
+ /// "botText": {
+ /// "say" : "{0}",
+ /// "join" : "{0} has joined the chat.",
+ /// "leave" : "{0} has disconnected.",
+ /// "jchan" : "{0} entered the channel.",
+ /// "lchan" : "{0} left the channel.",
+ /// "kick" : "{0} has been kicked.",
+ /// "nick" : "{0} is now known as {1}.",
+ /// "crchan" : "{0} has been created.",
+ /// "delchan" : "{0} has been deleted.",
+ /// "cpwdchan" : "Password changed successfully.",
+ /// "cpr [rest of string was truncated]";.
+ ///
+ internal static string CommonLang {
+ get {
+ return ResourceManager.GetString("CommonLang", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ws://aroltd.com:17777.
+ ///
+ internal static string ConnectionAddress {
+ get {
+ return ResourceManager.GetString("ConnectionAddress", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to e25c69c67adfc9e62fcd661b3e825cb3d5357652.
+ ///
+ internal static string ConnectionPwd {
+ get {
+ return ResourceManager.GetString("ConnectionPwd", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to malloc2.
+ ///
+ internal static string ConnectionUserDebug {
+ get {
+ return ResourceManager.GetString("ConnectionUserDebug", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to malloc.
+ ///
+ internal static string ConnectionUserRelease {
+ get {
+ return ResourceManager.GetString("ConnectionUserRelease", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {
+ /// "botText": {
+ /// "silence": "You have been silenced!",
+ /// "unsil": "You have been unsilenced!",
+ /// "usilok": "{0} has been unsilenced.",
+ /// "silok": "{0} has been silenced."
+ /// },
+ /// "botErrText": {
+ /// "silerr": "Error: User is already silenced!",
+ /// "usilerr": "Error: User is not silenced!",
+ /// "silperr": "Error: You are not allowed to silence this user!",
+ /// "usilperr": "Error: You are not allowed to unsilence this user!",
+ /// "si [rest of string was truncated]";.
+ ///
+ internal static string CoreLang {
+ get {
+ return ResourceManager.GetString("CoreLang", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/interop/Properties/Resources.resx b/interop/Properties/Resources.resx
new file mode 100644
index 0000000..ec7c4b5
--- /dev/null
+++ b/interop/Properties/Resources.resx
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ {
+ "botText": {
+ "say" : "{0}",
+ "join" : "{0} has joined the chat.",
+ "leave" : "{0} has disconnected.",
+ "jchan" : "{0} entered the channel.",
+ "lchan" : "{0} left the channel.",
+ "kick" : "{0} has been kicked.",
+ "nick" : "{0} is now known as {1}.",
+ "crchan" : "{0} has been created.",
+ "delchan" : "{0} has been deleted.",
+ "cpwdchan" : "Password changed successfully.",
+ "cprivchan" : "Channel privilege changed successfully.",
+ "ipaddr" : "User {0} - IP Address: {1}"
+ },
+ "botErrText": {
+ "nolang" : "Error: Chatbot sent {1} id '{0}' with no definition in this language!",
+ "nocmd" : "Error: Command {0} not found!",
+ "cmdna" : "Error: Not allowed to use {0}!",
+ "cmderr" : "Error: Command formatted incorrectly!",
+ "usernf" : "Error: User {0} not found!",
+ "kickna" : "Error: Not allowed to kick {0}!",
+ "samechan" : "Error: You are already in {0}!",
+ "nochan" : "Error: Channel {0} does not exist!",
+ "ipchan" : "Error: You are not allowed to join {0}!",
+ "nopwchan" : "Error: Channel {0} is password-protected! Use '/join {0} [password]'.",
+ "ipwchan" : "Error: Password to channel {0} is incorrect!",
+ "inchan" : "Error: Channel name cannot start with a @ or *!",
+ "nischan" : "Error: Channel {0} already exists!",
+ "ndchan" : "Error: You are not allowed to delete channel {0}!",
+ "namchan" : "Error: You are not allowed to modify channel {0}!",
+ "nameinuse" : "Error: Username {0} in use!",
+ "rankerr" : "Error: Cannot set channel privilege higher than your own!",
+ "reconnect" : "Connection lost! Attempting to reconnect automatically...",
+ "logerr" : "Server uses the flat file database implementation and cannot serve logs to clients. To view chat logs, open the logs.txt file in the ffdb folder in the server's root directory.",
+ "generr" : "An error has occurred!"
+ }
+}
+
+
+ ws://aroltd.com:17777
+
+
+ e25c69c67adfc9e62fcd661b3e825cb3d5357652
+
+
+ malloc2
+
+
+ malloc
+
+
+ {
+ "botText": {
+ "silence": "You have been silenced!",
+ "unsil": "You have been unsilenced!",
+ "usilok": "{0} has been unsilenced.",
+ "silok": "{0} has been silenced."
+ },
+ "botErrText": {
+ "silerr": "Error: User is already silenced!",
+ "usilerr": "Error: User is not silenced!",
+ "silperr": "Error: You are not allowed to silence this user!",
+ "usilperr": "Error: You are not allowed to unsilence this user!",
+ "silself": "Error: You cannot silence yourself!"
+ }
+}
+
+
\ No newline at end of file
diff --git a/interop/interop.csproj b/interop/interop.csproj
new file mode 100644
index 0000000..68bfd63
--- /dev/null
+++ b/interop/interop.csproj
@@ -0,0 +1,71 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {E67C0218-5911-4ACC-8449-E4A791200B78}
+ Exe
+ interop
+ interop
+ v4.6.1
+ 512
+ true
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+ ..\packages\WebSocketSharp.1.0.3-rc11\lib\websocket-sharp.dll
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/interop/packages.config b/interop/packages.config
new file mode 100644
index 0000000..cdb65c2
--- /dev/null
+++ b/interop/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file