From 0b748eb200c1e1ae49e6ba8805731bd8cd399632 Mon Sep 17 00:00:00 2001 From: linmeideli <2558527373@qq.com> Date: Sat, 23 May 2026 20:26:35 +0800 Subject: [PATCH] Command Description --- Dockerfile | 4 + src/Empostor.Api/Languages/LanguageService.cs | 48 +++++- .../NarratorCommand.cs | 111 +++++++++++++- .../NarratorPlugin.cs | 6 + src/Empostor.Plugins.MapVote/MapVotePlugin.cs | 6 + .../MessageCommand.cs | 7 +- .../MessageEventListener.cs | 31 ---- src/Empostor.Plugins.Message/MessagePlugin.cs | 6 + .../packages.lock.json | 137 ++++++++++++++++++ .../PlayerChannelPlugin.cs | 6 + .../QqVerifyPlugin.cs | 6 + .../Commands/Commands/HelpCommand.cs | 20 ++- src/Empostor.sln | 16 ++ 13 files changed, 356 insertions(+), 48 deletions(-) create mode 100644 src/Empostor.Plugins.Message/packages.lock.json diff --git a/Dockerfile b/Dockerfile index 1821bea..ed27d7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ COPY src/Empostor.Plugins.QqVerify/Empostor.Plugins.QqVerify.csproj ./Empostor.P COPY src/Empostor.Plugins.Titles/Empostor.Plugins.Titles.csproj ./Empostor.Plugins.Titles/ COPY src/Empostor.Plugins.Welcome/Empostor.Plugins.Welcome.csproj ./Empostor.Plugins.Welcome/ COPY src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj ./Empostor.Plugins.MapVote/ +COPY src/Empostor.Plugins.Message/Empostor.Plugins.Message.csproj ./Empostor.Plugins.Message/ # Restore dependencies RUN dotnet restore Empostor.Server/Empostor.Server.csproj -a $TARGETARCH @@ -64,6 +65,8 @@ RUN dotnet publish Empostor.Plugin.Chat/Empostor.Plugin.Chat.csproj \ dotnet publish Empostor.Plugins.Welcome/Empostor.Plugins.Welcome.csproj \ -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ dotnet publish Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.Message/Empostor.Plugins.Message.csproj \ -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore # ============================================ @@ -91,6 +94,7 @@ COPY --from=build /publish/plugins/Empostor.Plugins.QqVerify.dll ./plugins/ COPY --from=build /publish/plugins/Empostor.Plugins.Titles.dll ./plugins/ COPY --from=build /publish/plugins/Empostor.Plugins.Welcome.dll ./plugins/ COPY --from=build /publish/plugins/Empostor.Plugins.MapVote.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.Message.dll ./plugins/ # Copy marketplace data COPY marketplace/ ./marketplace/ diff --git a/src/Empostor.Api/Languages/LanguageService.cs b/src/Empostor.Api/Languages/LanguageService.cs index b7e64c7..8801829 100644 --- a/src/Empostor.Api/Languages/LanguageService.cs +++ b/src/Empostor.Api/Languages/LanguageService.cs @@ -189,7 +189,21 @@ public sealed class LanguageService "command.players.not_in_lobby": "This command can only be used in the lobby.", "command.players.header": "=== Players ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "Your ping: {0}ms" + "command.ping.result": "Your ping: {0}ms", + "command.help.description": "Show available commands.", + "command.help.usage": "help [command]", + "command.stat.description": "Show your player statistics.", + "command.stat.usage": "stat", + "command.setcolor.description": "Change your player color.", + "command.setcolor.usage": "setcolor <0-17>", + "command.players.description": "List players in the current game.", + "command.players.usage": "players", + "command.ping.description": "Show your network ping.", + "command.ping.usage": "ping", + "command.note.description": "Set or clear a lobby note (host only).", + "command.note.usage": "note | note clear", + "command.max.description": "Set max players for the lobby (host only).", + "command.max.usage": "max <1-15>" } """; @@ -228,7 +242,21 @@ public sealed class LanguageService "command.players.not_in_lobby": "此指令只能在大厅中使用。", "command.players.header": "=== 在线玩家 ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "你的延迟:{0}ms" + "command.ping.result": "你的延迟:{0}ms", + "command.help.description": "显示可用命令。", + "command.help.usage": "help [命令]", + "command.stat.description": "显示你的游戏统计。", + "command.stat.usage": "stat", + "command.setcolor.description": "修改你的颜色。", + "command.setcolor.usage": "setcolor <0-17>", + "command.players.description": "列出当前房间的玩家。", + "command.players.usage": "players", + "command.ping.description": "显示你的网络延迟。", + "command.ping.usage": "ping", + "command.note.description": "设置或清除房间备注(仅房主)。", + "command.note.usage": "note <文字> | note clear", + "command.max.description": "设置房间最大人数(仅房主)。", + "command.max.usage": "max <1-15>" } """; @@ -267,7 +295,21 @@ public sealed class LanguageService "command.players.not_in_lobby": "此指令只能在大廳中使用。", "command.players.header": "=== 線上玩家 ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "你的延遲:{0}ms" + "command.ping.result": "你的延遲:{0}ms", + "command.help.description": "顯示可用命令。", + "command.help.usage": "help [命令]", + "command.stat.description": "顯示你的遊戲統計。", + "command.stat.usage": "stat", + "command.setcolor.description": "修改你的顏色。", + "command.setcolor.usage": "setcolor <0-17>", + "command.players.description": "列出當前房間的玩家。", + "command.players.usage": "players", + "command.ping.description": "顯示你的網路延遲。", + "command.ping.usage": "ping", + "command.note.description": "設置或清除房間備註(僅房主)。", + "command.note.usage": "note <文字> | note clear", + "command.max.description": "設置房間最大人數(僅房主)。", + "command.max.usage": "max <1-15>" } """; diff --git a/src/Empostor.Plugin.Narrator/NarratorCommand.cs b/src/Empostor.Plugin.Narrator/NarratorCommand.cs index 30478d2..6df2e3d 100644 --- a/src/Empostor.Plugin.Narrator/NarratorCommand.cs +++ b/src/Empostor.Plugin.Narrator/NarratorCommand.cs @@ -1,3 +1,4 @@ +using System.Text; using System.Threading.Tasks; using Empostor.Api.Commands; using Microsoft.Extensions.Logging; @@ -25,23 +26,106 @@ public sealed class NarratorCommand : ICommand public async ValueTask ExecuteAsync(CommandContext ctx) { + var args = ctx.Args; + + // Host commands + if (args.Length > 0 && ctx.Sender.IsHost) + { + var sub = args[0].ToLowerInvariant(); + + if (sub == "enable" || sub == "on") + { + _service.SetEnabled(ctx.Game, true); + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.host_enabled", "Narrator enabled for this game."), ctx.PlayerControl); + return true; + } + + if (sub == "disable" || sub == "off") + { + _service.SetEnabled(ctx.Game, false); + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.host_disabled", "Narrator disabled for this game."), ctx.PlayerControl); + return true; + } + + if (sub == "status") + { + var enabled = _service.IsEnabled(ctx.Game); + var max = _service.GetMaxUses(ctx.Game); + var statusText = T(ctx, enabled ? "narrator.status_enabled" : "narrator.status_disabled", + enabled ? "enabled" : "disabled"); + var msg = T(ctx, "narrator.host_status", "Status: {0} | Max uses per player per game: {1}") + .Replace("{0}", statusText) + .Replace("{1}", max.ToString()); + await ctx.PlayerControl.SendChatToPlayerAsync(msg, ctx.PlayerControl); + return true; + } + + if (sub == "limit") + { + if (args.Length < 2 || !int.TryParse(args[1], out var limit) || limit < 0) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.host_limit_usage", "Usage: #narrator limit "), ctx.PlayerControl); + return true; + } + + _service.SetMaxUses(ctx.Game, limit); + if (limit == 0) + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.host_limit_zero", "Limit set to 0 (effectively disabled)."), ctx.PlayerControl); + else + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.host_limit_set", "Limit set to {0} per player per game.") + .Replace("{0}", limit.ToString()), + ctx.PlayerControl); + return true; + } + } + + // Show host commands if no args provided + if (args.Length == 0) + { + await ShowUsageAsync(ctx); + return true; + } + + // Check game state — meeting must be active if (!_service.IsMeetingActive(ctx.Game)) { await ctx.PlayerControl.SendChatToPlayerAsync( - "[Narrator] You can only use this command during a meeting.", ctx.PlayerControl); + T(ctx, "narrator.meeting_only", "You can only use #narrator during a meeting."), ctx.PlayerControl); + return true; + } + + // Check if narrator is enabled + if (!_service.IsEnabled(ctx.Game)) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "narrator.disabled", "The narrator is currently disabled by the host."), ctx.PlayerControl); + return true; + } + + var playerName = ctx.PlayerControl.PlayerInfo.PlayerName; + + // Check usage limits + var (allowed, error) = _service.TryUse(ctx.Game, playerName); + if (!allowed) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + $"[Narrator] {error}", ctx.PlayerControl); return true; } var playerMessage = ctx.RawArgs?.Trim(); if (string.IsNullOrEmpty(playerMessage)) { - await ctx.PlayerControl.SendChatToPlayerAsync( - "Usage: /narrator \nExample: /narrator I'm being accused but I have a visual task, how do I prove myself?", - ctx.PlayerControl); + await ShowUsageAsync(ctx); return true; } - _logger.LogInformation("[Narrator] {player} asks: {message}", ctx.PlayerControl.PlayerInfo.PlayerName, playerMessage); + _logger.LogInformation("[Narrator] {player} asks: {message}", playerName, playerMessage); var context = _service.BuildContext(ctx.Game, ctx.PlayerControl); var reply = await _service.AskNarratorAsync(context, playerMessage); @@ -51,4 +135,21 @@ public sealed class NarratorCommand : ICommand return true; } + + private async ValueTask ShowUsageAsync(CommandContext ctx) + { + var sb = new StringBuilder(); + sb.AppendLine(T(ctx, "narrator.usage", + "Usage: #narrator ")); + if (ctx.Sender.IsHost) + sb.AppendLine(T(ctx, "narrator.host_commands", + "Host commands: #narrator enable | disable | limit ")); + await ctx.PlayerControl.SendChatToPlayerAsync(sb.ToString(), ctx.PlayerControl); + } + + private static string T(CommandContext ctx, string key, string defaultText) + { + string result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? defaultText : result; + } } diff --git a/src/Empostor.Plugin.Narrator/NarratorPlugin.cs b/src/Empostor.Plugin.Narrator/NarratorPlugin.cs index 19336f5..08d60e2 100644 --- a/src/Empostor.Plugin.Narrator/NarratorPlugin.cs +++ b/src/Empostor.Plugin.Narrator/NarratorPlugin.cs @@ -29,6 +29,8 @@ public sealed class NarratorPlugin : PluginBase, IPluginLanguageProvider { ["en"] = new Dictionary { + ["command.narrator.description"] = "Ask the narrator for AI-powered advice during a meeting.", + ["command.narrator.usage"] = "narrator ", ["narrator.meeting_only"] = "You can only use #narrator during a meeting.", ["narrator.disabled"] = "The narrator is currently disabled by the host.", ["narrator.status_enabled"] = "enabled", @@ -44,6 +46,8 @@ public sealed class NarratorPlugin : PluginBase, IPluginLanguageProvider }, ["zh_CN"] = new Dictionary { + ["command.narrator.description"] = "在会议中向旁白寻求AI建议。", + ["command.narrator.usage"] = "narrator <你要提的问题>", ["narrator.meeting_only"] = "只能在会议中使用 #narrator。", ["narrator.disabled"] = "房主已禁用旁白功能。", ["narrator.status_enabled"] = "已启用", @@ -59,6 +63,8 @@ public sealed class NarratorPlugin : PluginBase, IPluginLanguageProvider }, ["zh_TW"] = new Dictionary { + ["command.narrator.description"] = "在會議中向旁白尋求AI建議。", + ["command.narrator.usage"] = "narrator <你要提的問題>", ["narrator.meeting_only"] = "只能在會議中使用 #narrator。", ["narrator.disabled"] = "房主已停用旁白功能。", ["narrator.status_enabled"] = "已啟用", diff --git a/src/Empostor.Plugins.MapVote/MapVotePlugin.cs b/src/Empostor.Plugins.MapVote/MapVotePlugin.cs index 64da178..b8306cd 100644 --- a/src/Empostor.Plugins.MapVote/MapVotePlugin.cs +++ b/src/Empostor.Plugins.MapVote/MapVotePlugin.cs @@ -29,6 +29,8 @@ public sealed class MapVotePlugin : PluginBase, IPluginLanguageProvider { ["en"] = new Dictionary { + ["command.votemap.description"] = "Vote for the next map.", + ["command.votemap.usage"] = "votemap ", ["mapvote.disabled"] = "Map voting is currently disabled by the host.", ["mapvote.unknown_map"] = "Unknown map: {0}. Available: Skeld, Mira, Polus, Airship, Fungle", ["mapvote.voted"] = "{0} voted for {1}.", @@ -46,6 +48,8 @@ public sealed class MapVotePlugin : PluginBase, IPluginLanguageProvider }, ["zh_CN"] = new Dictionary { + ["command.votemap.description"] = "投票选择下一张地图。", + ["command.votemap.usage"] = "votemap <地图名>", ["mapvote.disabled"] = "投票选图功能已被房主禁用。", ["mapvote.unknown_map"] = "未知地图:{0}。可选:Skeld, Mira, Polus, Airship, Fungle", ["mapvote.voted"] = "{0} 投票选择了 {1}。", @@ -63,6 +67,8 @@ public sealed class MapVotePlugin : PluginBase, IPluginLanguageProvider }, ["zh_TW"] = new Dictionary { + ["command.votemap.description"] = "投票選擇下一張地圖。", + ["command.votemap.usage"] = "votemap <地圖名>", ["mapvote.disabled"] = "投票選圖功能已被房主停用。", ["mapvote.unknown_map"] = "未知地圖:{0}。可選:Skeld, Mira, Polus, Airship, Fungle", ["mapvote.voted"] = "{0} 投票選擇了 {1}。", diff --git a/src/Empostor.Plugins.Message/MessageCommand.cs b/src/Empostor.Plugins.Message/MessageCommand.cs index ca061b1..4d03524 100644 --- a/src/Empostor.Plugins.Message/MessageCommand.cs +++ b/src/Empostor.Plugins.Message/MessageCommand.cs @@ -31,12 +31,7 @@ public sealed class MessageCommand : ICommand } var targetFc = ctx.Args[0].Trim().ToUpperInvariant(); - var content = ctx.RawArgs!; - - // Remove the friend code prefix from the raw args to get the message content - var contentStart = ctx.RawArgs!.IndexOf(targetFc, StringComparison.OrdinalIgnoreCase); - if (contentStart >= 0) - content = ctx.RawArgs[(contentStart + ctx.Args[0].Length)..].Trim(); + var content = ctx.RawArgs!.Substring(ctx.Args[0].Length).Trim(); if (string.IsNullOrWhiteSpace(content)) { diff --git a/src/Empostor.Plugins.Message/MessageEventListener.cs b/src/Empostor.Plugins.Message/MessageEventListener.cs index 0354673..3bedb78 100644 --- a/src/Empostor.Plugins.Message/MessageEventListener.cs +++ b/src/Empostor.Plugins.Message/MessageEventListener.cs @@ -1,10 +1,7 @@ -using System; using System.Linq; using System.Text; using System.Threading.Tasks; using Empostor.Api.Events; -using Empostor.Api.Events.Client; -using Empostor.Api.Events.Game; namespace Empostor.Plugins.Message; @@ -17,46 +14,18 @@ public sealed class MessageEventListener : IEventListener _store = store; } - [EventListener] - public async ValueTask OnClientConnected(IClientConnectedEvent e) - { - await DeliverMessages(e.Client.FriendCode, e.Client.Language, null); - } - [EventListener] public async ValueTask OnPlayerJoined(IGamePlayerJoinedEvent e) { var fc = e.Player.Client.FriendCode; if (string.IsNullOrEmpty(fc)) return; - // Deliver any undelivered messages when player joins a game var messages = _store.TakeAll(fc); if (messages.Count == 0) return; var ctrl = e.Player.Character; if (ctrl == null) return; - await DeliverToPlayer(messages, ctrl, e.Player.Client.Name ?? "Player"); - } - - private async ValueTask DeliverMessages(string? friendCode, Api.Languages.Language language, Api.Innersloth.IInnerPlayerControl? ctrl) - { - if (string.IsNullOrEmpty(friendCode)) return; - - var messages = _store.TakeAll(friendCode); - if (messages.Count == 0) return; - - // Messages were already delivered via OnPlayerJoined with character reference - // OnClientConnected happens before player has a character, so store them back and wait for game join - foreach (var msg in messages) - _store.Add(msg); - } - - private async ValueTask DeliverToPlayer( - System.Collections.Generic.IReadOnlyList messages, - Empostor.Api.Innersloth.IInnerPlayerControl ctrl, - string playerName) - { var sb = new StringBuilder(); sb.AppendLine(string.Format("--- You have {0} pending message(s) ---", messages.Count)); diff --git a/src/Empostor.Plugins.Message/MessagePlugin.cs b/src/Empostor.Plugins.Message/MessagePlugin.cs index 4f9be35..b3ee88f 100644 --- a/src/Empostor.Plugins.Message/MessagePlugin.cs +++ b/src/Empostor.Plugins.Message/MessagePlugin.cs @@ -29,6 +29,8 @@ public sealed class MessagePlugin : PluginBase, IPluginLanguageProvider { ["en"] = new Dictionary { + ["command.msg.description"] = "Leave a message for a player by friend code.", + ["command.msg.usage"] = "msg ", ["message.usage"] = "Usage: #msg \nExample: #msg ABCDEFG Hey, let's play again!", ["message.sent"] = "Message sent to {0}.", ["message.too_long"] = "Message is too long. Max {0} characters.", @@ -41,6 +43,8 @@ public sealed class MessagePlugin : PluginBase, IPluginLanguageProvider }, ["zh_CN"] = new Dictionary { + ["command.msg.description"] = "通过好友代码给玩家留言。", + ["command.msg.usage"] = "msg <好友代码> <留言内容>", ["message.usage"] = "用法:#msg <好友代码> <留言内容>\n示例:#msg ABCDEFG 下次一起玩!", ["message.sent"] = "留言已发送给 {0}。", ["message.too_long"] = "留言内容过长,最多 {0} 个字符。", @@ -53,6 +57,8 @@ public sealed class MessagePlugin : PluginBase, IPluginLanguageProvider }, ["zh_TW"] = new Dictionary { + ["command.msg.description"] = "透過好友代碼給玩家留言。", + ["command.msg.usage"] = "msg <好友代碼> <留言內容>", ["message.usage"] = "用法:#msg <好友代碼> <留言內容>\n範例:#msg ABCDEFG 下次一起玩!", ["message.sent"] = "留言已發送給 {0}。", ["message.too_long"] = "留言內容過長,最多 {0} 個字元。", diff --git a/src/Empostor.Plugins.Message/packages.lock.json b/src/Empostor.Plugins.Message/packages.lock.json new file mode 100644 index 0000000..ac11d80 --- /dev/null +++ b/src/Empostor.Plugins.Message/packages.lock.json @@ -0,0 +1,137 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.6.1", + "contentHash": "vZsG2YILhthgRqO+ZVgRff4ZFKKTl0v7kqaVBLCtRvpREhfBP33pcWrdA3PRYgWuFL1RxiUFvjMUHTdBZlJcoA==" + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "9.0.6", + "contentHash": "0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "System.Diagnostics.DiagnosticSource": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "4.3.1-dev-02373", + "contentHash": "f0HNPaleOox5++8zqxAdi9afnUUkoznLOmd0ur/UnLbzl8bIaPyyBGOpbIJmC1kz2vHfkQ0fXh6KXMdlz7bcLQ==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "empostor.api": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )", + "Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )", + "NextFast.Hazel": "[1.0.2-dev, )", + "NextFast.Hazel.Abstractions": "[1.0.2-dev, )" + } + }, + "NextFast.Hazel": { + "type": "Project", + "dependencies": { + "BouncyCastle.Cryptography": "[2.6.1, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[9.0.6, )", + "Microsoft.Extensions.ObjectPool": "[8.0.1, )", + "NextFast.Hazel.Abstractions": "[1.0.2-dev, )", + "Serilog": "[4.3.1-dev-02373, )" + } + }, + "NextFast.Hazel.Abstractions": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs b/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs index 5e74294..5f6d108 100644 --- a/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs +++ b/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs @@ -29,18 +29,24 @@ public sealed class PlayerChannelPlugin : PluginBase, IPluginLanguageProvider { ["en"] = new Dictionary { + ["command.channel.description"] = "Send a message to your player channel.", + ["command.channel.usage"] = "channel ", ["playerchannel.unknown_friendcode"] = "[Refuse Channel] Unknown Friendcode", ["playerchannel.not_in_channel"] = "[Refuse Channel] Not in any channel", ["playerchannel.message_format"] = "[{0}] {1}", }, ["zh_CN"] = new Dictionary { + ["command.channel.description"] = "向你的玩家频道发送消息。", + ["command.channel.usage"] = "channel <消息>", ["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代码", ["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何频道中", ["playerchannel.message_format"] = "[{0}] {1}", }, ["zh_TW"] = new Dictionary { + ["command.channel.description"] = "向你的玩家頻道發送訊息。", + ["command.channel.usage"] = "channel <訊息>", ["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代碼", ["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何頻道中", ["playerchannel.message_format"] = "[{0}] {1}", diff --git a/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs b/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs index 5fa5646..4706299 100644 --- a/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs +++ b/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs @@ -29,6 +29,8 @@ public sealed class QqVerifyPlugin : PluginBase, IPluginLanguageProvider { ["en"] = new Dictionary { + ["command.verify.description"] = "Verify your QQ number for group access.", + ["command.verify.usage"] = "verify ", ["qqverify.chinese_only"] = "This command is only available for Chinese language players.\n此功能仅支持简体/繁体中文玩家使用。", ["qqverify.no_friendcode"] = "Unable to get your friend code. Please make sure you are logged in.", ["qqverify.usage_message"] = "Usage: #verify \nExample: #verify 123456789", @@ -36,6 +38,8 @@ public sealed class QqVerifyPlugin : PluginBase, IPluginLanguageProvider }, ["zh_CN"] = new Dictionary { + ["command.verify.description"] = "验证你的QQ号以获取群组访问权限。", + ["command.verify.usage"] = "verify ", ["qqverify.chinese_only"] = "此功能仅支持简体/繁体中文玩家使用。\nThis command is only available for Chinese language players.", ["qqverify.no_friendcode"] = "无法获取你的好友代码,请确保已登录账号。", ["qqverify.usage_message"] = "用法:#verify <你的QQ号>\n示例:#verify 123456789", @@ -43,6 +47,8 @@ public sealed class QqVerifyPlugin : PluginBase, IPluginLanguageProvider }, ["zh_TW"] = new Dictionary { + ["command.verify.description"] = "驗證你的QQ號以獲取群組存取權限。", + ["command.verify.usage"] = "verify ", ["qqverify.chinese_only"] = "此功能僅支援簡體/繁體中文玩家使用。\nThis command is only available for Chinese language players.", ["qqverify.no_friendcode"] = "無法獲取你的好友代碼,請確保已登入帳號。", ["qqverify.usage_message"] = "用法:#verify <你的QQ號>\n範例:#verify 123456789", diff --git a/src/Empostor.Server/Commands/Commands/HelpCommand.cs b/src/Empostor.Server/Commands/Commands/HelpCommand.cs index b72e223..4e223b3 100644 --- a/src/Empostor.Server/Commands/Commands/HelpCommand.cs +++ b/src/Empostor.Server/Commands/Commands/HelpCommand.cs @@ -34,8 +34,8 @@ public sealed class HelpCommand : ICommand } var sb = new StringBuilder(); - sb.AppendLine(ctx.GetString("command.help.entry").Format(target.Name, target.Description)); - sb.AppendLine(ctx.GetString("command.usage").Format(target.Usage)); + sb.AppendLine(ctx.GetString("command.help.entry").Format(target.Name, GetDesc(ctx, target))); + sb.AppendLine(ctx.GetString("command.usage").Format(GetUsage(ctx, target))); if (target.Aliases.Length > 0) sb.AppendLine(ctx.GetString("command.help.aliases") .Format(string.Join(", ", target.Aliases.Select(a => "#" + a)))); @@ -47,10 +47,24 @@ public sealed class HelpCommand : ICommand var list = new StringBuilder(ctx.GetString("command.help.list").Get() + "\n"); foreach (var cmd in _service.All.OrderBy(c => c.Name)) { - list.AppendLine(ctx.GetString("command.help.entry").Format(cmd.Name, cmd.Description)); + list.AppendLine(ctx.GetString("command.help.entry").Format(cmd.Name, GetDesc(ctx, cmd))); } await ctx.PlayerControl.SendChatToPlayerAsync(list.ToString().TrimEnd(), ctx.PlayerControl); return true; } + + private static string GetDesc(CommandContext ctx, ICommand cmd) + { + var key = $"command.{cmd.Name}.description"; + var result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? cmd.Description : result.Get(); + } + + private static string GetUsage(CommandContext ctx, ICommand cmd) + { + var key = $"command.{cmd.Name}.usage"; + var result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? cmd.Usage : result.Get(); + } } diff --git a/src/Empostor.sln b/src/Empostor.sln index 1def983..3084470 100644 --- a/src/Empostor.sln +++ b/src/Empostor.sln @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugin.Narrator", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.MapVote", "Empostor.Plugins.MapVote\Empostor.Plugins.MapVote.csproj", "{9D67CAC6-D296-404C-9189-2FF9B86D287C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.Message", "Empostor.Plugins.Message\Empostor.Plugins.Message.csproj", "{D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -241,6 +243,18 @@ Global {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x64.Build.0 = Release|Any CPU {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x86.ActiveCfg = Release|Any CPU {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x86.Build.0 = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|x64.ActiveCfg = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|x64.Build.0 = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|x86.ActiveCfg = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Debug|x86.Build.0 = Debug|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|Any CPU.Build.0 = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|x64.ActiveCfg = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|x64.Build.0 = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|x86.ActiveCfg = Release|Any CPU + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -257,7 +271,9 @@ Global {D4B6C8A2-5F3E-4C1D-9B7A-6E2F8A4C1D3F} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} {9D1FD146-E7D9-4AFF-9B8E-25E6021A69D7} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} {ADC4F301-51C0-45DB-B97A-BE3844DFD19B} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} + {FE6D40CC-4BE5-4E38-9DCA-E6832709FE3F} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} {9D67CAC6-D296-404C-9189-2FF9B86D287C} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} + {D05FE3E6-1BD9-4D32-89EB-5DE02A785EF8} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DAF5625C-7E46-4DCC-AF2C-11D20D965454}