diff --git a/samples/idn/Inspector.cs b/samples/idn/Inspector.cs new file mode 100644 index 000000000..1544c8d07 --- /dev/null +++ b/samples/idn/Inspector.cs @@ -0,0 +1,74 @@ +using System.Collections; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Idn +{ + public static class Inspector + { + public static string Inspect(object value) + { + var builder = new StringBuilder(); + if (value != null) + { + var type = value.GetType().GetTypeInfo(); + builder.AppendLine($"[{type.Namespace}.{type.Name}]"); + builder.AppendLine($"{InspectProperty(value)}"); + + if (value is IEnumerable) + { + var items = (value as IEnumerable).Cast().ToArray(); + if (items.Length > 0) + { + builder.AppendLine(); + foreach (var item in items) + builder.AppendLine($"- {InspectProperty(item)}"); + } + } + else + { + var groups = type.GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(x => x.GetIndexParameters().Length == 0) + .GroupBy(x => x.Name) + .OrderBy(x => x.Key) + .ToArray(); + if (groups.Length > 0) + { + builder.AppendLine(); + int pad = groups.Max(x => x.Key.Length) + 1; + foreach (var group in groups) + builder.AppendLine($"{group.Key.PadRight(pad, ' ')}{InspectProperty(group.First().GetValue(value))}"); + } + } + } + else + builder.AppendLine("null"); + return builder.ToString(); + } + + private static string InspectProperty(object obj) + { + if (obj == null) + return "null"; + + var type = obj.GetType(); + + var debuggerDisplay = type.GetProperty("DebuggerDisplay", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (debuggerDisplay != null) + return debuggerDisplay.GetValue(obj).ToString(); + + var toString = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => x.Name == "ToString" && x.DeclaringType != typeof(object)) + .FirstOrDefault(); + if (toString != null) + return obj.ToString(); + + var count = type.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (count != null) + return $"[{count.GetValue(obj)} Items]"; + + return obj.ToString(); + } + } +} diff --git a/samples/idn/Program.cs b/samples/idn/Program.cs new file mode 100644 index 000000000..aab9924f9 --- /dev/null +++ b/samples/idn/Program.cs @@ -0,0 +1,152 @@ +using Discord; +using Discord.WebSocket; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Idn +{ + public class Program + { + public static readonly string[] Imports = + { + "System", + "System.Collections.Generic", + "System.Linq", + "System.Threading.Tasks", + "System.Diagnostics", + "System.IO", + "Discord", + "Discord.Rest", + "Discord.WebSocket", + "idn" + }; + + static async Task Main(string[] args) + { + var token = File.ReadAllText("token.ignore"); + var client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug }); + var logQueue = new ConcurrentQueue(); + var logCancelToken = new CancellationTokenSource(); + int presenceUpdates = 0; + + client.Log += msg => + { + logQueue.Enqueue(msg); + return Task.CompletedTask; + }; + Console.CancelKeyPress += (_ev, _s) => + { + logCancelToken.Cancel(); + }; + + var logTask = Task.Run(async () => + { + var fs = new FileStream("idn.log", FileMode.Append); + var logStringBuilder = new StringBuilder(200); + string logString = ""; + + byte[] helloBytes = Encoding.UTF8.GetBytes($"### new log session: {DateTime.Now} ###\n\n"); + await fs.WriteAsync(helloBytes); + + while (!logCancelToken.IsCancellationRequested) + { + if (logQueue.TryDequeue(out var msg)) + { + if (msg.Message?.IndexOf("PRESENCE_UPDATE)") > 0) + { + presenceUpdates++; + continue; + } + + _ = msg.ToString(builder: logStringBuilder); + logStringBuilder.AppendLine(); + logString = logStringBuilder.ToString(); + + Debug.Write(logString, "DNET"); + await fs.WriteAsync(Encoding.UTF8.GetBytes(logString)); + } + await fs.FlushAsync(); + try + { + await Task.Delay(100, logCancelToken.Token); + } + finally { } + } + + byte[] goodbyeBytes = Encoding.UTF8.GetBytes($"#!! end log session: {DateTime.Now} !!#\n\n\n"); + await fs.WriteAsync(goodbyeBytes); + await fs.DisposeAsync(); + }); + + await client.LoginAsync(TokenType.Bot, token); + await client.StartAsync(); + + var options = ScriptOptions.Default + .AddReferences(GetAssemblies().ToArray()) + .AddImports(Imports); + + var globals = new ScriptGlobals + { + Client = client, + PUCount = -1, + }; + + while (true) + { + Console.Write("> "); + string input = Console.ReadLine(); + + if (input == "quit!") + { + break; + } + + object eval; + try + { + globals.PUCount = presenceUpdates; + eval = await CSharpScript.EvaluateAsync(input, options, globals); + } + catch (Exception e) + { + eval = e; + } + Console.WriteLine(Inspector.Inspect(eval)); + } + + await client.StopAsync(); + client.Dispose(); + logCancelToken.Cancel(); + try + { await logTask; } + finally { Console.WriteLine("goodbye!"); } + } + + static IEnumerable GetAssemblies() + { + var Assemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies(); + foreach (var a in Assemblies) + { + var asm = Assembly.Load(a); + yield return asm; + } + yield return Assembly.GetEntryAssembly(); + } + + public class ScriptGlobals + { + public DiscordSocketClient Client { get; set; } + public int PUCount { get; set; } + } + } +} diff --git a/samples/idn/idn.csproj b/samples/idn/idn.csproj new file mode 100644 index 000000000..fafb3df3f --- /dev/null +++ b/samples/idn/idn.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + + + + + + + + diff --git a/samples/idn/logview.ps1 b/samples/idn/logview.ps1 new file mode 100644 index 000000000..0857475f5 --- /dev/null +++ b/samples/idn/logview.ps1 @@ -0,0 +1 @@ +Get-Content .\bin\Debug\netcoreapp3.1\idn.log -Tail 3 -Wait \ No newline at end of file