using franz; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; namespace directors_assistant { class Program { public static Config conf; public static Telefranz telefranz; static async Task Main(string[] args) { conf = JsonConvert.DeserializeObject(File.ReadAllText("appsettings.json")); Telefranz.Configure(name: conf.name, bootstrap_servers: conf.bootstrap_servers, commands: conf.commands.Select(c => c.name).ToList()); telefranz = Telefranz.Instance; telefranz.addHandler((gray_messages.directorial.execute_command ec) => { var matchedCommands = conf.commands.Where(c => c.name == ec.command)?.ToList(); //I swear I have a vague memory of foreach in linq'd query throwing an exception when results empty. if (matchedCommands != null && matchedCommands.Count > 0) { foreach (var cmd in matchedCommands) { Console.WriteLine($"executing {cmd.name}{(ec.args != null ? ", given args " + string.Join(' ', ec.args) : "")} with timeout {ec.timeout}"); var cmdAndArgs = cmd.shell.Split(' '); var justArgs = cmdAndArgs.Skip(1).ToList(); if(ec.args?.Count > 0) { justArgs.AddRange(ec.args); } var commandPath = cmdAndArgs[0]; var commandArguments = String.Join(' ', justArgs); if (ec.timeout != null && ec.timeout > TimeSpan.Zero) { executeTimed(cmd.name, commandPath, commandArguments, ec.timeout); } else { Task.Run(() => { executeUntimed(cmd.name, commandPath, commandArguments); }); } } } }); await Task.Delay(1000); telefranz.ProduceMessage(new gray_messages.global.sound_off()); await Task.Delay(-1); } private static void executeTimed(string commandName, string commandPath, string commandArguments, TimeSpan? timeout) { var process = readableProcess(commandPath, commandArguments); var outputs = new List(); var errors = new List(); process.OutputDataReceived += new DataReceivedEventHandler((s, e) => { outputs.Add(e.Data); }); process.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { errors.Add(e.Data); }); var stopwatch = new Stopwatch(); stopwatch.Start(); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); if (timeout != null && timeout > TimeSpan.Zero) { Task.WaitAny( Task.Run(() => { process.WaitForExit(); }), Task.Delay(timeout.Value) ); } else { process.WaitForExit(); } if (process.HasExited) { stopwatch.Stop(); Console.WriteLine($"{commandName} completed"); telefranz.ProduceMessage(new gray_messages.directorial.command_completed() { command = commandName, runtimeMilliseconds = stopwatch.ElapsedMilliseconds, exit_code = process.ExitCode, stdout = string.Join('\n', outputs), stderr = string.Join('\n', errors) }); } else { process.Kill(); Console.WriteLine($"{commandName} expired"); telefranz.ProduceMessage(new gray_messages.directorial.command_expired() { command = commandName, stdout = string.Join('\n', outputs), stderr = string.Join('\n', errors) }); } } private static void executeUntimed(string commandName, string commandPath, string commandArguments) { var stopwatch = new Stopwatch(); var process = readableProcess(commandPath, commandArguments); process.OutputDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine($"{commandName} output: {e.Data}"); if (string.IsNullOrWhiteSpace(e.Data)) { return; } telefranz.ProduceMessage(new gray_messages.directorial.command_output() { stdout = e.Data, command = commandName, runtime = (uint)stopwatch.ElapsedMilliseconds }); }); process.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) { return; } Console.Error.WriteLine($"{commandName} err (but not necessarily dead?): {e.Data}"); telefranz.ProduceMessage(new gray_messages.directorial.command_error() { stderr = e.Data, command = commandName, runtime = (uint)stopwatch.ElapsedMilliseconds }); }); try { stopwatch.Start(); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.WaitForExit(); stopwatch.Stop(); Console.WriteLine($"{commandName} returned {process.ExitCode} after {stopwatch.ElapsedMilliseconds}ms"); telefranz.ProduceMessage(new gray_messages.directorial.command_completed() { command = commandName, runtimeMilliseconds = stopwatch.ElapsedMilliseconds, exit_code = process.ExitCode }); } catch(Exception e) { Console.Error.WriteLine($"{commandName} FATAL: {JsonConvert.SerializeObject(e)}"); telefranz.ProduceMessage(new gray_messages.directorial.command_error() { stderr = e.Message, command = commandName, runtime = (uint)stopwatch.ElapsedMilliseconds }); } } private static Process readableProcess(string commandPath, string commandArguments) { var pi = new ProcessStartInfo(commandPath, commandArguments); pi.UseShellExecute = false; pi.CreateNoWindow = true; pi.RedirectStandardError = true; pi.RedirectStandardOutput = true; var process = new Process(); process.StartInfo = pi; return process; } } }