diff --git a/Tools/BiblioTech/CodexEndpoints.cs b/Tools/BiblioTech/CodexEndpoints.cs
new file mode 100644
index 00000000..706c27b6
--- /dev/null
+++ b/Tools/BiblioTech/CodexEndpoints.cs
@@ -0,0 +1,10 @@
+using Utils;
+
+namespace BiblioTech
+{
+ public class CodexEndpoints
+ {
+ public string Name { get; set; } = string.Empty;
+ public Address[] Addresses { get; set; } = Array.Empty
();
+ }
+}
diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs
deleted file mode 100644
index 42290a5b..00000000
--- a/Tools/BiblioTech/CommandHandler.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Discord.Commands;
-using Discord.WebSocket;
-using System.Reflection;
-
-namespace BiblioTech
-{
- public class CommandHandler
- {
- private readonly DiscordSocketClient client;
- private readonly CommandService commands;
-
- // Retrieve client and CommandService instance via ctor
- public CommandHandler(DiscordSocketClient client, CommandService commands)
- {
- this.commands = commands;
- this.client = client;
- }
-
- public async Task InstallCommandsAsync()
- {
- // Hook the MessageReceived event into our command handler
- client.MessageReceived += HandleCommandAsync;
-
- // Here we discover all of the command modules in the entry
- // assembly and load them. Starting from Discord.NET 2.0, a
- // service provider is required to be passed into the
- // module registration method to inject the
- // required dependencies.
- //
- // If you do not use Dependency Injection, pass null.
- // See Dependency Injection guide for more information.
- await commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(),
- services: null);
- }
-
- private async Task HandleCommandAsync(SocketMessage messageParam)
- {
- // Don't process the command if it was a system message
- var message = messageParam as SocketUserMessage;
- if (message == null) return;
-
- // Create a number to track where the prefix ends and the command begins
- int argPos = 0;
-
- // Determine if the message is a command based on the prefix and make sure no bots trigger commands
- if (message.Author.IsBot) return;
- if (!message.HasMentionPrefix(client.CurrentUser, ref argPos) &&
- !message.Content.StartsWith("!")) return;
-
- // Create a WebSocket-based command context based on the message
- var context = new SocketCommandContext(client, message);
-
- // Execute the command with the command context we just
- // created, along with the service provider for precondition checks.
- await commands.ExecuteAsync(
- context: context,
- argPos: argPos,
- services: null);
- }
- }
-}
diff --git a/Tools/BiblioTech/Configuration.cs b/Tools/BiblioTech/Configuration.cs
index 73b2fa8c..94a8ec3a 100644
--- a/Tools/BiblioTech/Configuration.cs
+++ b/Tools/BiblioTech/Configuration.cs
@@ -7,7 +7,10 @@ namespace BiblioTech
[Uniform("token", "t", "TOKEN", true, "Discord Application Token")]
public string ApplicationToken { get; set; } = string.Empty;
- [Uniform("deploys", "d", "DEPLOYS", false, "Path where deployment JSONs are located.")]
- public string DeploymentsPath { get; set; } = "deploys";
+ [Uniform("server-name", "sn", "SERVERNAME", true, "Name of the Discord server")]
+ public string ServerName { get; set; } = string.Empty;
+
+ [Uniform("endpoints", "e", "ENDPOINTS", false, "Path where endpoint JSONs are located. Also accepts codex-deployment JSONs.")]
+ public string EndpointsPath { get; set; } = "endpoints";
}
}
diff --git a/Tools/BiblioTech/DeploymentFilesMonitor.cs b/Tools/BiblioTech/DeploymentFilesMonitor.cs
deleted file mode 100644
index 0d8f0540..00000000
--- a/Tools/BiblioTech/DeploymentFilesMonitor.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using CodexPlugin;
-using Newtonsoft.Json;
-
-namespace BiblioTech
-{
- public class DeploymentFilesMonitor
- {
- private DateTime lastUpdate = DateTime.MinValue;
- private CodexDeployment[] deployments = Array.Empty();
-
- public CodexDeployment[] GetDeployments()
- {
- if (ShouldUpdate())
- {
- UpdateDeployments();
- }
-
- return deployments;
- }
-
- private void UpdateDeployments()
- {
- lastUpdate = DateTime.UtcNow;
- var path = Program.Config.DeploymentsPath;
- var files = Directory.GetFiles(path);
- deployments = files.Select(ProcessFile).Where(d => d != null).Cast().ToArray();
- }
-
- private CodexDeployment? ProcessFile(string filename)
- {
- try
- {
- var lines = File.ReadAllLines(filename);
- return JsonConvert.DeserializeObject(string.Join(" ", lines));
- }
- catch
- {
- return null;
- }
- }
-
- private bool ShouldUpdate()
- {
- return !deployments.Any() || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
- }
- }
-}
diff --git a/Tools/BiblioTech/EndpointsFilesMonitor.cs b/Tools/BiblioTech/EndpointsFilesMonitor.cs
new file mode 100644
index 00000000..9136740d
--- /dev/null
+++ b/Tools/BiblioTech/EndpointsFilesMonitor.cs
@@ -0,0 +1,78 @@
+using CodexPlugin;
+using KubernetesWorkflow;
+using Newtonsoft.Json;
+using Utils;
+
+namespace BiblioTech
+{
+ public class EndpointsFilesMonitor
+ {
+ private DateTime lastUpdate = DateTime.MinValue;
+ private CodexEndpoints[] endpoints = Array.Empty();
+
+ public CodexEndpoints[] GetEndpoints()
+ {
+ if (ShouldUpdate())
+ {
+ UpdateEndpoints();
+ }
+
+ return endpoints;
+ }
+
+ private void UpdateEndpoints()
+ {
+ lastUpdate = DateTime.UtcNow;
+ var path = Program.Config.EndpointsPath;
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ File.WriteAllText(Path.Combine(path, "readme.txt"), "Place codex-deployment.json or codex-endpoints.json here.");
+ return;
+ }
+
+ var files = Directory.GetFiles(path);
+ endpoints = files.Select(ProcessFile).Where(d => d != null).Cast().ToArray();
+ }
+
+ private CodexEndpoints? ProcessFile(string filename)
+ {
+ try
+ {
+ var lines = string.Join(" ", File.ReadAllLines(filename));
+ try
+ {
+ return JsonConvert.DeserializeObject(lines);
+ }
+ catch { }
+
+ return ConvertToEndpoints(JsonConvert.DeserializeObject(lines));
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private CodexEndpoints? ConvertToEndpoints(CodexDeployment? codexDeployment)
+ {
+ if (codexDeployment == null) return null;
+
+ return new CodexEndpoints
+ {
+ Name = "codex-deployment-" + codexDeployment.Metadata.StartUtc.ToString("o"),
+ Addresses = codexDeployment.CodexContainers.Select(ConvertToAddress).ToArray()
+ };
+ }
+
+ private Address ConvertToAddress(RunningContainer rc)
+ {
+ return rc.ClusterExternalAddress;
+ }
+
+ private bool ShouldUpdate()
+ {
+ return !endpoints.Any() || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
+ }
+ }
+}
diff --git a/Tools/BiblioTech/EndpointsMonitor.cs b/Tools/BiblioTech/EndpointsMonitor.cs
new file mode 100644
index 00000000..27e61bd2
--- /dev/null
+++ b/Tools/BiblioTech/EndpointsMonitor.cs
@@ -0,0 +1,81 @@
+using CodexPlugin;
+using Core;
+using KubernetesWorkflow;
+
+namespace BiblioTech
+{
+ public class EndpointsMonitor
+ {
+ private readonly EndpointsFilesMonitor fileMonitor;
+ private readonly EntryPoint entryPoint;
+ private DateTime lastUpdate = DateTime.MinValue;
+ private string report = string.Empty;
+
+ public EndpointsMonitor(EndpointsFilesMonitor fileMonitor, EntryPoint entryPoint)
+ {
+ this.fileMonitor = fileMonitor;
+ this.entryPoint = entryPoint;
+ }
+
+ public async Task GetReport()
+ {
+ if (ShouldUpdate())
+ {
+ await UpdateReport();
+ }
+
+ return report;
+ }
+
+ private async Task UpdateReport()
+ {
+ lastUpdate = DateTime.UtcNow;
+
+ var endpoints = fileMonitor.GetEndpoints();
+ if (!endpoints.Any())
+ {
+ report = "There are no networks currently online.";
+ }
+ else
+ {
+ var nl = Environment.NewLine;
+ report = $"There are {endpoints.Length} networks online." + nl;
+ for (var i = 0; i < endpoints.Length; i++)
+ {
+ var e = endpoints[i];
+ report += $" [{i} - {e.Name}] = {await GetStatusMessage(e)}";
+ }
+ }
+ }
+
+ private async Task GetStatusMessage(CodexEndpoints e)
+ {
+ var success = 0;
+ foreach (var addr in e.Addresses)
+ {
+ await Task.Run(() =>
+ {
+ // this is definitely not going to work:
+ var rc = new RunningContainer(null!, null!, null!, "", addr, addr);
+
+ var access = new CodexAccess(entryPoint.Tools, rc, null!);
+ var debugInfo = access.GetDebugInfo();
+
+ if (!string.IsNullOrEmpty(debugInfo.id))
+ {
+ success++;
+ }
+ });
+ }
+
+ return $"{success} / {e.Addresses.Length} online.";
+ // todo: analyze returned peerIDs to say something about
+ // the number of none-test-net managed nodes seen on the network.
+ }
+
+ private bool ShouldUpdate()
+ {
+ return string.IsNullOrEmpty(report) || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
+ }
+ }
+}
diff --git a/Tools/BiblioTech/HelloWorldCommand.cs b/Tools/BiblioTech/HelloWorldCommand.cs
index ac58f2f2..8659acf1 100644
--- a/Tools/BiblioTech/HelloWorldCommand.cs
+++ b/Tools/BiblioTech/HelloWorldCommand.cs
@@ -61,8 +61,5 @@ namespace BiblioTech
Console.WriteLine(json);
}
}
-
-
-
}
}
diff --git a/Tools/BiblioTech/HelloWorldModule.cs b/Tools/BiblioTech/HelloWorldModule.cs
deleted file mode 100644
index f0e711bd..00000000
--- a/Tools/BiblioTech/HelloWorldModule.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Discord.Commands;
-
-namespace BiblioTech
-{
- public class HelloWorldModule : ModuleBase
- {
- [Command("say")]
- [Summary("Echoes a message.")]
- public Task SayAsync([Remainder][Summary("The text to echo")] string echo)
- {
- return ReplyAsync("I say: " + echo);
- }
- }
-}
diff --git a/Tools/BiblioTech/Program.cs b/Tools/BiblioTech/Program.cs
index 4950f26f..e063f122 100644
--- a/Tools/BiblioTech/Program.cs
+++ b/Tools/BiblioTech/Program.cs
@@ -1,9 +1,8 @@
using ArgsUniform;
+using Core;
using Discord;
-using Discord.Commands;
-using Discord.Net;
using Discord.WebSocket;
-using Newtonsoft.Json;
+using Logging;
namespace BiblioTech
{
@@ -12,7 +11,7 @@ namespace BiblioTech
private DiscordSocketClient client = null!;
public static Configuration Config { get; private set; } = null!;
- public static DeploymentFilesMonitor DeploymentFilesMonitor { get; } = new DeploymentFilesMonitor();
+ public static EndpointsFilesMonitor DeploymentFilesMonitor { get; } = new EndpointsFilesMonitor();
public static Task Main(string[] args)
{
@@ -28,12 +27,18 @@ namespace BiblioTech
client = new DiscordSocketClient();
client.Log += Log;
- var helloWorld = new HelloWorldCommand(client);
+ ProjectPlugin.Load();
+ var entryPoint = new EntryPoint(new ConsoleLog(), new KubernetesWorkflow.Configuration(
+ kubeConfigFile: null, // todo: readonly file
+ operationTimeout: TimeSpan.FromMinutes(5),
+ retryDelay: TimeSpan.FromSeconds(10),
+ kubernetesNamespace: "not-applicable"), "datafiles");
- //var cmdService = new CommandService();
- //var handler = new CommandHandler(client, cmdService);
- //await handler.InstallCommandsAsync();
- //Console.WriteLine("Command handler installed...");
+ var fileMonitor = new EndpointsFilesMonitor();
+ var monitor = new EndpointsMonitor(fileMonitor, entryPoint);
+
+ var statusCommand = new StatusCommand(client, monitor);
+ //var helloWorld = new HelloWorldCommand(client); Example for how to do arguments.
await client.LoginAsync(TokenType.Bot, Config.ApplicationToken);
await client.StartAsync();
diff --git a/Tools/BiblioTech/StatusCommand.cs b/Tools/BiblioTech/StatusCommand.cs
new file mode 100644
index 00000000..9d081c0b
--- /dev/null
+++ b/Tools/BiblioTech/StatusCommand.cs
@@ -0,0 +1,50 @@
+using Discord.Net;
+using Discord.WebSocket;
+using Discord;
+using Newtonsoft.Json;
+using Core;
+
+namespace BiblioTech
+{
+ public class StatusCommand
+ {
+ private const string CommandName = "status";
+ private readonly DiscordSocketClient client;
+ private readonly EndpointsMonitor monitor;
+
+ public StatusCommand(DiscordSocketClient client, EndpointsMonitor monitor)
+ {
+ this.client = client;
+ this.monitor = monitor;
+
+ client.Ready += Client_Ready;
+ client.SlashCommandExecuted += SlashCommandHandler;
+ }
+
+ private async Task SlashCommandHandler(SocketSlashCommand command)
+ {
+ if (command.CommandName != CommandName) return;
+
+ await command.RespondAsync(await monitor.GetReport());
+ }
+
+ private async Task Client_Ready()
+ {
+ var guild = client.Guilds.Single(g => g.Name == Program.Config.ServerName);
+
+ var guildCommand = new SlashCommandBuilder()
+ .WithName(CommandName)
+ .WithDescription("Display status of test net.");
+
+ try
+ {
+ await guild.CreateApplicationCommandAsync(guildCommand.Build());
+ }
+ catch (HttpException exception)
+ {
+ var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
+ Console.WriteLine(json);
+ }
+ }
+ }
+}
diff --git a/Tools/BiblioTech/docker/docker-compose.yaml b/Tools/BiblioTech/docker/docker-compose.yaml
index f283ac72..dae80e44 100644
--- a/Tools/BiblioTech/docker/docker-compose.yaml
+++ b/Tools/BiblioTech/docker/docker-compose.yaml
@@ -3,4 +3,4 @@ services:
image: thatbenbierens/codex-discordbot:initial
environment:
- TOKEN=tokenplz
- - DEPLOYS=deploypath
+ - SERVERNAME=ThatBen's server