diff --git a/MinecraftDiscordBot/MinecraftDiscordBot.csproj b/MinecraftDiscordBot/MinecraftDiscordBot.csproj
index 21f27df..3cf40ea 100644
--- a/MinecraftDiscordBot/MinecraftDiscordBot.csproj
+++ b/MinecraftDiscordBot/MinecraftDiscordBot.csproj
@@ -6,7 +6,7 @@
enable
enable
Linux
- 1.1.1
+ 1.1.2
Michael Chen
$(Authors)
https://gitlab.com/chenmichael/mcdiscordbot
diff --git a/MinecraftDiscordBot/Models/Fluid.cs b/MinecraftDiscordBot/Models/Fluid.cs
index 07c4556..11e4b78 100644
--- a/MinecraftDiscordBot/Models/Fluid.cs
+++ b/MinecraftDiscordBot/Models/Fluid.cs
@@ -19,4 +19,6 @@ public class Fluid {
: $"{Amount:n0} mB of {DisplayName}";
[JsonIgnore]
public string CleanDisplayName => DisplayName[1..^1];
+ [JsonIgnore]
+ public string? TagString => Tags is string[] tags ? string.Join(", ", tags) : null;
}
diff --git a/MinecraftDiscordBot/Models/Item.cs b/MinecraftDiscordBot/Models/Item.cs
index 70e5298..05bc556 100644
--- a/MinecraftDiscordBot/Models/Item.cs
+++ b/MinecraftDiscordBot/Models/Item.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System.Diagnostics;
+using System.Text;
namespace MinecraftDiscordBot.Models;
@@ -10,5 +11,23 @@ public class Item : Fluid {
public Md5Hash Fingerprint { get; set; } = default!;
[JsonProperty("nbt", Required = Required.DisallowNull)]
public dynamic? NBT { get; set; }
- public override string ToString() => $"{Amount:n0}x {DisplayName}";
+ public override string ToString() => $"{AmountString} {CleanDisplayName}";
+ [JsonIgnore]
+ public string DetailString {
+ get {
+ var sb = new StringBuilder();
+ sb.AppendFormat("{0} {1}, fp: {2}", AmountString, CleanDisplayName, Fingerprint);
+ if (TagString is string tags)
+ sb.AppendFormat(", tags: [{0}]", tags);
+ if (NBT is not null)
+ sb.AppendFormat(", NBT: {0}", JsonConvert.SerializeObject(NBT));
+ return sb.ToString();
+ }
+ }
+ [JsonIgnore]
+ public string AmountString => Amount switch {
+ > 1000000 => $"> {Amount / 1000000:n0}m",
+ > 10000 => $"~ {Amount / 1000.0f:n2}k",
+ _ => Amount.ToString()
+ };
}
diff --git a/MinecraftDiscordBot/Program.cs b/MinecraftDiscordBot/Program.cs
index 09a7474..44609c2 100644
--- a/MinecraftDiscordBot/Program.cs
+++ b/MinecraftDiscordBot/Program.cs
@@ -57,10 +57,11 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana
_config = config;
_computer = new(this);
_computer.ChatMessageReceived += MinecraftMessageReceived;
+ _computer.SocketChanged += ComputerConnectedChanged;
_administrators = config.Administrators.ToHashSet();
ClientScript = GetClientScript(config);
_client.Log += LogAsync;
- _client.MessageReceived += (msg) => DiscordMessageReceived(msg);
+ _client.MessageReceived += (msg) => DiscordMessageReceived(msg, 20 * 1000);
_client.ReactionAdded += DiscordReactionAdded;
_wssv = new WebSocketServer($"ws://0.0.0.0:{config.Port}") {
RestartAfterListenError = true
@@ -69,6 +70,11 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana
_whitelistedChannels = config.Channels.ToHashSet();
}
+ private void ComputerConnectedChanged(object? sender, IWebSocketConnection? e) {
+ _ = Task.Run(() => Broadcast(i => i.SendMessageAsync(e is not null
+ ? "The Minecraft client is now available!"
+ : "The Minecraft client disconnected!")));
+ }
private void MinecraftMessageReceived(object? sender, ChatEvent e)
=> Task.Run(() => WebhookBroadcast(i => i.SendMessageAsync(e.Message, username: e.Username, avatarUrl: $"https://crafatar.com/renders/head/{e.UUID}")));
private Task WebhookBroadcast(Func> apply) => Task.WhenAll(Channels.Select(i => apply(new DiscordWebhookClient(i.Webhook))));
@@ -211,7 +217,8 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana
private Task SendResponse(SocketUserMessage message, ResponseType response) => response switch {
ResponseType.IChoiceResponse res => HandleChoice(message, res),
ResponseType.StringResponse res => message.ReplyAsync(res.Message),
- _ => message.ReplyAsync($"Whoops, someone forgot to implement '{response.GetType()}' responses?"),
+ ResponseType.FileResponse res => message.Channel.SendFileAsync(res.Path, text: res.Message),
+ _ => message.ReplyAsync($"Whoops, someone forgot to implement '{response.GetType().Name}' responses?"),
};
private readonly ConcurrentDictionary _choiceWait = new();
@@ -340,6 +347,8 @@ public abstract class ResponseType {
private static string DefaultDisplay(T obj) => obj?.ToString() ?? throw new InvalidProgramException("ToString did not yield anything!");
public static ResponseType AsString(string message) => new StringResponse(message);
public static ResponseType FromChoice(string query, IEnumerable choice, Func resultHandler, Func? display = null) => new ChoiceResponse(query, choice, resultHandler, display ?? DefaultDisplay);
+ internal static ResponseType File(string path, string message) => new FileResponse(path, message);
+
public class StringResponse : ResponseType {
public StringResponse(string message) => Message = message;
public string Message { get; }
@@ -363,4 +372,14 @@ public abstract class ResponseType {
_displayer = display;
}
}
+
+ public class FileResponse : ResponseType {
+ public FileResponse(string path, string message) {
+ Path = path;
+ Message = message;
+ }
+
+ public string Path { get; }
+ public string Message { get; }
+ }
}
\ No newline at end of file
diff --git a/MinecraftDiscordBot/Services/RefinedStorageService.cs b/MinecraftDiscordBot/Services/RefinedStorageService.cs
index 9224e26..8f1fbeb 100644
--- a/MinecraftDiscordBot/Services/RefinedStorageService.cs
+++ b/MinecraftDiscordBot/Services/RefinedStorageService.cs
@@ -1,4 +1,5 @@
-using Discord.WebSocket;
+using Discord;
+using Discord.WebSocket;
using MinecraftDiscordBot.Commands;
using MinecraftDiscordBot.Models;
using System.Text;
@@ -146,6 +147,7 @@ public class RefinedStorageService : CommandRouter {
[CommandHandler(CmdListItems, HelpText = "Gets a list of items that are currently stored in the RS system.")]
public async Task HandleItemListing(SocketUserMessage message, string[] parameters, CancellationToken ct) {
+ if (parameters.Length is 1 && parameters[0] == "full") return await SendFullItemList(message, ct);
var sb = new StringBuilder();
sb.Append("The Refined Storage system currently stores these items:");
var items = await RefreshItemList(ct);
@@ -153,11 +155,29 @@ public class RefinedStorageService : CommandRouter {
var taken = 0;
foreach (var item in items) {
if (sb.Length > 500) break;
- sb.AppendFormat("\n{0:n0}x {1}", item.Amount, item.DisplayName);
+ sb.Append('\n');
+ sb.Append(item.ToString());
taken++;
}
- if (items.Count > taken) sb.AppendFormat("\nand {0} more items.", items.Skip(taken).Sum(i => i.Amount));
+ if (items.Count > taken) sb.AppendFormat("\nand {0:n0} more items.", items.Skip(taken).Sum(i => i.Amount));
}
return ResponseType.AsString(sb.ToString());
}
+
+ private async Task SendFullItemList(SocketUserMessage message, CancellationToken ct) {
+ var path = await GetItemListFile(ct);
+ return ResponseType.File(path, $"{message.Author.Mention} Here you go:");
+ }
+
+ private async Task GetItemListFile(CancellationToken ct) {
+ var items = await RefreshItemList(ct);
+ var file = Path.Combine(Path.GetTempPath(), "itemlist.txt");
+ var fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
+ using (var sw = new StreamWriter(fs, Encoding.UTF8)) {
+ await sw.WriteLineAsync("The RS System stores the following items:");
+ foreach (var item in items)
+ await sw.WriteLineAsync(item.DetailString);
+ };
+ return file;
+ }
}