diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a1bcf15..24fff81 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: "[BUG]" -labels: '' +labels: bug assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e0c0168..f455865 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: "[FEATURE]" -labels: '' +labels: enhancement assignees: '' --- diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 601780b..c0c3369 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -2,9 +2,9 @@ name: .NET on: push: - branches: [ "2.2" ] + branches: [ "2.4" ] pull_request: - branches: [ "2.2" ] + branches: [ "2.4" ] jobs: build: @@ -21,3 +21,34 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore + - name: Push package + run: | + # Grab some versioning information + commithash=$(git rev-parse --short HEAD) + currtime=$(date +%s) + echo "commit hash is $commithash" + echo "time is $currtime" + + # Enter project directory + cd Mirai.Net + + # Build package + dotnet restore + dotnet build + + # Pack a nuget package with all the default config + dotnet pack + + # Enter package output directory + cd bin/Debug + + # Getting file named with .nupkg extension + file="" + cdir=`ls ./*.nupkg` + for eachfile in $cdir + do + file=$eachfile + done + + # Push it to source "nuget" + dotnet nuget push $file --api-key ${{ secrets.NUGET_KEY }} --source "nuget" --skip-duplicate diff --git a/.gitignore b/.gitignore index add57be..efc7268 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ bin/ obj/ /packages/ riderModule.iml -/_ReSharper.Caches/ \ No newline at end of file +/_ReSharper.Caches/ +/.vs/ +/MSBuild_Logs diff --git a/.idea/.idea.Mirai.Net/.idea/encodings.xml b/.idea/.idea.Mirai.Net/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/.idea/.idea.Mirai.Net/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/.idea.Mirai.Net/.idea/indexLayout.xml b/.idea/.idea.Mirai.Net/.idea/indexLayout.xml deleted file mode 100644 index 7b08163..0000000 --- a/.idea/.idea.Mirai.Net/.idea/indexLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.Mirai.Net/.idea/inspectionProfiles/Project_Default.xml b/.idea/.idea.Mirai.Net/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 3018bff..0000000 --- a/.idea/.idea.Mirai.Net/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/.idea.Mirai.Net/.idea/projectSettingsUpdater.xml b/.idea/.idea.Mirai.Net/.idea/projectSettingsUpdater.xml deleted file mode 100644 index 4bb9f4d..0000000 --- a/.idea/.idea.Mirai.Net/.idea/projectSettingsUpdater.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/.idea.Mirai.Net/.idea/statistic.xml b/.idea/.idea.Mirai.Net/.idea/statistic.xml deleted file mode 100644 index d588f64..0000000 --- a/.idea/.idea.Mirai.Net/.idea/statistic.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/.idea.Mirai.Net/.idea/vcs.xml b/.idea/.idea.Mirai.Net/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/.idea.Mirai.Net/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Mirai.Net.Test/Mirai.Net.Test.csproj b/Mirai.Net.Test/Mirai.Net.Test.csproj index 3ed0572..a10ab02 100644 --- a/Mirai.Net.Test/Mirai.Net.Test.csproj +++ b/Mirai.Net.Test/Mirai.Net.Test.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 latest @@ -11,7 +11,7 @@ - + diff --git a/Mirai.Net.Test/Module1.cs b/Mirai.Net.Test/Module1.cs index be1f620..0c53a6a 100644 --- a/Mirai.Net.Test/Module1.cs +++ b/Mirai.Net.Test/Module1.cs @@ -1,10 +1,8 @@ using System; using System.Linq; -using Mirai.Net.Data.Commands; using Mirai.Net.Data.Messages; using Mirai.Net.Data.Messages.Concretes; using Mirai.Net.Data.Messages.Receivers; -using Mirai.Net.Data.Modules; using Mirai.Net.Modules; using Mirai.Net.Utils.Scaffolds; @@ -15,23 +13,21 @@ public class Module1 : IModule public async void Execute(MessageReceiverBase @base) { var receiver = @base.Concretize(); + if (receiver.Sender.Id != "2933170747") + { + return; + } var plain = receiver.MessageChain.GetPlainMessage(); - - if (plain.CanExecute()) + if (plain == "/off") { - var command = receiver.MessageChain.GetPlainMessage().ParseCommand(); - await receiver.SendMessageAsync(command.Value); + IsEnable = false; + await receiver.SendMessageAsync("Current module will be turned off"); + return; } + await receiver.SendMessageAsync(plain); } public bool? IsEnable { get; set; } - - [CommandEntity(Name = "say", Identifier = "/")] - class SayCommand - { - [CommandArgument(Name = "v", IsRequired = true)] - public string Value { get; set; } - } } } \ No newline at end of file diff --git a/Mirai.Net.Test/Program.cs b/Mirai.Net.Test/Program.cs index cce0478..94af642 100644 --- a/Mirai.Net.Test/Program.cs +++ b/Mirai.Net.Test/Program.cs @@ -1,25 +1,21 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using System.Net; using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; -using Flurl; -using Mirai.Net.Data.Commands; -using Mirai.Net.Data.Events.Concretes.Message; +using Manganese.Array; +using Manganese.Text; +using Mirai.Net.Data.Events.Concretes.Request; using Mirai.Net.Data.Messages; using Mirai.Net.Data.Messages.Concretes; using Mirai.Net.Data.Messages.Receivers; -using Mirai.Net.Data.Modules; using Mirai.Net.Data.Sessions; -using Mirai.Net.Data.Shared; using Mirai.Net.Sessions; using Mirai.Net.Sessions.Http.Managers; -using Mirai.Net.Utils; +using Mirai.Net.Utils.Internal; using Mirai.Net.Utils.Scaffolds; +using Newtonsoft.Json; namespace Mirai.Net.Test { @@ -29,43 +25,24 @@ private static async Task Main() { var exit = new ManualResetEvent(false); - using var bot = new MiraiBot + var bot = new MiraiBot { - Address = "localhost:8080", + Address = new ConnectConfig + { + HttpAddress = new ConnectConfig.AdapterConfig("localhost", "8080"), + WebsocketAddress = new ConnectConfig.AdapterConfig("localhost", "8080") + }, VerifyKey = "1145141919810", - QQ = "2672886221" + QQ = "1590454991" }; await bot.LaunchAsync(); var modules = new Module1().GetModules(); - - bot.MessageReceived - .OfType() - .Subscribe(async r => - { - modules.SubscribeModule(r); + bot.MessageReceived.WithModules(modules); - // await r.SendMessageAsync(r.MessageChain.Append()); - }); - + Console.WriteLine("launched"); exit.WaitOne(); } } - - [CommandEntity(Name = "test")] - class TestCommand - { - [CommandArgument(Name = "arg1")] - public string Arg1 { get; set; } - - [CommandArgument(Name = "arg2", Default = 114514)] - public int Arg2 { get; set; } - - [CommandArgument(Name = "arg3")] - public string[] Arg3 { get; set; } - - [CommandArgument(Name = "arg4")] - public bool Arg4 { get; set; } - } } \ No newline at end of file diff --git a/Mirai.Net.UnitTest/CommandShould.cs b/Mirai.Net.UnitTest/CommandShould.cs index 230578e..48ba57a 100644 --- a/Mirai.Net.UnitTest/CommandShould.cs +++ b/Mirai.Net.UnitTest/CommandShould.cs @@ -1,5 +1,4 @@ -using Mirai.Net.Data.Commands; -using Mirai.Net.Utils.Scaffolds; + using Xunit; namespace Mirai.Net.UnitTest; @@ -18,7 +17,7 @@ public class CommandShould [InlineData("/test-arg4 awd awd -arg5 114.514")] public void CanExecute(string command) { - Assert.True(command.CanExecute()); + } [Theory] @@ -30,39 +29,13 @@ public void CanExecute(string command) [InlineData("/test -arg2 a -arg4 -arg5 awd")] public void CantExecute(string command) { - Assert.False(command.CanExecute()); - } - - [CommandEntity(Name = "test")] - class TestClass - { - [CommandArgument(Name = "arg1")] - public string Arg1 { get; set; } - - [CommandArgument(Name = "arg2")] - public int Arg2 { get; set; } - - [CommandArgument(Name = "arg3")] - public string[] Arg3 { get; set; } - - [CommandArgument(Name = "arg4")] - public bool Arg4 { get; set; } - [CommandArgument(Name = "arg5")] - public double Arg5 { get; set; } } - - [CommandEntity(Name = "test")] - class DefaultClass - { - [CommandArgument(Name = "arg4", IsRequired = true)] - public bool Arg4 { get; set; } - } - + [Fact] public void CantExecuteDefault() { - Assert.False("/test -arg4".CanExecute()); + } } \ No newline at end of file diff --git a/Mirai.Net.UnitTest/Mirai.Net.UnitTest.csproj b/Mirai.Net.UnitTest/Mirai.Net.UnitTest.csproj index bdf3e4f..f124b77 100644 --- a/Mirai.Net.UnitTest/Mirai.Net.UnitTest.csproj +++ b/Mirai.Net.UnitTest/Mirai.Net.UnitTest.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 false @@ -10,13 +10,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Mirai.Net/Data/Commands/CommandArgumentAttribute.cs b/Mirai.Net/Data/Commands/CommandArgumentAttribute.cs deleted file mode 100644 index 46f0453..0000000 --- a/Mirai.Net/Data/Commands/CommandArgumentAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace Mirai.Net.Data.Commands; - -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] -public class CommandArgumentAttribute : Attribute -{ - /// - /// 参数名,比如"arg1"之于"/test -arg1" - /// - public string Name { get; set; } - - /// - /// 参数是否为必须,如果是必须参数不填会报错,与Default冲突,二者选其一 - /// - public bool IsRequired { get; set; } = false; - // - // /// - // /// 是否为默认参数,默认参数即为不需要指定参数名的参数,默认参数只会解析紧随命令标识其后的参数,与IsRequired冲突,二者选其一 - // /// - // public bool IsDefault { get; set; } - - /// - /// 参数的默认值,与IsRequired冲突,二者选其一 - /// - public object Default { get; set; } = null; -} \ No newline at end of file diff --git a/Mirai.Net/Data/Commands/CommandEntityAttribute.cs b/Mirai.Net/Data/Commands/CommandEntityAttribute.cs deleted file mode 100644 index 02a0997..0000000 --- a/Mirai.Net/Data/Commands/CommandEntityAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace Mirai.Net.Data.Commands; - -[AttributeUsage(AttributeTargets.Class)] -public class CommandEntityAttribute : Attribute -{ - /// - /// 此命令的正式名称,比如"test"之于"/test" - /// - public string Name { get; set; } - - /// - /// 此命令的别名,比如"/t"和"/tst"等价于"/test" - /// - public string[] Alias { get; set; } = { }; - - /// - /// 命令参数分隔符,比如"-"之于"/test -arg1 xxx -arg2 yyy"",默认为"-" - /// - public string Separator { get; set; } = "-"; - - /// - /// 命令的标识符,比如"/"之于"/test",默认为"/" - /// - public string Identifier { get; set; } = "/"; -} \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Bot/DroppedEvent.cs b/Mirai.Net/Data/Events/Concretes/Bot/DroppedEvent.cs index 250e84f..8cedb19 100644 --- a/Mirai.Net/Data/Events/Concretes/Bot/DroppedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Bot/DroppedEvent.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Events.Concretes.Bot; -public class DroppedEvent : EventBase +/// +/// Bot被服务器断开或因网络问题而掉线 +/// +public record DroppedEvent : EventBase { + /// + /// Bot的QQ号 + /// [JsonProperty("qq")] public string QQ { get; private set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Dropped; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Bot/OfflineEvent.cs b/Mirai.Net/Data/Events/Concretes/Bot/OfflineEvent.cs index dab5201..a2a79b4 100644 --- a/Mirai.Net/Data/Events/Concretes/Bot/OfflineEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Bot/OfflineEvent.cs @@ -3,11 +3,17 @@ namespace Mirai.Net.Data.Events.Concretes.Bot; /// -/// Bot自身事件 +/// Bot主动离线 /// -public class OfflineEvent : EventBase +public record OfflineEvent : EventBase { + /// + /// Bot的QQ号 + /// [JsonProperty("qq")] public string QQ { get; private set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Offline; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Bot/OfflineForceEvent.cs b/Mirai.Net/Data/Events/Concretes/Bot/OfflineForceEvent.cs index c35e68c..726fec7 100644 --- a/Mirai.Net/Data/Events/Concretes/Bot/OfflineForceEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Bot/OfflineForceEvent.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Events.Concretes.Bot; -public class OfflineForceEvent : EventBase +/// +/// Bot被挤下线 +/// +public record OfflineForceEvent : EventBase { + /// + /// Bit的QQ号 + /// [JsonProperty("qq")] public string QQ { get; private set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.OfflineForce; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Bot/OnlineEvent.cs b/Mirai.Net/Data/Events/Concretes/Bot/OnlineEvent.cs index e86366d..205dbe9 100644 --- a/Mirai.Net/Data/Events/Concretes/Bot/OnlineEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Bot/OnlineEvent.cs @@ -5,9 +5,15 @@ namespace Mirai.Net.Data.Events.Concretes.Bot; /// /// Bot自身事件 /// -public class OnlineEvent : EventBase +public record OnlineEvent : EventBase { + /// + /// Bot的QQ号 + /// [JsonProperty("qq")] public string QQ { get; private set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Online; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Bot/ReconnectedEvent.cs b/Mirai.Net/Data/Events/Concretes/Bot/ReconnectedEvent.cs index 9dd6aab..36ac2e8 100644 --- a/Mirai.Net/Data/Events/Concretes/Bot/ReconnectedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Bot/ReconnectedEvent.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Events.Concretes.Bot; -public class ReconnectedEvent : EventBase +/// +/// Bot主动重新登录 +/// +public record ReconnectedEvent : EventBase { + /// + /// Bot的QQ号 + /// [JsonProperty("qq")] public string QQ { get; private set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Reconnected; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Friend/FriendInputStatusChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Friend/FriendInputStatusChangedEvent.cs index 4bee85d..8d0c5ba 100644 --- a/Mirai.Net/Data/Events/Concretes/Friend/FriendInputStatusChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Friend/FriendInputStatusChangedEvent.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Events.Concretes.Friend; -public class FriendInputStatusChangedEvent : EventBase +/// +/// 好友输入状态改变 +/// +public record FriendInputStatusChangedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.FriendInputStatusChanged; /// diff --git a/Mirai.Net/Data/Events/Concretes/Friend/FriendNickChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Friend/FriendNickChangedEvent.cs index 2178801..07cf7cf 100644 --- a/Mirai.Net/Data/Events/Concretes/Friend/FriendNickChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Friend/FriendNickChangedEvent.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Events.Concretes.Friend; -public class FriendNickChangedEvent : EventBase +/// +/// 好友昵称改变 +/// +public record FriendNickChangedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.FriendNickChanged; /// diff --git a/Mirai.Net/Data/Events/Concretes/Friend/FriendRecalledEvent.cs b/Mirai.Net/Data/Events/Concretes/Friend/FriendRecalledEvent.cs index c2eecfd..4f2ee4e 100644 --- a/Mirai.Net/Data/Events/Concretes/Friend/FriendRecalledEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Friend/FriendRecalledEvent.cs @@ -2,8 +2,15 @@ namespace Mirai.Net.Data.Events.Concretes.Friend; -public class FriendRecalledEvent : EventBase + +/// +/// 好友撤回了某条消息 +/// +public record FriendRecalledEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.FriendRecalled; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/DisbandedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/DisbandedEvent.cs new file mode 100644 index 0000000..e8d2ab6 --- /dev/null +++ b/Mirai.Net/Data/Events/Concretes/Group/DisbandedEvent.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mirai.Net.Data.Events.Concretes.Group +{ + + /// + /// Bot因群主解散群而退出群 + /// + public record DisbandedEvent : EventBase + { + + /// + /// 事件类型 + /// + public override Events Type { get; set; } = Events.Disbanded; + + /// + /// Bot所在被解散的群的信息 + /// + [JsonProperty("group")] + public Shared.Group Group { get; set; } + + /// + /// Bot离开群后获取操作人的 Member 对象 + /// + [JsonProperty("operator")] + public Shared.Member Operator { get; set; } + + } +} diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedAnonymousChatEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedAnonymousChatEvent.cs index 9ad4a52..d287dd4 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedAnonymousChatEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedAnonymousChatEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupAllowedAnonymousChatEvent : GroupSettingChangedEventBase +/// +/// 群内是否允许匿名聊天的状态改变 +/// +public record GroupAllowedAnonymousChatEvent : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupAllowedAnonymousChat; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedConfessTalkChanged.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedConfessTalkChanged.cs index 5bea7b9..23ad734 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedConfessTalkChanged.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedConfessTalkChanged.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupAllowedConfessTalkChanged : GroupSettingChangedEventBase +/// +/// 群内是否允许坦白说的状态发生改变 +/// +public record GroupAllowedConfessTalkChanged : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupAllowedConfessTalk; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedMemberInviteEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedMemberInviteEvent.cs index b670dff..5d68eff 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedMemberInviteEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupAllowedMemberInviteEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupAllowedMemberInviteEvent : GroupSettingChangedEventBase +/// +/// 群内是否允许群员邀请新成员的状态发生改变 +/// +public record GroupAllowedMemberInviteEvent : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupAllowedMemberInvite; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupEntranceAnnouncementChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupEntranceAnnouncementChangedEvent.cs index b211ab9..7124f47 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupEntranceAnnouncementChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupEntranceAnnouncementChangedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupEntranceAnnouncementChangedEvent : GroupSettingChangedEventBase +/// +/// 入群公告发生改变 +/// +public record GroupEntranceAnnouncementChangedEvent : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupEntranceAnnouncementChanged; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupMemberSettingChangedEventBase.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupMemberSettingChangedEventBase.cs index 421827f..a7ade83 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupMemberSettingChangedEventBase.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupMemberSettingChangedEventBase.cs @@ -3,10 +3,20 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public abstract class GroupMemberSettingChangedEventBase : EventBase +/// +/// 群成员设置改变基类 +/// +/// +public abstract record GroupMemberSettingChangedEventBase : EventBase { + /// + /// 原来的值 + /// [JsonProperty("origin")] public T Origin { get; set; } + /// + /// 当前的值 + /// [JsonProperty("current")] public T Current { get; set; } /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupMessageRecalledEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupMessageRecalledEvent.cs index 93c5e3d..8ab28ee 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupMessageRecalledEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupMessageRecalledEvent.cs @@ -3,8 +3,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupMessageRecalledEvent : EventBase +/// +/// 某条群消息被撤回 +/// +public record GroupMessageRecalledEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupMessageRecalled; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupMutedAllEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupMutedAllEvent.cs index 5eaa1cb..78b04c1 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupMutedAllEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupMutedAllEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupMutedAllEvent : GroupSettingChangedEventBase +/// +/// 某群开启/关闭了全员禁言 +/// +public record GroupMutedAllEvent : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupMutedAll; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupNameChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupNameChangedEvent.cs index 0b81e35..3b0415a 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupNameChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupNameChangedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class GroupNameChangedEvent : GroupSettingChangedEventBase +/// +/// 某群的改了群名 +/// +public record GroupNameChangedEvent : GroupSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.GroupNameChanged; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/GroupSettingChangedEventBase.cs b/Mirai.Net/Data/Events/Concretes/Group/GroupSettingChangedEventBase.cs index d771260..0d14687 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/GroupSettingChangedEventBase.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/GroupSettingChangedEventBase.cs @@ -3,7 +3,11 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public abstract class GroupSettingChangedEventBase : EventBase +/// +/// 群设置改变基类 +/// +/// +public abstract record GroupSettingChangedEventBase : EventBase { /// /// 原来的 diff --git a/Mirai.Net/Data/Events/Concretes/Group/JoinedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/JoinedEvent.cs index 60899f4..c861b64 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/JoinedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/JoinedEvent.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class JoinedEvent : EventBase +/// +/// Bot加入了一个新群 +/// +public record JoinedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Joined; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/KickedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/KickedEvent.cs index fafcdd7..e05a504 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/KickedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/KickedEvent.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class KickedEvent : EventBase +/// +/// Bot被某群踢了 +/// +public record KickedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Kicked; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/LeftEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/LeftEvent.cs index 46fddff..44b13c2 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/LeftEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/LeftEvent.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class LeftEvent : EventBase +/// +/// Bot主动离开了某群 +/// +public record LeftEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Left; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberCardChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberCardChangedEvent.cs index 8bd42bd..12c29d3 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberCardChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberCardChangedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberCardChangedEvent : GroupMemberSettingChangedEventBase +/// +/// 某人的群名片改变 +/// +public record MemberCardChangedEvent : GroupMemberSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberCardChanged; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberHonorChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberHonorChangedEvent.cs index 2bf6408..1eb6a49 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberHonorChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberHonorChangedEvent.cs @@ -1,24 +1,44 @@ -using System.ComponentModel; -using System.Runtime.Serialization; -using Mirai.Net.Data.Shared; +using Mirai.Net.Data.Shared; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using System.ComponentModel; +using System.Runtime.Serialization; namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberHonorChangedEvent : EventBase +/// +/// 群员称号改变 +/// +public record MemberHonorChangedEvent : EventBase { + /// + /// 改变类型 + /// public enum MemberHonorAction { - [EnumMember(Value = "achieve")] [Description("achieve")] + /// + /// 得到称号 + /// + [EnumMember(Value = "achieve")] + [Description("achieve")] Achieve, - [EnumMember(Value = "lose")] [Description("lose")] + /// + /// 失去称号 + /// + [EnumMember(Value = "lose")] + [Description("lose")] Lose } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberHonorChanged; + /// + /// 当事人 + /// [JsonProperty("member")] public Member Member { get; set; } /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberJoinedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberJoinedEvent.cs index e5fc1b8..30cd4b3 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberJoinedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberJoinedEvent.cs @@ -3,9 +3,23 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberJoinedEvent : EventBase +/// +/// 新成员入群 +/// +public record MemberJoinedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberJoined; - [JsonProperty("member")] public Member Member { get; set; } + /// + /// 当事人 + /// + [JsonProperty("member")] public Member Member { get; set; } + + /// + /// 肇事者 + /// + [JsonProperty("invitor")] public Member Invitor { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberKickedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberKickedEvent.cs index 0ea065c..09d5ba7 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberKickedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberKickedEvent.cs @@ -3,11 +3,23 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberKickedEvent : EventBase +/// +/// 某群员被踢出群 +/// +public record MemberKickedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberKicked; + /// + /// 当事人 + /// [JsonProperty("member")] public Member Member { get; set; } + /// + /// 踢人者 + /// [JsonProperty("operator")] public Member Operator { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberLeftEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberLeftEvent.cs index aa71856..f9810d9 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberLeftEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberLeftEvent.cs @@ -3,9 +3,18 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberLeftEvent : EventBase +/// +/// 群成员离开群 +/// +public record MemberLeftEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberLeft; + /// + /// 当事人 + /// [JsonProperty("member")] public Member Member { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberMutedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberMutedEvent.cs index 79da120..d9b5fec 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberMutedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberMutedEvent.cs @@ -3,13 +3,28 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberMutedEvent : EventBase +/// +/// 某群员被禁言 +/// +public record MemberMutedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberMuted; - [JsonProperty("durationSeconds")] public string Period { get; set; } + /// + /// 禁言时长,单位为秒 + /// + [JsonProperty("durationSeconds")] public int Period { get; set; } + /// + /// 当事人 + /// [JsonProperty("Member")] public Member Member { get; set; } + /// + /// 禁言者 + /// [JsonProperty("Operator")] public Member Operator { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberPermissionChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberPermissionChangedEvent.cs index 266f4c4..283a44e 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberPermissionChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberPermissionChangedEvent.cs @@ -2,7 +2,13 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberPermissionChangedEvent : GroupMemberSettingChangedEventBase +/// +/// 某群员权限改变,操作者一定是群主 +/// +public record MemberPermissionChangedEvent : GroupMemberSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberPermissionChanged; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberTitleChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberTitleChangedEvent.cs index 0995d3a..1b71a9a 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberTitleChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberTitleChangedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberTitleChangedEvent : GroupMemberSettingChangedEventBase +/// +/// 群头衔改动(只有群主有操作限权) +/// +public record MemberTitleChangedEvent : GroupMemberSettingChangedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberTitleChanged; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MemberUnmutedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MemberUnmutedEvent.cs index cb60a99..1d83a5e 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MemberUnmutedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MemberUnmutedEvent.cs @@ -3,11 +3,23 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MemberUnmutedEvent : EventBase +/// +/// 群员被解除禁言 +/// +public record MemberUnmutedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.MemberUnmuted; + /// + /// 当事人 + /// [JsonProperty("Member")] public Member Member { get; set; } + /// + /// 解除禁言的人 + /// [JsonProperty("Operator")] public Member Operator { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/MutedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/MutedEvent.cs index 678966c..ee6f30f 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/MutedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/MutedEvent.cs @@ -3,8 +3,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class MutedEvent : EventBase +/// +/// Bot被禁言 +/// +public record MutedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Muted; /// diff --git a/Mirai.Net/Data/Events/Concretes/Group/PermissionChangedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/PermissionChangedEvent.cs index 7eaec96..55d8066 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/PermissionChangedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/PermissionChangedEvent.cs @@ -4,17 +4,32 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class PermissionChangedEvent : EventBase +/// +/// Bot在群内的权限改变,操作者一定是群主 +/// +public record PermissionChangedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.PermissionChanged; + /// + /// 原来的权限 + /// [JsonProperty("origin")] [JsonConverter(typeof(StringEnumConverter))] public Permissions Origin { get; set; } + /// + /// 目前的权限 + /// [JsonProperty("current")] [JsonConverter(typeof(StringEnumConverter))] public Permissions Current { get; set; } + /// + /// 事发群 + /// [JsonProperty("group")] public Shared.Group Group { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Group/UnmutedEvent.cs b/Mirai.Net/Data/Events/Concretes/Group/UnmutedEvent.cs index 2640dda..d39fdff 100644 --- a/Mirai.Net/Data/Events/Concretes/Group/UnmutedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Group/UnmutedEvent.cs @@ -3,8 +3,14 @@ namespace Mirai.Net.Data.Events.Concretes.Group; -public class UnmutedEvent : EventBase +/// +/// Bot被解除禁言 +/// +public record UnmutedEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Unmuted; /// diff --git a/Mirai.Net/Data/Events/Concretes/Message/AtEvent.cs b/Mirai.Net/Data/Events/Concretes/Message/AtEvent.cs index d887936..a1c499a 100644 --- a/Mirai.Net/Data/Events/Concretes/Message/AtEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Message/AtEvent.cs @@ -1,11 +1,19 @@ using Mirai.Net.Data.Messages.Receivers; -using Mirai.Net.Data.Shared; namespace Mirai.Net.Data.Events.Concretes.Message; -public class AtEvent : EventBase +/// +/// Bot被人at +/// +public record AtEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.At; + /// + /// 被at的那条消息容器 + /// public GroupMessageReceiver Receiver { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Message/NudgeEvent.cs b/Mirai.Net/Data/Events/Concretes/Message/NudgeEvent.cs index 97ccc17..4b1677c 100644 --- a/Mirai.Net/Data/Events/Concretes/Message/NudgeEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Message/NudgeEvent.cs @@ -2,24 +2,54 @@ namespace Mirai.Net.Data.Events.Concretes.Message; -public class NudgeEvent : EventBase +/// +/// 某人被戳了一戳 +/// +public record NudgeEvent : EventBase { + /// + /// 是谁戳的 + /// [JsonProperty("fromId")] public string FromId { get; set; } + /// + /// 谁被戳了 + /// [JsonProperty("target")] public string Target { get; set; } + /// + /// 怎么戳的 + /// [JsonProperty("action")] public string Action { get; set; } + /// + /// 自定义的怎么戳 + /// [JsonProperty("suffix")] public string Suffix { get; set; } + /// + /// 来源 + /// [JsonProperty("subject")] public NudgeSubject Subject { get; set; } + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.Nudged; + /// + /// 来源 + /// public class NudgeSubject { + /// + /// 来源的QQ号(好友)或群号 + /// [JsonProperty("id")] public string Id { get; set; } + /// + /// 来源的类型,"Friend"或"Group" + /// [JsonProperty("kind")] public string Kind { get; set; } } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOfflineEvent.cs b/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOfflineEvent.cs index 7ff2416..13fbcf9 100644 --- a/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOfflineEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOfflineEvent.cs @@ -2,10 +2,19 @@ namespace Mirai.Net.Data.Events.Concretes.OtherClient; -public class OtherClientOfflineEvent : EventBase +/// +/// 其它客户端离线 +/// +public record OtherClientOfflineEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.OtherClientOffline; - + + /// + /// 啥客户端 + /// [JsonProperty("client")] - public Shared.OtherClient Client {get; set;} + public Shared.OtherClient Client { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOnlineEvent.cs b/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOnlineEvent.cs index 91e0b11..daf750c 100644 --- a/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOnlineEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/OtherClient/OtherClientOnlineEvent.cs @@ -2,13 +2,25 @@ namespace Mirai.Net.Data.Events.Concretes.OtherClient; -public class OtherClientOnlineEvent : EventBase +/// +/// 其它客户端上线 +/// +public record OtherClientOnlineEvent : EventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.OtherClientOnline; + /// + /// 啥客户端 + /// [JsonProperty("client")] public Shared.OtherClient Client { get; set; } - + + /// + /// 详细设备类型 + /// [JsonProperty("kind")] - public string Kind {get; set;} + public long Kind { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Request/NewFriendRequestedEvent.cs b/Mirai.Net/Data/Events/Concretes/Request/NewFriendRequestedEvent.cs index b63d89a..f844784 100644 --- a/Mirai.Net/Data/Events/Concretes/Request/NewFriendRequestedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Request/NewFriendRequestedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Request; -public class NewFriendRequestedEvent : RequestedEventBase +/// +/// 新的好友请求 +/// +public record NewFriendRequestedEvent : RequestedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.NewFriendRequested; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Request/NewInvitationRequestedEvent.cs b/Mirai.Net/Data/Events/Concretes/Request/NewInvitationRequestedEvent.cs index 516b0b1..7f83e8f 100644 --- a/Mirai.Net/Data/Events/Concretes/Request/NewInvitationRequestedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Request/NewInvitationRequestedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Request; -public class NewInvitationRequestedEvent : RequestedEventBase +/// +/// 新的邀请(邀请bot加入某群) +/// +public record NewInvitationRequestedEvent : RequestedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.NewInvitationRequested; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Request/NewMemberRequestedEvent.cs b/Mirai.Net/Data/Events/Concretes/Request/NewMemberRequestedEvent.cs index 88c2867..609035b 100644 --- a/Mirai.Net/Data/Events/Concretes/Request/NewMemberRequestedEvent.cs +++ b/Mirai.Net/Data/Events/Concretes/Request/NewMemberRequestedEvent.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Events.Concretes.Request; -public class NewMemberRequestedEvent : RequestedEventBase +/// +/// 新成员申请 +/// +public record NewMemberRequestedEvent : RequestedEventBase { + /// + /// 事件类型 + /// public override Events Type { get; set; } = Events.NewMemberRequested; } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/Request/RequestedEventBase.cs b/Mirai.Net/Data/Events/Concretes/Request/RequestedEventBase.cs index 1396279..0df65f2 100644 --- a/Mirai.Net/Data/Events/Concretes/Request/RequestedEventBase.cs +++ b/Mirai.Net/Data/Events/Concretes/Request/RequestedEventBase.cs @@ -2,15 +2,33 @@ namespace Mirai.Net.Data.Events.Concretes.Request; -public abstract class RequestedEventBase : EventBase +/// +/// 请求事件基类 +/// +public record class RequestedEventBase : EventBase { + /// + /// 事件id + /// [JsonProperty("eventId")] public string EventId { get; set; } + /// + /// 来自谁 + /// [JsonProperty("fromId")] public string FromId { get; set; } + /// + /// 群号 + /// [JsonProperty("groupId")] public string GroupId { get; set; } + /// + /// 昵称 + /// [JsonProperty("nick")] public string Nick { get; set; } + /// + /// 附加消息 + /// [JsonProperty("message")] public string Message { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Concretes/UnknownEvent.cs b/Mirai.Net/Data/Events/Concretes/UnknownEvent.cs new file mode 100644 index 0000000..c6cc438 --- /dev/null +++ b/Mirai.Net/Data/Events/Concretes/UnknownEvent.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Events.Concretes; + +/// +/// 未知的事件 +/// +public record UnknownEvent : EventBase +{ + /// + /// + /// + [JsonIgnore] public override Events Type { get; set; } = Events.Unknown; + + /// + /// 原json + /// + [JsonIgnore] + public string RawJson { get; set; } + + /// + /// + /// + public UnknownEvent() + { + } + + /// + /// + /// + /// + public UnknownEvent(string rawJson) + { + RawJson = rawJson; + } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Events/EventBase.cs b/Mirai.Net/Data/Events/EventBase.cs index 511a9e7..d71c91e 100644 --- a/Mirai.Net/Data/Events/EventBase.cs +++ b/Mirai.Net/Data/Events/EventBase.cs @@ -1,19 +1,32 @@ -using AHpx.Extensions.StringExtensions; +using Manganese.Text; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Mirai.Net.Data.Events; -public class EventBase +/// +/// 事件基类 +/// +public record EventBase { + /// + /// 你又看不到这个 + /// protected EventBase() { } + /// + /// 事件类型 + /// [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] public virtual Events Type { get; set; } + /// + /// ToString实际上是ToJsonString也就是说会被序列化成JSON文本 + /// + /// public override string ToString() { return this.ToJsonString(); diff --git a/Mirai.Net/Data/Events/EventTargets.cs b/Mirai.Net/Data/Events/EventTargets.cs deleted file mode 100644 index 36fdfe1..0000000 --- a/Mirai.Net/Data/Events/EventTargets.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Mirai.Net.Data.Events; - -public enum EventTargets -{ - Bot, - Friend, - Group, - Request -} \ No newline at end of file diff --git a/Mirai.Net/Data/Events/Events.cs b/Mirai.Net/Data/Events/Events.cs index 81ddf2b..ae3077f 100644 --- a/Mirai.Net/Data/Events/Events.cs +++ b/Mirai.Net/Data/Events/Events.cs @@ -3,221 +3,286 @@ namespace Mirai.Net.Data.Events; +/// +/// 事件类型 +/// public enum Events { /// /// Bot登录成功 /// - [Description("BotOnlineEvent")] [EnumMember(Value = "BotOnlineEvent")] + [Description("BotOnlineEvent")] + [EnumMember(Value = "BotOnlineEvent")] Online, /// /// Bot主动离线 /// - [Description("BotOfflineEventActive")] [EnumMember(Value = "BotOfflineEventActive")] + [Description("BotOfflineEventActive")] + [EnumMember(Value = "BotOfflineEventActive")] Offline, /// /// Bot被挤下线 /// - [Description("BotOfflineEventForce")] [EnumMember(Value = "BotOfflineEventForce")] + [Description("BotOfflineEventForce")] + [EnumMember(Value = "BotOfflineEventForce")] OfflineForce, /// /// Bot被服务器断开或因网络问题而掉线 /// - [Description("BotOfflineEventDropped")] [EnumMember(Value = "BotOfflineEventDropped")] + [Description("BotOfflineEventDropped")] + [EnumMember(Value = "BotOfflineEventDropped")] Dropped, /// /// Bot主动重新登录 /// - [Description("BotReloginEvent")] [EnumMember(Value = "BotReloginEvent")] + [Description("BotReloginEvent")] + [EnumMember(Value = "BotReloginEvent")] Reconnected, /// /// 好友输入状态改变 /// - [Description("FriendInputStatusChangedEvent")] [EnumMember(Value = "FriendInputStatusChangedEvent")] + [Description("FriendInputStatusChangedEvent")] + [EnumMember(Value = "FriendInputStatusChangedEvent")] FriendInputStatusChanged, /// /// 好友昵称改变 /// - [Description("FriendNickChangedEvent")] [EnumMember(Value = "FriendNickChangedEvent")] + [Description("FriendNickChangedEvent")] + [EnumMember(Value = "FriendNickChangedEvent")] FriendNickChanged, /// /// Bot在群里的权限被改变. 操作人一定是群主 /// - [Description("BotGroupPermissionChangeEvent")] [EnumMember(Value = "BotGroupPermissionChangeEvent")] + [Description("BotGroupPermissionChangeEvent")] + [EnumMember(Value = "BotGroupPermissionChangeEvent")] PermissionChanged, /// /// Bot被禁言 /// - [Description("BotMuteEvent")] [EnumMember(Value = "BotMuteEvent")] + [Description("BotMuteEvent")] + [EnumMember(Value = "BotMuteEvent")] Muted, /// /// Bot被取消禁言 /// - [Description("BotUnmuteEvent")] [EnumMember(Value = "BotUnmuteEvent")] + [Description("BotUnmuteEvent")] + [EnumMember(Value = "BotUnmuteEvent")] Unmuted, /// /// Bot加入了一个新群 /// - [Description("BotJoinGroupEvent")] [EnumMember(Value = "BotJoinGroupEvent")] + [Description("BotJoinGroupEvent")] + [EnumMember(Value = "BotJoinGroupEvent")] Joined, /// /// Bot主动退出一个群 /// - [Description("BotLeaveEventActive")] [EnumMember(Value = "BotLeaveEventActive")] + [Description("BotLeaveEventActive")] + [EnumMember(Value = "BotLeaveEventActive")] Left, /// /// Bot被踢出一个群 /// - [Description("BotLeaveEventKick")] [EnumMember(Value = "BotLeaveEventKick")] - Kicked, + [Description("BotLeaveEventKick")] + [EnumMember(Value = "BotLeaveEventKick")] + Kicked, + + /// + /// Bot因群主解散群而退出群 + /// + [Description("BotLeaveEventDisband")] + [EnumMember(Value = "BotLeaveEventDisband")] + Disbanded, /// /// 群消息撤回 /// - [Description("GroupRecallEvent")] [EnumMember(Value = "GroupRecallEvent")] + [Description("GroupRecallEvent")] + [EnumMember(Value = "GroupRecallEvent")] GroupMessageRecalled, /// /// 好友消息撤回 /// - [Description("FriendRecallEvent")] [EnumMember(Value = "FriendRecallEvent")] + [Description("FriendRecallEvent")] + [EnumMember(Value = "FriendRecallEvent")] FriendRecalled, /// /// 某个群名改变 /// - [Description("GroupNameChangeEvent")] [EnumMember(Value = "GroupNameChangeEvent")] + [Description("GroupNameChangeEvent")] + [EnumMember(Value = "GroupNameChangeEvent")] GroupNameChanged, /// /// 某群入群公告改变 /// - [Description("GroupEntranceAnnouncementChangeEvent")] [EnumMember(Value = "GroupEntranceAnnouncementChangeEvent")] + [Description("GroupEntranceAnnouncementChangeEvent")] + [EnumMember(Value = "GroupEntranceAnnouncementChangeEvent")] GroupEntranceAnnouncementChanged, /// /// 全员禁言 /// - [Description("GroupMuteAllEvent")] [EnumMember(Value = "GroupMuteAllEvent")] + [Description("GroupMuteAllEvent")] + [EnumMember(Value = "GroupMuteAllEvent")] GroupMutedAll, /// /// 匿名聊天 /// - [Description("GroupAllowAnonymousChatEvent")] [EnumMember(Value = "GroupAllowAnonymousChatEvent")] + [Description("GroupAllowAnonymousChatEvent")] + [EnumMember(Value = "GroupAllowAnonymousChatEvent")] GroupAllowedAnonymousChat, /// /// 坦白说 /// - [Description("GroupAllowConfessTalkEvent")] [EnumMember(Value = "GroupAllowConfessTalkEvent")] + [Description("GroupAllowConfessTalkEvent")] + [EnumMember(Value = "GroupAllowConfessTalkEvent")] GroupAllowedConfessTalk, /// /// 允许群员邀请好友加群 /// - [Description("GroupAllowMemberInviteEvent")] [EnumMember(Value = "GroupAllowMemberInviteEvent")] + [Description("GroupAllowMemberInviteEvent")] + [EnumMember(Value = "GroupAllowMemberInviteEvent")] GroupAllowedMemberInvite, /// /// 新人入群的事件 /// - [Description("MemberJoinEvent")] [EnumMember(Value = "MemberJoinEvent")] + [Description("MemberJoinEvent")] + [EnumMember(Value = "MemberJoinEvent")] MemberJoined, /// /// 成员被踢出群(该成员不是Bot) /// - [Description("MemberLeaveEventKick")] [EnumMember(Value = "MemberLeaveEventKick")] + [Description("MemberLeaveEventKick")] + [EnumMember(Value = "MemberLeaveEventKick")] MemberKicked, /// /// 成员主动离群(该成员不是Bot) /// - [Description("MemberLeaveEventQuit")] [EnumMember(Value = "MemberLeaveEventQuit")] + [Description("MemberLeaveEventQuit")] + [EnumMember(Value = "MemberLeaveEventQuit")] MemberLeft, /// /// 群名片改动 /// - [Description("MemberCardChangeEvent")] [EnumMember(Value = "MemberCardChangeEvent")] + [Description("MemberCardChangeEvent")] + [EnumMember(Value = "MemberCardChangeEvent")] MemberCardChanged, /// /// 群头衔改动(只有群主有操作限权) /// - [Description("MemberSpecialTitleChangeEvent")] [EnumMember(Value = "MemberSpecialTitleChangeEvent")] + [Description("MemberSpecialTitleChangeEvent")] + [EnumMember(Value = "MemberSpecialTitleChangeEvent")] MemberTitleChanged, /// /// 成员权限改变的事件(该成员不是Bot) /// - [Description("MemberPermissionChangeEvent")] [EnumMember(Value = "MemberPermissionChangeEvent")] + [Description("MemberPermissionChangeEvent")] + [EnumMember(Value = "MemberPermissionChangeEvent")] MemberPermissionChanged, /// /// 群成员被禁言事件(该成员不是Bot) /// - [Description("MemberMuteEvent")] [EnumMember(Value = "MemberMuteEvent")] + [Description("MemberMuteEvent")] + [EnumMember(Value = "MemberMuteEvent")] MemberMuted, /// /// 群成员被取消禁言事件(该成员不是Bot) /// - [Description("MemberUnmuteEvent")] [EnumMember(Value = "MemberUnmuteEvent")] + [Description("MemberUnmuteEvent")] + [EnumMember(Value = "MemberUnmuteEvent")] MemberUnmuted, /// /// 群员称号改变 /// - [Description("MemberHonorChangeEvent")] [EnumMember(Value = "MemberHonorChangeEvent")] + [Description("MemberHonorChangeEvent")] + [EnumMember(Value = "MemberHonorChangeEvent")] MemberHonorChanged, /// /// 添加好友申请 /// - [Description("NewFriendRequestEvent")] [EnumMember(Value = "NewFriendRequestEvent")] + [Description("NewFriendRequestEvent")] + [EnumMember(Value = "NewFriendRequestEvent")] NewFriendRequested, /// /// 用户入群申请(Bot需要有管理员权限) /// - [Description("MemberJoinRequestEvent")] [EnumMember(Value = "MemberJoinRequestEvent")] + [Description("MemberJoinRequestEvent")] + [EnumMember(Value = "MemberJoinRequestEvent")] NewMemberRequested, /// /// Bot被邀请入群申请 /// - [Description("BotInvitedJoinGroupRequestEvent")] [EnumMember(Value = "BotInvitedJoinGroupRequestEvent")] + [Description("BotInvitedJoinGroupRequestEvent")] + [EnumMember(Value = "BotInvitedJoinGroupRequestEvent")] NewInvitationRequested, /// /// 戳一戳事件 /// - [Description("NudgeEvent")] [EnumMember(Value = "NudgeEvent")] + [Description("NudgeEvent")] + [EnumMember(Value = "NudgeEvent")] Nudged, - + + /// + /// 其他客户端登录 + /// [Description("OtherClientOnlineEvent")] [EnumMember(Value = "OtherClientOnlineEvent")] OtherClientOnline, - + + /// + /// 其他客户端离线 + /// [Description("OtherClientOfflineEvent")] [EnumMember(Value = "OtherClientOfflineEvent")] OtherClientOffline, - + + /// + /// bot被at + /// [Description("At")] [EnumMember(Value = "At")] - At + At, + + /// + /// 命令被执行 + /// + [Description("CommandExecutedEvent")] + [EnumMember(Value = "CommandExecutedEvent")] + CommandExecuted, + + /// + /// 未知类型的事件 + /// + Unknown } \ No newline at end of file diff --git a/Mirai.Net/Data/Exceptions/InvalidAddressException.cs b/Mirai.Net/Data/Exceptions/InvalidAddressException.cs index da3eebb..a3bb286 100644 --- a/Mirai.Net/Data/Exceptions/InvalidAddressException.cs +++ b/Mirai.Net/Data/Exceptions/InvalidAddressException.cs @@ -16,19 +16,19 @@ public class InvalidAddressException : Exception // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp // - public InvalidAddressException() + internal InvalidAddressException() { } - public InvalidAddressException(string message) : base(message) + internal InvalidAddressException(string message) : base(message) { } - public InvalidAddressException(string message, Exception inner) : base(message, inner) + internal InvalidAddressException(string message, Exception inner) : base(message, inner) { } - protected InvalidAddressException( + internal InvalidAddressException( SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Mirai.Net/Data/Exceptions/InvalidCommandEntityException.cs b/Mirai.Net/Data/Exceptions/InvalidCommandEntityException.cs deleted file mode 100644 index 13b72ed..0000000 --- a/Mirai.Net/Data/Exceptions/InvalidCommandEntityException.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Mirai.Net.Data.Exceptions; - -[Serializable] -public class InvalidCommandEntityException : Exception -{ - // - // For guidelines regarding the creation of new exception types, see - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp - // and - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp - // - - public InvalidCommandEntityException() - { - } - - public InvalidCommandEntityException(string message) : base(message) - { - } - - public InvalidCommandEntityException(string message, Exception inner) : base(message, inner) - { - } - - protected InvalidCommandEntityException( - SerializationInfo info, - StreamingContext context) : base(info, context) - { - } -} \ No newline at end of file diff --git a/Mirai.Net/Data/Exceptions/InvalidCommandException.cs b/Mirai.Net/Data/Exceptions/InvalidCommandException.cs deleted file mode 100644 index cb96139..0000000 --- a/Mirai.Net/Data/Exceptions/InvalidCommandException.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Mirai.Net.Data.Exceptions; - -[Serializable] -public class InvalidCommandException : Exception -{ - // - // For guidelines regarding the creation of new exception types, see - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp - // and - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp - // - - public InvalidCommandException() - { - } - - public InvalidCommandException(string message) : base(message) - { - } - - public InvalidCommandException(string message, Exception inner) : base(message, inner) - { - } - - protected InvalidCommandException( - SerializationInfo info, - StreamingContext context) : base(info, context) - { - } -} \ No newline at end of file diff --git a/Mirai.Net/Data/Exceptions/InvalidQQException.cs b/Mirai.Net/Data/Exceptions/InvalidQQException.cs index 35bb8df..0c9258f 100644 --- a/Mirai.Net/Data/Exceptions/InvalidQQException.cs +++ b/Mirai.Net/Data/Exceptions/InvalidQQException.cs @@ -16,19 +16,19 @@ public class InvalidQQException : Exception // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp // - public InvalidQQException() + internal InvalidQQException() { } - public InvalidQQException(string message) : base(message) + internal InvalidQQException(string message) : base(message) { } - public InvalidQQException(string message, Exception inner) : base(message, inner) + internal InvalidQQException(string message, Exception inner) : base(message, inner) { } - protected InvalidQQException( + internal InvalidQQException( SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Mirai.Net/Data/Exceptions/InvalidResponseException.cs b/Mirai.Net/Data/Exceptions/InvalidResponseException.cs index cc38705..9ae935d 100644 --- a/Mirai.Net/Data/Exceptions/InvalidResponseException.cs +++ b/Mirai.Net/Data/Exceptions/InvalidResponseException.cs @@ -3,6 +3,9 @@ namespace Mirai.Net.Data.Exceptions; +/// +/// 错误的响应 +/// [Serializable] public class InvalidResponseException : Exception { @@ -13,19 +16,19 @@ public class InvalidResponseException : Exception // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp // - public InvalidResponseException() + internal InvalidResponseException() { } - public InvalidResponseException(string message) : base(message) + internal InvalidResponseException(string message) : base(message) { } - public InvalidResponseException(string message, Exception inner) : base(message, inner) + internal InvalidResponseException(string message, Exception inner) : base(message, inner) { } - protected InvalidResponseException( + internal InvalidResponseException( SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Mirai.Net/Data/Exceptions/InvalidWebsocketReponseException.cs b/Mirai.Net/Data/Exceptions/InvalidWebsocketReponseException.cs new file mode 100644 index 0000000..f1a9da3 --- /dev/null +++ b/Mirai.Net/Data/Exceptions/InvalidWebsocketReponseException.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.Serialization; + +namespace Mirai.Net.Data.Exceptions; + +/// +/// +/// +[Serializable] +public class InvalidWebsocketReponseException : Exception +{ + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // + + /// + /// + /// + public InvalidWebsocketReponseException() + { + } + + /// + /// + /// + /// + public InvalidWebsocketReponseException(string message) : base(message) + { + } + + /// + /// + /// + /// + /// + public InvalidWebsocketReponseException(string message, Exception inner) : base(message, inner) + { + } + + /// + /// + /// + /// + /// + protected InvalidWebsocketReponseException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/AppMessage.cs b/Mirai.Net/Data/Messages/Concretes/AppMessage.cs index cbb7c33..b490936 100644 --- a/Mirai.Net/Data/Messages/Concretes/AppMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/AppMessage.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class AppMessage : MessageBase +/// +/// 小程序消息 +/// +public record AppMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.App; - [JsonProperty("app")] public string App { get; set; } + /// + /// 消息内容 + /// + [JsonProperty("content")] public string Content { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/AtAllMessage.cs b/Mirai.Net/Data/Messages/Concretes/AtAllMessage.cs index eadb569..9271dd8 100644 --- a/Mirai.Net/Data/Messages/Concretes/AtAllMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/AtAllMessage.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class AtAllMessage : MessageBase +/// +/// @全体成员 +/// +public record AtAllMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.AtAll; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/AtMessage.cs b/Mirai.Net/Data/Messages/Concretes/AtMessage.cs index fbcf1a7..a536063 100644 --- a/Mirai.Net/Data/Messages/Concretes/AtMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/AtMessage.cs @@ -1,14 +1,34 @@ -using Newtonsoft.Json; +using Mirai.Net.Data.Shared; +using Newtonsoft.Json; namespace Mirai.Net.Data.Messages.Concretes; -public class AtMessage : MessageBase +/// +/// @某人 +/// +public record AtMessage : MessageBase { + /// + /// 带参数的构造器 + /// + /// 要@的人的qq public AtMessage(string target) { Target = target; } + /// + /// + /// + /// + public AtMessage(Member target) + { + Target = target.Id; + } + + /// + /// + /// public AtMessage() { } @@ -23,7 +43,10 @@ public AtMessage() /// At时显示的文字,发送消息时无效,自动使用群名片,默认为空 /// [JsonProperty("display")] - public string Display { get; set; } = ""; + internal string Display { get; set; } = ""; + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.At; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/DiceMessage.cs b/Mirai.Net/Data/Messages/Concretes/DiceMessage.cs index cb96d85..5b61eba 100644 --- a/Mirai.Net/Data/Messages/Concretes/DiceMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/DiceMessage.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class DiceMessage : MessageBase +/// +/// 骰子消息 +/// +public record DiceMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Dice; /// diff --git a/Mirai.Net/Data/Messages/Concretes/FaceMessage.cs b/Mirai.Net/Data/Messages/Concretes/FaceMessage.cs index 81aa1a8..5c68968 100644 --- a/Mirai.Net/Data/Messages/Concretes/FaceMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/FaceMessage.cs @@ -2,10 +2,13 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class FaceMessage : MessageBase +/// +/// qq表情 +/// +public record FaceMessage : MessageBase { /// - /// QQ表情编号,可选,优先高于name + /// QQ表情编号,可选,优先高于 /// [JsonProperty("faceId")] public string FaceId { get; set; } @@ -16,5 +19,8 @@ public class FaceMessage : MessageBase [JsonProperty("name")] public string Name { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Face; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/FileMessage.cs b/Mirai.Net/Data/Messages/Concretes/FileMessage.cs index 3d45ebf..5dfc2f4 100644 --- a/Mirai.Net/Data/Messages/Concretes/FileMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/FileMessage.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class FileMessage : MessageBase +/// +/// 文件 +/// +public record FileMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.File; /// diff --git a/Mirai.Net/Data/Messages/Concretes/FlashImageMessage.cs b/Mirai.Net/Data/Messages/Concretes/FlashImageMessage.cs index ce8acb5..3c7cbfd 100644 --- a/Mirai.Net/Data/Messages/Concretes/FlashImageMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/FlashImageMessage.cs @@ -1,6 +1,12 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class FlashImageMessage : ImageMessage +/// +/// 闪照,构造方式与相同 +/// +public record FlashImageMessage : ImageMessage { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.FlashImage; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/ForwardMessage.cs b/Mirai.Net/Data/Messages/Concretes/ForwardMessage.cs index a367ff6..ff807d1 100644 --- a/Mirai.Net/Data/Messages/Concretes/ForwardMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/ForwardMessage.cs @@ -1,19 +1,60 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; namespace Mirai.Net.Data.Messages.Concretes; -public class ForwardMessage : MessageBase +/// +/// 转发的消息 +/// +public record ForwardMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Forward; + /// + /// 点进消息前的显示内容 + /// + [JsonProperty("display")] + public ForwardDisplay Display { get; set; } + /// /// 消息节点 /// [JsonProperty("nodeList")] public IEnumerable NodeList { get; set; } - public class ForwardNode + /// + /// 从单个人的消息中构建转发消息 + /// + /// 某人的id + /// 某人的昵称 + /// 消息链的集合,每个消息链就是一条消息 + /// + public static ForwardMessage FromChains(string id, string name, IEnumerable chains) + { + var re = new ForwardMessage + { + NodeList = chains.Select(c => new ForwardNode + { + SenderId = id, + SenderName = name, + Time = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), + MessageChain = c, + SourceId = "114514" + }) + }; + + return re; + } + + /// + /// 转发的消息节点 + /// + public record ForwardNode { /// /// 发送人QQ号 @@ -37,7 +78,7 @@ public class ForwardNode /// 消息数组 /// [JsonProperty("messageChain")] - public IEnumerable MessageChain { get; set; } + public MessageChain MessageChain { get; set; } /// /// 可以只使用消息messageId,从缓存中读取一条消息作为节点 @@ -45,4 +86,67 @@ public class ForwardNode [JsonProperty("sourceId")] public string SourceId { get; set; } } + + /// + /// 未进入转发消息时的显示内容 + /// + public record ForwardDisplay + { + /// + /// + /// + public ForwardDisplay() + {} + + /// + /// 生成转发消息的外层显示 + /// + /// + /// + /// + /// + /// + public ForwardDisplay(string title = default, string brief = default, string source = default, IEnumerable preview = default, string summary = default) + { + this.Title = title; + + this.Brief = brief; + + this.Source = source; + + this.Preview = preview; + + this.Summary = summary; + } + + /// + /// 标题 + /// + [JsonProperty("title")] + public string Title { get; set; } + + /// + /// 在未进入聊天页面时所产生的消息提示 + /// + [JsonProperty("brief")] + public string Brief { get; set; } + + /// + /// 目前未发现在哪能显示,建议不填 + /// + [JsonProperty("source")] + public string Source { get; set; } + + /// + /// 消息内容预览 + /// + [JsonProperty("preview")] + public IEnumerable Preview { get; set; } + + /// + /// 消息总结 + /// + [JsonProperty("summary")] + public string Summary { get; set; } + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/ImageMessage.cs b/Mirai.Net/Data/Messages/Concretes/ImageMessage.cs index 9629ed9..9a54c9e 100644 --- a/Mirai.Net/Data/Messages/Concretes/ImageMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/ImageMessage.cs @@ -2,8 +2,14 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class ImageMessage : MessageBase +/// +/// 图片 +/// +public record ImageMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Image; /// @@ -29,4 +35,16 @@ public class ImageMessage : MessageBase /// [JsonProperty("base64")] public string Base64 { get; set; } + + /// + /// 图片的宽度 + /// + [JsonProperty("width")] + public string Width { get; set; } + + /// + /// 图片的高度 + /// + [JsonProperty("height")] + public string Height { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/JsonMessage.cs b/Mirai.Net/Data/Messages/Concretes/JsonMessage.cs index cc88c63..121d5cd 100644 --- a/Mirai.Net/Data/Messages/Concretes/JsonMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/JsonMessage.cs @@ -2,9 +2,16 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class JsonMessage : MessageBase +/// +/// json消息 +/// +public record JsonMessage : MessageBase { + ///json文本 [JsonProperty("json")] public string Json { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Json; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/MarketFaceMessage.cs b/Mirai.Net/Data/Messages/Concretes/MarketFaceMessage.cs new file mode 100644 index 0000000..a5e926a --- /dev/null +++ b/Mirai.Net/Data/Messages/Concretes/MarketFaceMessage.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Concretes; + +/// +/// 商城表情 +/// 目前商城表情仅支持接收和转发,不支持构造发送 +/// +public record MarketFaceMessage : MessageBase +{ + /// + /// 消息类型 + /// + public override Messages Type { get; set; } = Messages.MarketFace; + + /// + /// 表情id + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// 表情名称 + /// + [JsonProperty("name")] + public string Name { get; set; } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/MiraiCodeMessage.cs b/Mirai.Net/Data/Messages/Concretes/MiraiCodeMessage.cs new file mode 100644 index 0000000..bc7b6c7 --- /dev/null +++ b/Mirai.Net/Data/Messages/Concretes/MiraiCodeMessage.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Concretes; + +/// +/// Mirai码, +/// 看这里 +/// +public record MiraiCodeMessage : MessageBase +{ + /// + /// 消息类型 + /// + public override Messages Type { get; set; } = Messages.MiraiCode; + + /// + /// MiraiCode码,请确保已经转义 + /// + [JsonProperty("code")] + public string Code { get; set; } + + /// + /// 带参数的构造器 + /// + /// + public MiraiCodeMessage(string code) + { + Code = code; + } + + /// + /// + /// + public MiraiCodeMessage() + { + } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/MusicShareMessage.cs b/Mirai.Net/Data/Messages/Concretes/MusicShareMessage.cs index 5d5df1f..01af217 100644 --- a/Mirai.Net/Data/Messages/Concretes/MusicShareMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/MusicShareMessage.cs @@ -2,21 +2,48 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class MusicShareMessage : MessageBase +/// +/// 音乐分享 +/// +public record MusicShareMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.MusicShare; + /// + /// 类型,即分享自的软件平台 + /// [JsonProperty("kind")] public string Kind { get; set; } + /// + /// 标题 + /// [JsonProperty("title")] public string Title { get; set; } + /// + /// 概述 + /// [JsonProperty("summary")] public string Summary { get; set; } + /// + /// 跳转链接 + /// [JsonProperty("jumpUrl")] public string JumpUrl { get; set; } + /// + /// 封面链接 + /// [JsonProperty("pictureUrl")] public string PictureUrl { get; set; } + /// + /// 音乐链接 + /// [JsonProperty("musicUrl")] public string MusicUrl { get; set; } + /// + /// 简介 + /// [JsonProperty("brief")] public string Brief { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/PlainMessage.cs b/Mirai.Net/Data/Messages/Concretes/PlainMessage.cs index afa328e..bd1cf6d 100644 --- a/Mirai.Net/Data/Messages/Concretes/PlainMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/PlainMessage.cs @@ -2,18 +2,54 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class PlainMessage : MessageBase +/// +/// 纯文本消息 +/// +public record PlainMessage : MessageBase { + /// + /// 带参数的构造器 + /// + /// public PlainMessage(string text) { Text = text; } + /// + /// + /// public PlainMessage() { } + /// + /// 文本 + /// [JsonProperty("text")] public string Text { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Plain; + + /// + /// 可以和string相互转换 + /// + /// + /// + public static implicit operator PlainMessage(string s) + { + return new PlainMessage(s); + } + + /// + /// 可以和string相互转换 + /// + /// + /// + public static implicit operator string(PlainMessage plainMessage) + { + return plainMessage.Text; + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/PokeMessage.cs b/Mirai.Net/Data/Messages/Concretes/PokeMessage.cs index 40ddedd..b6221d4 100644 --- a/Mirai.Net/Data/Messages/Concretes/PokeMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/PokeMessage.cs @@ -2,7 +2,10 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class PokeMessage : MessageBase +/// +/// 戳一戳系列消息 +/// +public record PokeMessage : MessageBase { /// /// "Poke": 戳一戳 @@ -15,5 +18,8 @@ public class PokeMessage : MessageBase [JsonProperty("name")] public string Name { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Poke; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/QuoteMessage.cs b/Mirai.Net/Data/Messages/Concretes/QuoteMessage.cs index be9c07d..468e251 100644 --- a/Mirai.Net/Data/Messages/Concretes/QuoteMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/QuoteMessage.cs @@ -1,10 +1,16 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +using Newtonsoft.Json; +using System.Collections.Generic; namespace Mirai.Net.Data.Messages.Concretes; -public class QuoteMessage : MessageBase +/// +/// 引用消息 +/// +public record QuoteMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Quote; /// diff --git a/Mirai.Net/Data/Messages/Concretes/SourceMessage.cs b/Mirai.Net/Data/Messages/Concretes/SourceMessage.cs index d00d110..424c705 100644 --- a/Mirai.Net/Data/Messages/Concretes/SourceMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/SourceMessage.cs @@ -2,7 +2,11 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class SourceMessage : MessageBase +/// +/// 源消息 +/// 仅在接收消息时有效,且总是消息链的第一个元素 +/// +public record SourceMessage : MessageBase { /// /// 消息id @@ -16,5 +20,8 @@ public class SourceMessage : MessageBase [JsonProperty("time")] public string Time { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Source; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/UnknownMessage.cs b/Mirai.Net/Data/Messages/Concretes/UnknownMessage.cs new file mode 100644 index 0000000..06ad9fa --- /dev/null +++ b/Mirai.Net/Data/Messages/Concretes/UnknownMessage.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Concretes; + +/// +/// 未知的(没有实现)的消息类型 +/// +public record UnknownMessage : MessageBase +{ + /// + /// + /// + [JsonIgnore] public override Messages Type { get; set; } = Messages.Unknown; + + /// + /// 收到的json + /// + [JsonIgnore] + public string RawJson { get; set; } + + /// + /// + /// + /// + public UnknownMessage(string rawJson) + { + RawJson = rawJson; + } + + /// + /// + /// + public UnknownMessage() + { + } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/VoiceMessage.cs b/Mirai.Net/Data/Messages/Concretes/VoiceMessage.cs index a17d9cf..b8d5c54 100644 --- a/Mirai.Net/Data/Messages/Concretes/VoiceMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/VoiceMessage.cs @@ -2,7 +2,10 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class VoiceMessage : MessageBase +/// +/// 语音消息 +/// +public record VoiceMessage : MessageBase { /// /// 语音的voiceId,不为空时将忽略url属性 @@ -28,5 +31,8 @@ public class VoiceMessage : MessageBase [JsonProperty("base64")] public string Base64 { get; set; } + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Voice; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Concretes/XmlMessage.cs b/Mirai.Net/Data/Messages/Concretes/XmlMessage.cs index 20d7a82..277e57e 100644 --- a/Mirai.Net/Data/Messages/Concretes/XmlMessage.cs +++ b/Mirai.Net/Data/Messages/Concretes/XmlMessage.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Messages.Concretes; -public class XmlMessage : MessageBase +/// +/// xml消息 +/// +public record XmlMessage : MessageBase { + /// + /// 消息类型 + /// public override Messages Type { get; set; } = Messages.Xml; + /// + /// xml文本 + /// [JsonProperty("xml")] public string Xml { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/MessageBase.cs b/Mirai.Net/Data/Messages/MessageBase.cs index 8faa661..55c91aa 100644 --- a/Mirai.Net/Data/Messages/MessageBase.cs +++ b/Mirai.Net/Data/Messages/MessageBase.cs @@ -1,11 +1,118 @@ -using Newtonsoft.Json; +using Manganese.Text; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using Mirai.Net.Data.Messages.Concretes; namespace Mirai.Net.Data.Messages; -public class MessageBase +/// +/// 所有消息的基类 +/// +public record MessageBase { + /// + /// 消息类型 + /// [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] public virtual Messages Type { get; set; } + + /// + /// 实际上是转换成json文本 + /// + /// + public override string ToString() + { + return this.ToJsonString(); + } + + /// + /// 提供Mirai码序列化方法 + /// + /// + public string SerializeToMiraiCode() + { + switch (this) + { + case PlainMessage msg: + + return msg.Text; + + case FlashImageMessage msg: + + return $"[mirai:flash:{msg.ImageId}]"; + + case ImageMessage msg: + + if (string.IsNullOrEmpty(msg.ImageId)) + throw new System.ArgumentNullException("ImageId为空!"); + + else + return $"[mirai:image:{msg.ImageId}]"; + + case AtMessage msg: + + return $"[mirai:at:{msg.Target}]"; + + case AtAllMessage msg: + + return "[mirai:atall]"; + + case FaceMessage msg: + + if (string.IsNullOrEmpty(msg.FaceId)) + throw new System.ArgumentNullException("FaceId为空!"); + + else + return $"[mirai:face:{msg.FaceId}]"; + + case PokeMessage msg: + + var poke = msg.Name switch + { + "Poke" => "戳一戳,1,-1", + + "ShowLove" => "比心,2,-1", + + "Like" => "点赞,3,-1", + + "Heartbroken" => "心碎,4,-1", + + "SixSixSix" => "666,5,-1", + + "FangDaZhao" => "放大招,6,-1", + + _ => throw new System.NotSupportedException("该Poke消息还未支持!") + }; + + return $"[mirai:poke:{poke}]"; + + case DiceMessage msg: + + return $"[mirai:dice:{msg.Value}]"; + + case MusicShareMessage msg: // MusicShare在mirai的2.15.0-M1版本中显示为[mirai:origin:MUSIC_SHARE]MusicShare(*这里有一长串省略的内容) 与https://github.com/mamoe/mirai/blob/dev/docs/Messages.md 中所说的[mirai:musicshare:$args]不符 因此这里先采用后者的方式 使用半角逗号分隔 至少保证序列化的稳定 + + return $"[mirai:musicshare:{msg.Kind},{msg.Title},{msg.Summary},{msg.JumpUrl},{msg.PictureUrl},{msg.MusicUrl},{msg.Brief}]"; + + case FileMessage msg: // FileMessage由于缺少参数(InternalId)目前是实验性功能 该参数自动使用Id补全 + + return $"[mirai:file:{msg.FileId},{msg.FileId},{msg.Name},{msg.Size}]"; + + default: + + throw new System.NotSupportedException("遇到了不支持序列化的消息!"); + } + } + + /// + /// 自动转换成消息链 + /// + /// + /// + /// + public static MessageChain operator +(MessageBase msg1, MessageBase msg2) + { + return new MessageChain { msg1, msg2 }; + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/MessageChain.cs b/Mirai.Net/Data/Messages/MessageChain.cs new file mode 100644 index 0000000..a353ae3 --- /dev/null +++ b/Mirai.Net/Data/Messages/MessageChain.cs @@ -0,0 +1,7 @@ +namespace Mirai.Net.Data.Messages; + +//just a definition in Data namespace +public partial class MessageChain +{ + +} \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/MessageReceiverBase.cs b/Mirai.Net/Data/Messages/MessageReceiverBase.cs index 9aec479..c872bba 100644 --- a/Mirai.Net/Data/Messages/MessageReceiverBase.cs +++ b/Mirai.Net/Data/Messages/MessageReceiverBase.cs @@ -1,14 +1,36 @@ -using System.Collections.Generic; +using System.Threading.Tasks; +using System; + using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Mirai.Net.Data.Messages; -public class MessageReceiverBase +/// +/// 消息接收器基类 +/// +public record MessageReceiverBase { + /// + /// 消息接收器类型 + /// [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] public virtual MessageReceivers Type { get; set; } - [JsonProperty("messageChain")] public IEnumerable MessageChain { get; set; } + /// + /// 接受到的消息链 + /// + [JsonProperty("messageChain")] public MessageChain MessageChain { get; set; } + + /// + /// 发送消息 + /// + /// 消息链 + /// + /// 直接尝试呼叫此方法时掷出 + public virtual Task SendMessageAsync(MessageChain chain) + { + throw new NotSupportedException("Direct call to virtual MessageReceiverBase.SendMessageAsync is not supported."); + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/MessageReceivers.cs b/Mirai.Net/Data/Messages/MessageReceivers.cs index 719b20f..104b967 100644 --- a/Mirai.Net/Data/Messages/MessageReceivers.cs +++ b/Mirai.Net/Data/Messages/MessageReceivers.cs @@ -3,20 +3,69 @@ namespace Mirai.Net.Data.Messages; +/// +/// 消息接收器的类型 +/// public enum MessageReceivers { - [Description("FriendMessage")] [EnumMember(Value = "FriendMessage")] + /// + /// 好友消息 + /// + [Description("FriendMessage")] + [EnumMember(Value = "FriendMessage")] Friend, - [Description("GroupMessage")] [EnumMember(Value = "GroupMessage")] + /// + /// 群消息 + /// + [Description("GroupMessage")] + [EnumMember(Value = "GroupMessage")] Group, - [Description("TempMessage")] [EnumMember(Value = "TempMessage")] + /// + /// 临时消息 + /// + [Description("TempMessage")] + [EnumMember(Value = "TempMessage")] Temp, - [Description("StrangerMessage")] [EnumMember(Value = "StrangerMessage")] + /// + /// 陌生人消息 + /// + [Description("StrangerMessage")] + [EnumMember(Value = "StrangerMessage")] Stranger, - [Description("OtherClientMessage")] [EnumMember(Value = "OtherClientMessage")] - OtherClient + /// + /// 其它客户端消息 + /// + [Description("OtherClientMessage")] + [EnumMember(Value = "OtherClientMessage")] + OtherClient, + + /// + /// 好友消息的同步消息 + /// + [Description("FriendSyncMessage")] + [EnumMember(Value = "FriendSyncMessage")] + FriendSync, + + /// + /// 群消息的同步消息 + /// + [Description("GroupSyncMessage")] + [EnumMember(Value = "GroupSyncMessage")] + GroupSync, + + /// + /// 临时消息的同步消息 + /// + [Description("TempSyncMessage")] + [EnumMember(Value = "TempSyncMessage")] + TempSync, + + /// + /// 未知类型的接收器 + /// + Unknown } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Messages.cs b/Mirai.Net/Data/Messages/Messages.cs index cbbf670..bfcaa26 100644 --- a/Mirai.Net/Data/Messages/Messages.cs +++ b/Mirai.Net/Data/Messages/Messages.cs @@ -1,22 +1,88 @@ namespace Mirai.Net.Data.Messages; +/// +/// 消息类型 +/// public enum Messages { + /// + /// 源消息 + /// Source, + /// + /// 引用消息 + /// Quote, + /// + /// @消息 + /// At, + /// + /// @全体成员消息 + /// AtAll, + /// + /// qq表情消息 + /// Face, + /// + /// 纯文本消息 + /// Plain, + /// + /// 图片消息 + /// Image, + /// + /// 闪照消息 + /// FlashImage, + /// + /// 语音消息 + /// Voice, + /// + /// xml消息 + /// Xml, + /// + /// json消息 + /// Json, + /// + /// app消息 + /// App, + /// + /// 不知道是啥玩意消息 + /// Poke, + /// + /// 骰子消息 + /// Dice, + /// + /// 音乐分享消息 + /// MusicShare, + /// + /// 转发消息 + /// Forward, - File + /// + /// 文件消息 + /// + File, + /// + /// 商城表情消息 + /// + MarketFace, + /// + /// mirai码消息 + /// + MiraiCode, + /// + /// 未知类型的消息 + /// + Unknown } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/FriendMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/FriendMessageReceiver.cs index a8a7b46..88d196e 100644 --- a/Mirai.Net/Data/Messages/Receivers/FriendMessageReceiver.cs +++ b/Mirai.Net/Data/Messages/Receivers/FriendMessageReceiver.cs @@ -1,29 +1,59 @@ -using Mirai.Net.Data.Shared; +using Mirai.Net.Data.Sessions; +using System.Threading.Tasks; + +using Mirai.Net.Data.Shared; +using Mirai.Net.Sessions.Http.Managers; + using Newtonsoft.Json; namespace Mirai.Net.Data.Messages.Receivers; -public class FriendMessageReceiver : MessageReceiverBase +/// +/// 好友消息接收器 +/// +public record FriendMessageReceiver : MessageReceiverBase { + /// + /// 消息接收器类型 + /// public override MessageReceivers Type { get; set; } = MessageReceivers.Friend; + /// + /// 发送者,某好友 + /// [JsonProperty("sender")] public Friend Sender { get; set; } - + /// /// 好友昵称 /// [JsonIgnore] - public string Name => Sender.NickName; + public string FriendName => Sender.NickName; /// /// 好友备注 /// [JsonIgnore] - public string Remark => Sender.Remark; + public string FriendRemark => Sender.Remark; /// /// 好友QQ号 /// [JsonIgnore] - public string Id => Sender.Id; + public string FriendId => Sender.Id; + + /// + /// 发送好友消息 + /// + /// 消息链 + /// + public override async Task SendMessageAsync(MessageChain chain) + { + var payload = new + { + target = Sender.Id, + messageChain = chain + }; + + return await MessageManager.SendMessageAsync(HttpEndpoints.SendFriendMessage, payload); + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/FriendSyncMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/FriendSyncMessageReceiver.cs new file mode 100644 index 0000000..7a7d254 --- /dev/null +++ b/Mirai.Net/Data/Messages/Receivers/FriendSyncMessageReceiver.cs @@ -0,0 +1,24 @@ +using Mirai.Net.Data.Shared; +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Receivers +{ + + /// + /// 好友同步消息接收器 + /// + public record FriendSyncMessageReceiver : MessageReceiverBase + { + + /// + /// 消息接收器类型 + /// + public override MessageReceivers Type { get; set; } = MessageReceivers.FriendSync; + + /// + /// 好友信息 + /// + [JsonProperty("subject")] public Friend TargetFriend { get; set; } + + } +} diff --git a/Mirai.Net/Data/Messages/Receivers/GroupMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/GroupMessageReceiver.cs index c48f341..dfc4fc2 100644 --- a/Mirai.Net/Data/Messages/Receivers/GroupMessageReceiver.cs +++ b/Mirai.Net/Data/Messages/Receivers/GroupMessageReceiver.cs @@ -1,29 +1,59 @@ -using Mirai.Net.Data.Shared; +using Mirai.Net.Data.Sessions; +using System.Threading.Tasks; + +using Mirai.Net.Data.Shared; +using Mirai.Net.Sessions.Http.Managers; + using Newtonsoft.Json; namespace Mirai.Net.Data.Messages.Receivers; -public class GroupMessageReceiver : MessageReceiverBase +/// +/// 群消息接收器s +/// +public record GroupMessageReceiver : MessageReceiverBase { + /// + /// 消息接收器类型 + /// [JsonProperty("sender")] public Member Sender { get; set; } /// /// 群号 /// [JsonIgnore] - public string Id => Sender.Group.Id; + public string GroupId => Sender.Group.Id; /// /// 群名称 /// [JsonIgnore] - public string Name => Sender.Group.Name; + public string GroupName => Sender.Group.Name; /// /// bot在群内的权限 /// [JsonIgnore] - public Permissions Permission => Sender.Group.Permission; + public Permissions BotPermission => Sender.Group.Permission; + /// + /// 消息接收器类型 + /// public override MessageReceivers Type { get; set; } = MessageReceivers.Group; + + /// + /// 发送群消息 + /// + /// 消息链 + /// + public override async Task SendMessageAsync(MessageChain chain) + { + var payload = new + { + target = Sender.Group.Id, + messageChain = chain + }; + + return await MessageManager.SendMessageAsync(HttpEndpoints.SendGroupMessage, payload); + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/GroupSyncMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/GroupSyncMessageReceiver.cs new file mode 100644 index 0000000..0f64eeb --- /dev/null +++ b/Mirai.Net/Data/Messages/Receivers/GroupSyncMessageReceiver.cs @@ -0,0 +1,24 @@ +using Mirai.Net.Data.Shared; +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Receivers +{ + + /// + /// 群同步消息接收器 + /// + public record GroupSyncMessageReceiver : MessageReceiverBase + { + + /// + /// 消息接收器类型 + /// + public override MessageReceivers Type { get; set; } = MessageReceivers.GroupSync; + + /// + /// 群信息 + /// + [JsonProperty("subject")] public Group TargetGroup { get; set; } + + } +} diff --git a/Mirai.Net/Data/Messages/Receivers/OtherClientMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/OtherClientMessageReceiver.cs index aba4b4d..127ac4a 100644 --- a/Mirai.Net/Data/Messages/Receivers/OtherClientMessageReceiver.cs +++ b/Mirai.Net/Data/Messages/Receivers/OtherClientMessageReceiver.cs @@ -3,9 +3,18 @@ namespace Mirai.Net.Data.Messages.Receivers; -public class OtherClientMessageReceiver : MessageReceiverBase +/// +/// 其他客户端消息接收器 +/// +public record OtherClientMessageReceiver : MessageReceiverBase { + /// + /// 消息接收器类型 + /// public override MessageReceivers Type { get; set; } = MessageReceivers.OtherClient; + /// + /// 发送者 + /// [JsonProperty("sender")] public OtherClient Sender { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/StrangerMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/StrangerMessageReceiver.cs index 7713d48..a02e154 100644 --- a/Mirai.Net/Data/Messages/Receivers/StrangerMessageReceiver.cs +++ b/Mirai.Net/Data/Messages/Receivers/StrangerMessageReceiver.cs @@ -3,27 +3,30 @@ namespace Mirai.Net.Data.Messages.Receivers; -public class StrangerMessageReceiver : MessageReceiverBase +/// +/// 陌生人接收器 +/// +public record StrangerMessageReceiver : MessageReceiverBase { + /// + /// 消息接收器类型 + /// public override MessageReceivers Type { get; set; } = MessageReceivers.Stranger; - [JsonProperty("sender")] public Friend Sender { get; set; } - /// - /// 昵称 + /// 发送者 /// - [JsonIgnore] - public string Name => Sender.NickName; + [JsonProperty("sender")] public Friend Sender { get; set; } /// - /// 备注 + /// 昵称 /// [JsonIgnore] - public string Remark => Sender.Remark; + public string StrangerName => Sender.NickName; /// /// QQ号 /// [JsonIgnore] - public string Id => Sender.Id; + public string StrangerId => Sender.Id; } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/TempMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/TempMessageReceiver.cs index a7ff785..ad27e45 100644 --- a/Mirai.Net/Data/Messages/Receivers/TempMessageReceiver.cs +++ b/Mirai.Net/Data/Messages/Receivers/TempMessageReceiver.cs @@ -1,29 +1,59 @@ -using Mirai.Net.Data.Shared; +using Mirai.Net.Data.Sessions; +using System.Threading.Tasks; + +using Mirai.Net.Data.Shared; +using Mirai.Net.Sessions.Http.Managers; + using Newtonsoft.Json; namespace Mirai.Net.Data.Messages.Receivers; -public class TempMessageReceiver : MessageReceiverBase +/// +/// 临时消息接收器 +/// +public record TempMessageReceiver : MessageReceiverBase { + /// + /// 消息接收器类型 + /// public override MessageReceivers Type { get; set; } = MessageReceivers.Temp; + /// + /// 发送者 + /// [JsonProperty("sender")] public Member Sender { get; set; } - + /// /// 群号 /// [JsonIgnore] - public string Id => Sender.Group.Id; + public string GroupId => Sender.Group.Id; /// /// 群名称 /// [JsonIgnore] - public string Name => Sender.Group.Name; + public string GroupName => Sender.Group.Name; /// /// bot在群内的权限 /// [JsonIgnore] - public Permissions Permission => Sender.Group.Permission; + public Permissions BotPermission => Sender.Group.Permission; + + /// + /// 发送临时消息 + /// + /// 消息链 + /// + public override async Task SendMessageAsync(MessageChain chain) + { + var payload = new + { + target = Sender.Id, + messageChain = chain + }; + + return await MessageManager.SendMessageAsync(HttpEndpoints.SendTempMessage, payload); + } } \ No newline at end of file diff --git a/Mirai.Net/Data/Messages/Receivers/TempSyncMessageReceiver.cs b/Mirai.Net/Data/Messages/Receivers/TempSyncMessageReceiver.cs new file mode 100644 index 0000000..50d480d --- /dev/null +++ b/Mirai.Net/Data/Messages/Receivers/TempSyncMessageReceiver.cs @@ -0,0 +1,24 @@ +using Mirai.Net.Data.Shared; +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Receivers +{ + + /// + /// 群临时消息同步消息接收器 + /// + public record TempSyncMessageReceiver : MessageReceiverBase + { + + /// + /// 消息接收器类型 + /// + public override MessageReceivers Type { get; set; } = MessageReceivers.TempSync; + + /// + /// 目标群成员的信息 + /// + [JsonProperty("subject")] public Member TargetMember { get; set; } + + } +} diff --git a/Mirai.Net/Data/Messages/Receivers/UnknownReceiver.cs b/Mirai.Net/Data/Messages/Receivers/UnknownReceiver.cs new file mode 100644 index 0000000..7c7e681 --- /dev/null +++ b/Mirai.Net/Data/Messages/Receivers/UnknownReceiver.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Messages.Receivers; + +/// +/// 未知类型的消息接收器 +/// +public record UnknownReceiver : MessageReceiverBase +{ + /// + /// + /// + [JsonIgnore] + public override MessageReceivers Type { get; set; } = MessageReceivers.Unknown; + + /// + /// 收到的json + /// + [JsonIgnore] + public string RawJson { get; set; } + + /// + /// + /// + public UnknownReceiver() + { + } + + /// + /// + /// + /// + public UnknownReceiver(string rawJson) + { + RawJson = rawJson; + } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Modules/CommandTriggerAttribute.cs b/Mirai.Net/Data/Modules/CommandTriggerAttribute.cs deleted file mode 100644 index 5fdb5cc..0000000 --- a/Mirai.Net/Data/Modules/CommandTriggerAttribute.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace Mirai.Net.Data.Modules; - -/// -/// -/// /hello --arg1 value1 value2 --arg2 --arg3 -/// /: prefix -/// hello : name -/// -- : args separator -/// -/// -[AttributeUsage(AttributeTargets.Method)] -[Obsolete] -public class CommandTriggerAttribute : Attribute -{ - public CommandTriggerAttribute(string name, string prefix = "/", string argsSeparator = "-", bool equalName = false) - { - Prefix = prefix; - Name = name; - ArgsSeparator = argsSeparator; - EqualName = equalName; - } - - public string Prefix { get; set; } - - public string Name { get; set; } - - public string ArgsSeparator { get; set; } - - /// - /// 命令中是否只能包含Name - /// - public bool EqualName { get; set; } -} \ No newline at end of file diff --git a/Mirai.Net/Data/Sessions/ConnectConfig.cs b/Mirai.Net/Data/Sessions/ConnectConfig.cs new file mode 100644 index 0000000..1bff492 --- /dev/null +++ b/Mirai.Net/Data/Sessions/ConnectConfig.cs @@ -0,0 +1,104 @@ +using Manganese.Text; +using Mirai.Net.Data.Exceptions; +using System.Linq; + +namespace Mirai.Net.Data.Sessions +{ + + /// + /// 连接配置 + /// + public record ConnectConfig + { + /// + /// http连接配置 + /// + public AdapterConfig HttpAddress { get; set; } + + /// + /// websocket连接配置 + /// + public AdapterConfig WebsocketAddress { get; set; } + + /// + /// 从地址构造连接配置 + /// + /// + /// + public static implicit operator ConnectConfig(string address) + { + return new ConnectConfig + { + HttpAddress = address, + WebsocketAddress = address + }; + } + + /// + /// 连接配置转换成地址 + /// + /// + /// + /// + public static implicit operator string(ConnectConfig config) + { + if (config.HttpAddress != config.WebsocketAddress) + { + throw new InvalidAddressException("只有相同的地址才能转换为字符串"); + } + + return $"{config.HttpAddress.Host}:{config.HttpAddress.Port}"; + } + + /// + /// 适配器配置 + /// + /// + /// + public record AdapterConfig(string Host, string Port) + { + /// + /// 从string自动转换成ip:port对 + /// + /// + /// + public static implicit operator AdapterConfig(string address) + { + address = address.TrimEnd('/').Empty("http://").Empty("https://"); + if (!address.Contains(':')) throw new InvalidAddressException($"错误的地址: {address}"); + + var split = address.Split(':'); + + if (split.Length != 2) throw new InvalidAddressException($"错误的地址: {address}"); + if (!split.Last().IsInteger()) throw new InvalidAddressException($"错误的地址: {address}"); + + return new AdapterConfig(split[0], split[1]); + } + + /// + /// 转换为string + /// + /// + /// + public static implicit operator string(AdapterConfig config) + { + return $"{config.Host}:{config.Port}"; + } + + /// + /// this + /// + /// + public override string ToString() + { + return this; + } + } + } +} + + +namespace System.Runtime.CompilerServices +{ + internal static class IsExternalInit { } +} \ No newline at end of file diff --git a/Mirai.Net/Data/Sessions/HttpEndpoints.cs b/Mirai.Net/Data/Sessions/HttpEndpoints.cs index 6cd6420..19bd793 100644 --- a/Mirai.Net/Data/Sessions/HttpEndpoints.cs +++ b/Mirai.Net/Data/Sessions/HttpEndpoints.cs @@ -24,6 +24,7 @@ internal enum HttpEndpoints [Description("sendTempMessage")] SendTempMessage, [Description("sendNudge")] SendNudge, [Description("recall")] Recall, + [Description("roamingMessages")] RoamingMessages, [Description("file/list")] FileList, [Description("file/info")] FileInfo, @@ -47,6 +48,11 @@ internal enum HttpEndpoints [Description("setEssence")] SetEssence, [Description("groupConfig")] GroupConfig, [Description("memberInfo")] MemberInfo, + [Description("userProfile")] UserProfile, + + [Description("anno/list")] GetAnnouncement, + [Description("anno/publish")] PubAnnouncement, + [Description("anno/delete")] DelAnnouncement, [Description("resp/newFriendRequestEvent")] NewFriendRequested, @@ -56,7 +62,10 @@ internal enum HttpEndpoints [Description("resp/botInvitedJoinGroupRequestEvent")] BotInvited, - + [Description("messageFromId")] - MessageFromId + MessageFromId, + + [Description("cmd/execute")] ExecuteCommand, + [Description("cmd/register")] RegisterCommand } \ No newline at end of file diff --git a/Mirai.Net/Data/Sessions/WebsocketEndpoints.cs b/Mirai.Net/Data/Sessions/WebsocketEndpoints.cs deleted file mode 100644 index 913375e..0000000 --- a/Mirai.Net/Data/Sessions/WebsocketEndpoints.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.ComponentModel; - -namespace Mirai.Net.Data.Sessions; - -/// -/// MiraiBot类内部需要的Websocket请求端点 -/// -internal enum WebsocketEndpoints -{ - [Description("message")] Message, - [Description("all")] All, - [Description("event")] Event -} \ No newline at end of file diff --git a/Mirai.Net/Data/Sessions/WebsocketMessageTypes.cs b/Mirai.Net/Data/Sessions/WebsocketMessageTypes.cs deleted file mode 100644 index 834a506..0000000 --- a/Mirai.Net/Data/Sessions/WebsocketMessageTypes.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Mirai.Net.Data.Sessions; - -internal enum WebsocketMessageTypes -{ - Message, - Event, - Unknown -} \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/Announcement.cs b/Mirai.Net/Data/Shared/Announcement.cs new file mode 100644 index 0000000..783342d --- /dev/null +++ b/Mirai.Net/Data/Shared/Announcement.cs @@ -0,0 +1,53 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Shared +{ + /// + /// 群公告 + /// + public record Announcement + { + /// + /// 群信息 + /// + [JsonProperty("group")] + public Group Group { get; set; } + + /// + /// 群公告内容 + /// + [JsonProperty("content")] + public string Content { get; set; } + + /// + /// 发布者账号 + /// + [JsonProperty("senderID")] + public string SenderID { get; set; } + + /// + /// 公告唯一id + /// + [JsonProperty("fid")] + public string Fid { get; set; } + + /// + /// 是否所有群成员已确认 + /// + [JsonProperty("allConfirmed")] + public bool AllConfirmed { get; set; } + + /// + /// 确认群成员人数 + /// + [JsonProperty("confirmedMembersCount")] + public string ConfirmedMembersCount { get; set; } + + /// + /// 发布时间 + /// + [JsonProperty("publicationTime")] + public string PublicationTime { get; set; } + + } +} diff --git a/Mirai.Net/Data/Shared/AnnouncementSetting.cs b/Mirai.Net/Data/Shared/AnnouncementSetting.cs new file mode 100644 index 0000000..b2ada0b --- /dev/null +++ b/Mirai.Net/Data/Shared/AnnouncementSetting.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; + +namespace Mirai.Net.Data.Shared +{ + /// + /// 群公告设置 + /// + public record AnnouncementSetting + { + + /// + /// 群号 + /// + [JsonProperty("target")] + public string Target { get; set; } + + /// + /// 公告内容 + /// + [JsonProperty("content")] + public string Content { get; set; } = ""; + + /// + /// 是否发送给新成员 + /// + [JsonProperty("sendToNewMember")] + public bool SendToNewMember { get; set; } = false; + + /// + /// 是否置顶 + /// + [JsonProperty("pinned")] + public bool Pinned { get; set; } = false; + + /// + /// 是否显示群成员修改群名片的引导 + /// + [JsonProperty("showEditCard")] + public bool ShowEditCard { get; set; } = false; + + /// + /// 是否自动弹出 + /// + [JsonProperty("showPopup")] + public bool ShowPopup { get; set; } = false; + + /// + /// 是否需要群成员确认 + /// + [JsonProperty("requireConfirmation")] + public bool RequireConfirmation { get; set; } = false; + + /// + /// 公告图片url + /// + [JsonProperty("imageUrl")] + public string ImageUrl { get; set; } + + /// + /// 公告图片本地路径 + /// + [JsonProperty("imagePath")] + public string ImagePath { get; set; } + + /// + /// 公告图片base64编码 + /// + [JsonProperty("imageBase64")] + public string ImageBase64 { get; set; } + + } +} diff --git a/Mirai.Net/Data/Shared/File.cs b/Mirai.Net/Data/Shared/File.cs index cf662fd..be8e907 100644 --- a/Mirai.Net/Data/Shared/File.cs +++ b/Mirai.Net/Data/Shared/File.cs @@ -3,53 +3,116 @@ namespace Mirai.Net.Data.Shared; -public class File +/// +/// 文件 +/// +public record File { + /// + /// 文件名 + /// [JsonProperty("name")] public string Name { get; set; } + /// + /// 文件标识id + /// [JsonProperty("id")] public string Id { get; set; } + /// + /// 文件路径 + /// [JsonProperty("path")] public string Path { get; set; } + /// + /// 父目录 + /// [JsonProperty("parent")] public File Parent { get; set; } + /// + /// 是不是文件 + /// [JsonProperty("isFile")] public bool IsFile { get; set; } + /// + /// 是不是母鹿 + /// [JsonProperty("isDirectory")] public bool IsDirectory { get; set; } + /// + /// 上传者 + /// [JsonProperty("contact")] public FileUploader Contact { get; set; } + /// + /// 下载信息 + /// [JsonProperty("downloadInfo")] public FileDownloadInfo DownloadInfo { get; set; } - public class FileUploader + /// + /// 文件上传者 + /// + public record FileUploader { + /// + /// 上传者qq + /// [JsonProperty("id")] public string Id { get; set; } + /// + /// 上传者昵称 + /// [JsonProperty("name")] public string Name { get; set; } + /// + /// 上传者在群内的权限 + /// [JsonProperty("permission")] [JsonConverter(typeof(StringEnumConverter))] public Permissions Permission { get; set; } } - public class FileDownloadInfo + /// + /// 文件下载信息 + /// + public record FileDownloadInfo { + /// + /// sha1 + /// [JsonProperty("sha1")] public string Sha1 { get; set; } + /// + /// md5 + /// [JsonProperty("md5")] public string Md5 { get; set; } + /// + /// 下载链接 + /// [JsonProperty("url")] public string Url { get; set; } - + + /// + /// 下载次数 + /// [JsonProperty("downloadTimes")] - public string DownloadTimes {get; set;} - + public string DownloadTimes { get; set; } + + /// + /// 上传者id + /// [JsonProperty("uploaderId")] - public string UploaderId {get; set;} - + public string UploaderId { get; set; } + + /// + /// 上传时间戳 + /// [JsonProperty("uploadTime")] - public string UploadTime {get; set;} - + public string UploadTime { get; set; } + + /// + /// 最后修改时间 + /// [JsonProperty("lastModifyTime")] - public string LastModifyTime {get; set;} + public string LastModifyTime { get; set; } } } diff --git a/Mirai.Net/Data/Shared/Friend.cs b/Mirai.Net/Data/Shared/Friend.cs index 40ad90a..76f5db1 100644 --- a/Mirai.Net/Data/Shared/Friend.cs +++ b/Mirai.Net/Data/Shared/Friend.cs @@ -3,9 +3,15 @@ namespace Mirai.Net.Data.Shared; -public class Friend +/// +/// 好友 +/// +public record Friend { - [JsonIgnore] public Profile Profile => this.GetFriendProfileAsync().GetAwaiter().GetResult(); + /// + /// 好友的资料 + /// + [JsonIgnore] public Profile FriendProfile => this.GetFriendProfileAsync().GetAwaiter().GetResult(); /// /// 好友的QQ号 diff --git a/Mirai.Net/Data/Shared/Genders.cs b/Mirai.Net/Data/Shared/Genders.cs index af0f15e..b3398b2 100644 --- a/Mirai.Net/Data/Shared/Genders.cs +++ b/Mirai.Net/Data/Shared/Genders.cs @@ -3,14 +3,29 @@ namespace Mirai.Net.Data.Shared; +/// +/// 发送者的性别 +/// public enum Genders { - [EnumMember(Value = "MALE")] [Description("MALE")] + /// + /// 男性 + /// + [EnumMember(Value = "MALE")] + [Description("MALE")] Male, - [EnumMember(Value = "FEMALE")] [Description("FEMALE")] + /// + /// 女性 + /// + [EnumMember(Value = "FEMALE")] + [Description("FEMALE")] Female, - [EnumMember(Value = "UNKNOWN")] [Description("UNKNOWN")] + /// + /// 未知 + /// + [EnumMember(Value = "UNKNOWN")] + [Description("UNKNOWN")] Unknown } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/Group.cs b/Mirai.Net/Data/Shared/Group.cs index b525b85..3ce9091 100644 --- a/Mirai.Net/Data/Shared/Group.cs +++ b/Mirai.Net/Data/Shared/Group.cs @@ -3,7 +3,10 @@ namespace Mirai.Net.Data.Shared; -public class Group +/// +/// 群 +/// +public record Group { /// /// 群号 diff --git a/Mirai.Net/Data/Shared/GroupSetting.cs b/Mirai.Net/Data/Shared/GroupSetting.cs index 6671db1..01ae1d0 100644 --- a/Mirai.Net/Data/Shared/GroupSetting.cs +++ b/Mirai.Net/Data/Shared/GroupSetting.cs @@ -2,7 +2,10 @@ namespace Mirai.Net.Data.Shared; -public class GroupSetting +/// +/// 群设置 +/// +public record GroupSetting { /// /// 群名称 diff --git a/Mirai.Net/Data/Shared/ImageUploadTargets.cs b/Mirai.Net/Data/Shared/ImageUploadTargets.cs index 7aa1bab..a44272b 100644 --- a/Mirai.Net/Data/Shared/ImageUploadTargets.cs +++ b/Mirai.Net/Data/Shared/ImageUploadTargets.cs @@ -3,14 +3,29 @@ namespace Mirai.Net.Data.Shared; +/// +/// 图片上传目标 +/// public enum ImageUploadTargets { - [Description("friend")] [EnumMember(Value = "friend")] + /// + /// 好友 + /// + [Description("friend")] + [EnumMember(Value = "friend")] Friend, - [Description("group")] [EnumMember(Value = "group")] + /// + /// 群 + /// + [Description("group")] + [EnumMember(Value = "group")] Group, - [Description("temp")] [EnumMember(Value = "temp")] + /// + /// 临时消息 + /// + [Description("temp")] + [EnumMember(Value = "temp")] Temp } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/Member.cs b/Mirai.Net/Data/Shared/Member.cs index 12ed8f7..bcd2f09 100644 --- a/Mirai.Net/Data/Shared/Member.cs +++ b/Mirai.Net/Data/Shared/Member.cs @@ -7,10 +7,13 @@ namespace Mirai.Net.Data.Shared; /// /// 群成员 /// -public class Member +public record Member { - [JsonIgnore] public Profile Profile => this.GetMemberProfileAsync().GetAwaiter().GetResult(); - + /// + /// 群员资料 + /// + [JsonIgnore] public Profile MemberProfile => this.GetMemberProfileAsync().GetAwaiter().GetResult(); + /// /// 群员的QQ号 /// @@ -59,4 +62,4 @@ public class Member /// [JsonProperty("group")] public Group Group { get; set; } -} \ No newline at end of file +} diff --git a/Mirai.Net/Data/Shared/NewFriendRequestHandlers.cs b/Mirai.Net/Data/Shared/NewFriendRequestHandlers.cs index 333b5ba..c420621 100644 --- a/Mirai.Net/Data/Shared/NewFriendRequestHandlers.cs +++ b/Mirai.Net/Data/Shared/NewFriendRequestHandlers.cs @@ -1,8 +1,20 @@ namespace Mirai.Net.Data.Shared; +/// +/// 好友请求处理 +/// public enum NewFriendRequestHandlers { + /// + /// 同意 + /// Approve = 0, + /// + /// 拒绝 + /// Reject = 1, + /// + /// 拒绝并屏蔽 + /// RejectAndBlock = 2 } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/NewInvitationRequestHandlers.cs b/Mirai.Net/Data/Shared/NewInvitationRequestHandlers.cs index 0959301..7349593 100644 --- a/Mirai.Net/Data/Shared/NewInvitationRequestHandlers.cs +++ b/Mirai.Net/Data/Shared/NewInvitationRequestHandlers.cs @@ -1,7 +1,16 @@ namespace Mirai.Net.Data.Shared; +/// +/// 新入群邀请处理 +/// public enum NewInvitationRequestHandlers { + /// + /// 同意 + /// Approve, + /// + /// 拒绝 + /// Reject } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/NewMemberRequestHandlers.cs b/Mirai.Net/Data/Shared/NewMemberRequestHandlers.cs index d905352..f76975d 100644 --- a/Mirai.Net/Data/Shared/NewMemberRequestHandlers.cs +++ b/Mirai.Net/Data/Shared/NewMemberRequestHandlers.cs @@ -1,10 +1,28 @@ namespace Mirai.Net.Data.Shared; +/// +/// 新成员进群处理 +/// public enum NewMemberRequestHandlers { + /// + /// 同意 + /// Approve = 0, + /// + /// 拒绝 + /// Reject = 1, + /// + /// 忽略 + /// Dismiss = 2, + /// + /// 拒绝并拉黑 + /// RejectAndBlock = 3, + /// + /// 忽略 + /// DismissAndBlock = 4 } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/OtherClient.cs b/Mirai.Net/Data/Shared/OtherClient.cs index 78537b4..46df0c9 100644 --- a/Mirai.Net/Data/Shared/OtherClient.cs +++ b/Mirai.Net/Data/Shared/OtherClient.cs @@ -2,9 +2,18 @@ namespace Mirai.Net.Data.Shared; -public class OtherClient +/// +/// 其它客户端 +/// +public record OtherClient { - [JsonProperty("id")] public string Id { get; set; } + /// + /// 客户端id + /// + [JsonProperty("id")] public int Id { get; set; } + /// + /// 平台 + /// [JsonProperty("platform")] public string Platform { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/Permissions.cs b/Mirai.Net/Data/Shared/Permissions.cs index 37facd0..392cb97 100644 --- a/Mirai.Net/Data/Shared/Permissions.cs +++ b/Mirai.Net/Data/Shared/Permissions.cs @@ -3,14 +3,29 @@ namespace Mirai.Net.Data.Shared; +/// +/// 群内权限 +/// public enum Permissions { - [EnumMember(Value = "OWNER")] [Description("OWNER")] + /// + /// 群主 + /// + [EnumMember(Value = "OWNER")] + [Description("OWNER")] Owner, - [EnumMember(Value = "ADMINISTRATOR")] [Description("ADMINISTRATOR")] + /// + /// 管理员 + /// + [EnumMember(Value = "ADMINISTRATOR")] + [Description("ADMINISTRATOR")] Administrator, - [EnumMember(Value = "MEMBER")] [Description("MEMBER")] + /// + /// 群员 + /// + [EnumMember(Value = "MEMBER")] + [Description("MEMBER")] Member } \ No newline at end of file diff --git a/Mirai.Net/Data/Shared/Profile.cs b/Mirai.Net/Data/Shared/Profile.cs index 2f93df1..2ec4690 100644 --- a/Mirai.Net/Data/Shared/Profile.cs +++ b/Mirai.Net/Data/Shared/Profile.cs @@ -3,14 +3,29 @@ namespace Mirai.Net.Data.Shared; -public class Profile +/// +/// 资料卡 +/// +public record Profile { + /// + /// 昵称 + /// [JsonProperty("nickname")] public string NickName { get; set; } + /// + /// 邮箱 + /// [JsonProperty("email")] public string Email { get; set; } + /// + /// 年龄 + /// [JsonProperty("age")] public string Age { get; set; } + /// + /// 等级 + /// [JsonProperty("level")] public string Level { get; set; } /// @@ -19,6 +34,9 @@ public class Profile [JsonProperty("sign")] public string Signature { get; set; } + /// + /// 性别 + /// [JsonProperty("sex")] [JsonConverter(typeof(StringEnumConverter))] public Genders Gender { get; set; } diff --git a/Mirai.Net/Mirai.Net.csproj b/Mirai.Net/Mirai.Net.csproj index da04a1b..c65ee44 100644 --- a/Mirai.Net/Mirai.Net.csproj +++ b/Mirai.Net/Mirai.Net.csproj @@ -1,23 +1,33 @@ - netstandard2.0 latest true - 2.4.0-Pre1 + 2.5.1 Mirai.Net + latest 基于mirai-api-http的轻量级mirai社区sdk Copyright 2022 AHpx reserve all rights. https://github.com/SinoAHpx/Mirai.Net AGPL-3.0-only https://github.com/SinoAHpx/Mirai.Net + Mirai, QQBot, mirai-api-http + netstandard2.0 + + + + bin\Debug\Mirai.Net.xml + + + + bin\Release\Mirai.Net.xml - - - - + + + + <_Parameter1>Mirai.Net.UnitTest diff --git a/Mirai.Net/Modules/ICommandModule.cs b/Mirai.Net/Modules/ICommandModule.cs deleted file mode 100644 index c001ad7..0000000 --- a/Mirai.Net/Modules/ICommandModule.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Mirai.Net.Data.Messages; -using Mirai.Net.Data.Modules; - -namespace Mirai.Net.Modules; - -[Obsolete("请使用IModule以体验全新的命令-模块系统")] -public interface ICommandModule -{ - public bool? IsEnable { get; set; } - - [CommandTrigger(nameof(ICommandModule))] - public void Execute(MessageReceiverBase receiver, MessageBase executeMessage); -} \ No newline at end of file diff --git a/Mirai.Net/Modules/IModule.cs b/Mirai.Net/Modules/IModule.cs index 8713c56..eba7499 100644 --- a/Mirai.Net/Modules/IModule.cs +++ b/Mirai.Net/Modules/IModule.cs @@ -2,8 +2,19 @@ namespace Mirai.Net.Modules; +/// +/// 模块化接口 +/// public interface IModule { + /// + /// 执行器 + /// + /// void Execute(MessageReceiverBase @base); + + /// + /// 是否启用模块 + /// bool? IsEnable { get; set; } } \ No newline at end of file diff --git a/Mirai.Net/NuGet.config b/Mirai.Net/NuGet.config new file mode 100644 index 0000000..2261421 --- /dev/null +++ b/Mirai.Net/NuGet.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Mirai.Net/Sessions/Http/Managers/AccountManager.cs b/Mirai.Net/Sessions/Http/Managers/AccountManager.cs index 6d41059..6888314 100644 --- a/Mirai.Net/Sessions/Http/Managers/AccountManager.cs +++ b/Mirai.Net/Sessions/Http/Managers/AccountManager.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; +using Manganese.Text; using Mirai.Net.Data.Sessions; using Mirai.Net.Data.Shared; using Mirai.Net.Utils.Internal; using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace Mirai.Net.Sessions.Http.Managers; @@ -55,11 +54,11 @@ public static async Task> GetGroupsAsync() /// /// 获取某群的全部群成员 /// - public static async Task> GetGroupMembersAsync(string target) + public static async Task> GetGroupMembersAsync(string groupId) { return await GetCollectionAsync(HttpEndpoints.MemberList, new { - target + target = groupId }); } @@ -74,12 +73,12 @@ public static async Task> GetGroupMembersAsync(this Group ta /// /// 删除好友 /// - /// - public static async Task DeleteFriendAsync(string target) + /// + public static async Task DeleteFriendAsync(string friendId) { _ = await HttpEndpoints.DeleteFriend.PostJsonAsync(new { - target + target = friendId }); } @@ -103,11 +102,11 @@ public static async Task GetBotProfileAsync() /// /// 获取好友资料 /// - public static async Task GetFriendProfileAsync(string target) + public static async Task GetFriendProfileAsync(string friendId) { return await GetProfileAsync(HttpEndpoints.FriendProfile, new { - target + target = friendId }); } @@ -123,12 +122,12 @@ public static async Task GetFriendProfileAsync(this Friend target) /// 获取群员资料 /// /// - /// 群号 - public static async Task GetMemberProfileAsync(string id, string target) + /// 群号 + public static async Task GetMemberProfileAsync(string id, string memberId) { return await GetProfileAsync(HttpEndpoints.MemberProfile, new { - target, + target = memberId, memberId = id }); } @@ -141,5 +140,16 @@ public static async Task GetMemberProfileAsync(this Member member) return await GetMemberProfileAsync(member.Id, member.Group.Id); } + /// + /// 获取任意QQ的资料(需要mirai-api-http 2.4.0及以上) + /// + public static async Task GetProfileAsync(string target) + { + return await GetProfileAsync(HttpEndpoints.UserProfile, new + { + target + }); + } + #endregion } \ No newline at end of file diff --git a/Mirai.Net/Sessions/Http/Managers/ConsoleManager.cs b/Mirai.Net/Sessions/Http/Managers/ConsoleManager.cs new file mode 100644 index 0000000..e893309 --- /dev/null +++ b/Mirai.Net/Sessions/Http/Managers/ConsoleManager.cs @@ -0,0 +1,246 @@ +using Manganese.Array; +using Mirai.Net.Data.Events; +using Mirai.Net.Data.Messages; +using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Sessions; +using Mirai.Net.Utils.Internal; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Reactive; +using System.Threading.Tasks; +using System.Linq; + +namespace Mirai.Net.Sessions.Http.Managers +{ + + /// + /// 控制台管理器 + /// + public static class ConsoleManager + { +#nullable enable + /// + /// 登录指令 + /// 登录后的账号会生成新的SessionKey 使用已有的MiraiBot实例是无法连接到该账号的 + /// 如果配置相同(QQ Address VerifyKey)可以再次在同一个MiraiBot上使用LaunchAsync方法之后就可以正常使用了 + /// 在LaunchAsync之前使用的任何Subscribe方法都不会应用于新登录的账号 必须重新Subscribe + /// + /// + /// + /// + /// + public static async Task LoginAsync(string qq, string password, Protocol? protocol = null) + { + var command = protocol is null + ? new PlainMessage[] { $"/login {qq} {password}" } + : new PlainMessage[] { $"/login {qq} {password} {protocol.ToString()}" }; + + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } +#nullable restore + + /// + /// 关闭指令 + /// + /// + public static async Task StopAsync() + { + var command = new PlainMessage[] { "/stop" }; + + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } + + /// + /// 登出指令 + /// + /// + /// + public static async Task LogoutAsync(string qq) + { + var command = new PlainMessage[] { $"/logout {qq}" }; + + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } + + /// + /// 执行命令 + /// + /// + /// 命令与参数 + /// + /// + public static async Task ExecuteCommandAsync(string commands) + { + var command = new PlainMessage[] { commands }; + + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } + + /// + /// 执行命令 + /// + /// + /// 命令与参数 + /// + /// + public static async Task ExecuteCommandAsync(params string[] commands) + { + var builder = new System.Text.StringBuilder(); + + foreach (var item in commands) + { + builder.Append(item); + + builder.Append(' '); + } + + var command = new PlainMessage[] { builder.ToString().Trim() }; + + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } + + /// + /// 使用消息执行命令 + /// + /// + /// 命令与参数 如果使用纯文本的话需要将整条命令写在同一个PlainMessage中 + /// 控制台支持以不同消息类型作为指令的参数, 执行命令需要以消息类型作为参数, 若执行纯文本的命令, 构建多个文本格式的消息控制台会将第一个消息作为指令名, 后续消息作为参数 + /// + /// + public static async Task ExecuteMessageCommandAsync(IEnumerable command) + { + await HttpEndpoints.ExecuteCommand.PostJsonAsync(new + { + command + }); + } + + /// + /// 注册命令 + /// 注册的命令会直接覆盖已有的指令(包括控制台内置的指令) + /// + /// 指令名 + /// 使用说明 + /// 命令描述 + /// + public static async Task RegisterCommandAsync(string name, string usage, string description) + { + await HttpEndpoints.RegisterCommand.PostJsonAsync(new + { + name, + usage, + description + }); + } + + /// + /// 注册命令 + /// 注册的命令会直接覆盖已有的指令(包括控制台内置的指令) + /// + /// 命令 + /// + public static async Task RegisterCommandAsync(Command command) + { + await HttpEndpoints.RegisterCommand.PostJsonAsync(command); + } + + /// + /// 注册用的命令 + /// + public record Command + { + + /// + /// 指令名 + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// 指令别名 + /// + [JsonProperty("alias")] + public IEnumerable Alias { get; set; } + + /// + /// 使用说明 + /// + [JsonProperty("usage")] + public string Usage { get; set; } + + /// + /// 命令描述 + /// + [JsonProperty("description")] + public string Description { get; set; } + } + + /// + /// 命令被执行 + /// + public record CommandExecutedEvent : EventBase + { + /// + /// 事件类型 + /// + public override Events Type { get; set; } = Events.CommandExecuted; + + /// + /// 命令名称 + /// + [JsonProperty("name")] public string Name { get; set; } +#nullable enable + /// + /// 发送命令的好友, 从控制台发送为 null + /// + [JsonProperty("friend")] public string? FriendId { get; set; } + + /// + /// 发送命令的群成员, 从控制台发送为 null + /// + [JsonProperty("member")] public string? MemberId { get; set; } +#nullable restore + /// + /// 指令的参数, 以消息类型传递 + /// + [JsonProperty("args")] public dynamic Args { get; set; } + } + + } + + /// + /// 可选的登录协议 + /// + public enum Protocol + { + /// + /// 安卓手机 + /// + ANDROID_PHONE, + + /// + /// 安卓平板 + /// + ANDROID_PAD, + + /// + /// 安卓手表 + /// + ANDROID_WATCH + } +} diff --git a/Mirai.Net/Sessions/Http/Managers/FileManager.cs b/Mirai.Net/Sessions/Http/Managers/FileManager.cs index 77ea187..5818035 100644 --- a/Mirai.Net/Sessions/Http/Managers/FileManager.cs +++ b/Mirai.Net/Sessions/Http/Managers/FileManager.cs @@ -1,32 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; -using Flurl.Http; +using Flurl.Http; +using Manganese.Text; using Mirai.Net.Data.Sessions; using Mirai.Net.Data.Shared; using Mirai.Net.Utils.Internal; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using File = Mirai.Net.Data.Shared.File; namespace Mirai.Net.Sessions.Http.Managers; +/// +/// 文件管理器 +/// public static class FileManager { /// /// 获取群文件列表 /// - /// + /// /// 附带下载信息,默认不附带 /// 文件夹id,空字符串即为根目录 /// - [Obsolete("此方法因为mirai-api-http的缺陷,存在严重性能问题(只能获取少量群文件)。")] - public static async Task> GetFilesAsync(string target, bool? withDownloadInfo = null, + public static async Task> GetFilesAsync(string groupId, bool? withDownloadInfo = null, string folderId = "") { var result = await HttpEndpoints.FileList.GetAsync(new { - target, + target = groupId, withDownloadInfo, id = folderId }); @@ -39,15 +43,15 @@ public static async Task> GetFilesAsync(string target, bool? w /// /// 获取群文件信息 /// - /// 群号 + /// 群号 /// 文件id /// /// - public static async Task GetFileAsync(string target, string fileId, bool? withDownloadInfo = null) + public static async Task GetFileAsync(string groupId, string fileId, bool? withDownloadInfo = null) { var result = await HttpEndpoints.FileInfo.GetAsync(new { - target, + target = groupId, id = fileId, withDownloadInfo }); @@ -58,15 +62,15 @@ public static async Task GetFileAsync(string target, string fileId, bool? /// /// 创建群文件夹 /// - /// + /// /// /// - public static async Task CreateFolderAsync(string target, string name) + public static async Task CreateFolderAsync(string groupId, string name) { var result = await HttpEndpoints.FileCreate.PostJsonAsync(new { id = "", - target, + target = groupId, directoryName = name }); @@ -76,13 +80,13 @@ public static async Task CreateFolderAsync(string target, string name) /// /// 删除群文件 /// - /// + /// /// - public static async Task DeleteFileAsync(string target, string fileId) + public static async Task DeleteFileAsync(string groupId, string fileId) { _ = await HttpEndpoints.FileDelete.PostJsonAsync(new { - target, + target = groupId, id = fileId }); } @@ -90,14 +94,14 @@ public static async Task DeleteFileAsync(string target, string fileId) /// /// 移动群文件 /// - /// + /// /// 移动文件id /// 移动目标文件夹id - public static async Task MoveFileAsync(string target, string fileId, string destination) + public static async Task MoveFileAsync(string groupId, string fileId, string destination) { _ = await HttpEndpoints.FileMove.PostJsonAsync(new { - target, + target = groupId, id = fileId, movoTo = destination }); @@ -106,41 +110,53 @@ public static async Task MoveFileAsync(string target, string fileId, string dest /// /// 重命名群文件 /// - /// + /// /// 重命名文件id /// 新文件名 - public static async Task RenameFileAsync(string target, string fileId, string newName) + public static async Task RenameFileAsync(string groupId, string fileId, string newName) { _ = await HttpEndpoints.FileRename.PostJsonAsync(new { - target, + target = groupId, id = fileId, renameTo = newName }); } /// - /// 上传群文件,参考https://github.com/project-mirai/mirai-api-http/issues/456 + /// 上传群文件,修复 https://github.com/SinoAHpx/Mirai.Net/issues/72 中提到的编码错误 /// - /// 上传到哪个群 + /// 上传到哪个群 /// 文件的路径 - /// 上传路径,例如/xx(不可以指定文件名,默认为上传到根目录) + /// 上传路径,例如/xx + /// 上传的文件名 /// 有几率返回null,这是个mirai-api-http的玄学问题 - public static async Task UploadFileAsync(string target, string filePath, string uploadPath = "/") + public static async Task UploadFileAsync(string groupId, string filePath, string uploadPath = "/", string fileName = null) { - uploadPath ??= $"/{filePath.FetchFileName()}"; + fileName ??= Path.GetFileName(filePath); - var url = $"http://{MiraiBot.Instance.Address}/{HttpEndpoints.FileUpload.GetDescription()}"; + var url = $"http://{MiraiBot.Instance.Address.HttpAddress}/{HttpEndpoints.FileUpload.GetDescription()}"; - var result = await url - .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") - .PostMultipartAsync(x => x - .AddString("type", "group") - .AddString("path", uploadPath) - .AddString("target", target) - .AddFile("file", filePath)); + using var fileStream = System.IO.File.Open(filePath, FileMode.Open); + var streamContent = new StreamContent(fileStream); + var hackedFileName = new string(Encoding.UTF8.GetBytes(fileName).Select(b => (char)b).ToArray()); + streamContent.Headers.Add("Content-Disposition", $@"form-data; name=""file""; filename=""{hackedFileName}""; filename*=""{hackedFileName}"""); + + var response = await url + .PostAsync(new MultipartFormDataContent + { + { new StringContent(MiraiBot.Instance.HttpSessionKey), "sessionKey" }, + { new StringContent("group"), "type" }, + { new StringContent(groupId), "target" }, + { new StringContent(uploadPath), "path" }, + { streamContent, "file", fileName } + }) + .ReceiveString(); + + + response.EnsureSuccess("这大抵是个玄学问题罢。"); - var re = (await result.GetStringAsync()).ToJObject(); + var re = response.ToJObject(); return !re.ContainsKey("name") ? null : re.ToObject(); } @@ -154,7 +170,7 @@ public static async Task UploadFileAsync(string target, string filePath, s public static async Task<(string, string)> UploadImageAsync(string imagePath, ImageUploadTargets imageUploadTargets = ImageUploadTargets.Group) { - var url = $"http://{MiraiBot.Instance.Address}/{HttpEndpoints.UploadImage.GetDescription()}"; + var url = $"http://{MiraiBot.Instance.Address.HttpAddress}/{HttpEndpoints.UploadImage.GetDescription()}"; var result = await url .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") @@ -163,6 +179,7 @@ public static async Task UploadFileAsync(string target, string filePath, s .AddFile("img", imagePath)); var re = await result.GetStringAsync(); + re.EnsureSuccess("这大抵是个玄学问题罢。"); return (re.Fetch("imageId"), re.Fetch("url")); } @@ -174,7 +191,7 @@ public static async Task UploadFileAsync(string target, string filePath, s /// item1: 语音id,item2:语音url public static async Task<(string, string)> UploadVoiceAsync(string voicePath) { - var url = $"http://{MiraiBot.Instance.Address}/{HttpEndpoints.UploadVoice.GetDescription()}"; + var url = $"http://{MiraiBot.Instance.Address.HttpAddress}/{HttpEndpoints.UploadVoice.GetDescription()}"; var result = await url .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") @@ -183,6 +200,7 @@ public static async Task UploadFileAsync(string target, string filePath, s .AddFile("voice", voicePath)); var re = await result.GetStringAsync(); + re.EnsureSuccess("这大抵是个玄学问题罢。"); return (re.Fetch("voiceId"), re.Fetch("url")); } diff --git a/Mirai.Net/Sessions/Http/Managers/GroupManager.cs b/Mirai.Net/Sessions/Http/Managers/GroupManager.cs index c0e926e..f7a8970 100644 --- a/Mirai.Net/Sessions/Http/Managers/GroupManager.cs +++ b/Mirai.Net/Sessions/Http/Managers/GroupManager.cs @@ -1,15 +1,21 @@ -using System; -using System.Linq; -using System.Threading.Tasks; +using Manganese.Text; using Mirai.Net.Data.Messages; using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Messages.Receivers; using Mirai.Net.Data.Sessions; using Mirai.Net.Data.Shared; using Mirai.Net.Utils.Internal; using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace Mirai.Net.Sessions.Http.Managers; +/// +/// 群管理器 +/// public static class GroupManager { #region Mute @@ -17,15 +23,15 @@ public static class GroupManager /// /// 禁言某群员 /// - /// - /// - /// - public static async Task MuteAsync(string target, string group, int time) + /// 禁言对象的QQ号 + /// 禁言对象所在的群号 + /// 禁言的时长 + public static async Task MuteAsync(string memberId, string group, int time) { var payload = new { target = group, - memberId = target, + memberId = memberId, time }; @@ -33,16 +39,16 @@ public static async Task MuteAsync(string target, string group, int time) } /// - public static async Task MuteAsync(string target, string group, TimeSpan time) + public static async Task MuteAsync(string memberId, string group, TimeSpan time) { - await MuteAsync(target, group, Convert.ToInt32(time.TotalSeconds)); + await MuteAsync(memberId, group, Convert.ToInt32(time.TotalSeconds)); } /// /// 禁言某群员 /// - /// - /// + /// 禁言的对象 + /// 禁言的时长 public static async Task MuteAsync(this Member member, int time) { await MuteAsync(member.Id, member.Group.Id, time); @@ -61,14 +67,14 @@ public static async Task MuteAsync(this Member member, TimeSpan time) /// /// 取消禁言 /// - /// - /// - public static async Task UnMuteAsync(string target, string group) + /// 取消禁言对象的QQ号 + /// 取消禁言对象所在的群号 + public static async Task UnMuteAsync(string memberId, string group) { var payload = new { target = group, - memberId = target + memberId }; await HttpEndpoints.Unmute.PostJsonAsync(payload); @@ -77,7 +83,7 @@ public static async Task UnMuteAsync(string target, string group) /// /// 取消禁言 /// - /// + /// 取消禁言的对象 public static async Task UnMuteAsync(this Member member) { await UnMuteAsync(member.Id, member.Group.Id); @@ -90,15 +96,15 @@ public static async Task UnMuteAsync(this Member member) /// /// 踢出某群员 /// - /// - /// - /// - public static async Task KickAsync(string target, string group, string message = "") + /// 踢出对象的QQ号 + /// 踢出对象所在的群号 + /// 踢出的原因 + public static async Task KickAsync(string memberId, string group, string message = "") { var payload = new { target = group, - memberId = target, + memberId = memberId, msg = message }; @@ -108,8 +114,8 @@ public static async Task KickAsync(string target, string group, string message = /// /// 踢出某群员 /// - /// - /// + /// 踢出的对象 + /// 踢出的原因 public static async Task KickAsync(this Member member, string message = "") { await KickAsync(member.Id, member.Group.Id); @@ -122,12 +128,12 @@ public static async Task KickAsync(this Member member, string message = "") /// /// bot退出某群 /// - /// - public static async Task LeaveAsync(string target) + /// 要退出的群号 + public static async Task LeaveAsync(string groupId) { var payload = new { - target + groupId }; await HttpEndpoints.Leave.PostJsonAsync(payload); @@ -136,7 +142,7 @@ public static async Task LeaveAsync(string target) /// /// bot退出某群 /// - /// + /// 要退出的群 public static async Task LeaveAsync(this Group group) { await LeaveAsync(group.Id); @@ -149,14 +155,14 @@ public static async Task LeaveAsync(this Group group) /// /// 全体禁言 /// - /// - /// 是否禁言 - public static async Task MuteAllAsync(string target, bool mute = true) + /// 目标群号 + /// 是否禁言。 false为解除禁言,true为禁言 + public static async Task MuteAllAsync(string groupId, bool mute = true) { var endpoint = mute ? HttpEndpoints.MuteAll : HttpEndpoints.UnmuteAll; var payload = new { - target + groupId }; await endpoint.PostJsonAsync(payload); @@ -165,8 +171,8 @@ public static async Task MuteAllAsync(string target, bool mute = true) /// /// 全体禁言 /// - /// - /// 是否禁言 + /// 目标群 + /// 是否禁言。 false为解除禁言,true为禁言 public static async Task MuteAllAsync(this Group group, bool mute = true) { await MuteAllAsync(group.Id, mute); @@ -180,11 +186,13 @@ public static async Task MuteAllAsync(this Group group, bool mute = true) /// 设置精华消息 /// /// 消息id - public static async Task SetEssenceMessageAsync(string messageId) + /// 目标群号 + public static async Task SetEssenceMessageAsync(string messageId, string groupId) { var payload = new { - target = messageId + target = groupId, + messageId }; await HttpEndpoints.SetEssence.PostJsonAsync(payload); @@ -196,12 +204,11 @@ public static async Task SetEssenceMessageAsync(string messageId) /// public static async Task SetEssenceMessageAsync(this MessageReceiverBase receiver) { - var payload = new + if (receiver is GroupMessageReceiver groupMessageReceiver) { - target = receiver.MessageChain.OfType().First().MessageId - }; - - await HttpEndpoints.SetEssence.PostJsonAsync(payload); + await SetEssenceMessageAsync(receiver.MessageChain.OfType().Single().MessageId, + groupMessageReceiver.GroupId); + } } #endregion @@ -211,11 +218,11 @@ public static async Task SetEssenceMessageAsync(this MessageReceiverBase receive /// /// 获取群设置 /// - /// + /// 目标群号 /// - public static async Task GetGroupSettingAsync(string target) + public static async Task GetGroupSettingAsync(string groupId) { - var response = await HttpEndpoints.GroupConfig.GetAsync(new { target }); + var response = await HttpEndpoints.GroupConfig.GetAsync(new { target = groupId }); return JsonConvert.DeserializeObject(response); } @@ -234,13 +241,13 @@ public static async Task GetGroupSettingAsync(this Group group) /// /// 修改群设置 /// - /// - /// - public static async Task SetGroupSettingAsync(string target, GroupSetting setting) + /// 目标群号 + /// 群设置 + public static async Task SetGroupSettingAsync(string groupId, GroupSetting setting) { var payload = new { - target, + target = groupId, config = setting }; @@ -250,8 +257,8 @@ public static async Task SetGroupSettingAsync(string target, GroupSetting settin /// /// 修改群设置 /// - /// - /// + /// 目标群 + /// 群设置 public static async Task SetGroupSettingAsync(this Group group, GroupSetting setting) { await SetGroupSettingAsync(group.Id, setting); @@ -264,8 +271,8 @@ public static async Task SetGroupSettingAsync(this Group group, GroupSetting set /// /// 获取群员 /// - /// - /// + /// 目标的QQ号 + /// 目标群号 /// public static async Task GetMemberAsync(string memberQQ, string group) { @@ -281,8 +288,8 @@ public static async Task GetMemberAsync(string memberQQ, string group) /// /// 修改群员设置,需要相关的权限 /// - /// - /// + /// 目标的QQ号 + /// 目标群号 /// 群名片, 需要管理员权限 /// 群头衔, 需要群主权限 /// @@ -306,4 +313,74 @@ public static async Task SetMemberInfoAsync(string memberQQ, string grou } #endregion + + #region Anno + + /// + /// 获取指定群公告列表 + /// + /// 目标群号 + /// 分页参数 + /// 分页参数,默认10 + /// + public static async Task> GetGroupAnnouncementAsync(string group, long offset = 0, long size = 10) + { + var response = await HttpEndpoints.GetAnnouncement.GetAsync(new + { + id = group, + offset, + size + }); + + return JsonConvert.DeserializeObject>(response.Fetch("data")); + } + + /// + /// 向指定群发布群公告 + /// + /// 目标群号 + /// 公告内容 + /// 是否置顶 + /// + public static async Task PublishGroupAnnouncementAsync(string group, string content, bool pinned = true) + { + var setting = new AnnouncementSetting + { + Target = group, + Content = content, + Pinned = pinned + }; + + return await PublishGroupAnnouncementAsync(setting); + } + + /// + /// 向指定群发布群公告 + /// + /// 群公告设置 + /// + public static async Task PublishGroupAnnouncementAsync(AnnouncementSetting announcementSetting) + { + var response = await HttpEndpoints.PubAnnouncement.PostJsonAsync(announcementSetting); + + return JsonConvert.DeserializeObject(response.Fetch("data")); + } + + /// + /// 删除指定群中一条公告 + /// + /// 目标群号 + /// 群公告唯一id + /// + public static async Task DeleteGroupAnnouncementAsync(string group, string fid) + { + await HttpEndpoints.DelAnnouncement.PostJsonAsync(new + { + id = group, + fid + }); + } + + #endregion + } \ No newline at end of file diff --git a/Mirai.Net/Sessions/Http/Managers/MessageManager.cs b/Mirai.Net/Sessions/Http/Managers/MessageManager.cs index 763e9e9..3b96e8f 100644 --- a/Mirai.Net/Sessions/Http/Managers/MessageManager.cs +++ b/Mirai.Net/Sessions/Http/Managers/MessageManager.cs @@ -1,19 +1,25 @@ -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; +using Manganese.Text; + using Mirai.Net.Data.Messages; using Mirai.Net.Data.Sessions; using Mirai.Net.Data.Shared; -using Mirai.Net.Utils.Internal; -using Mirai.Net.Utils.Scaffolds; -using Newtonsoft.Json; +using Mirai.Net.Utils.Internal; + +using Newtonsoft.Json; + +using System.Collections.Generic; +using System.Threading.Tasks; namespace Mirai.Net.Sessions.Http.Managers; +/// +/// 消息管理器 +/// public static class MessageManager { - #region Private helpers + #region Non-Public helpers - private static async Task SendMessageAsync(HttpEndpoints endpoints, object payload) + internal static async Task SendMessageAsync(HttpEndpoints endpoints, object payload) { var response = await endpoints.PostJsonAsync(payload); @@ -27,29 +33,54 @@ private static async Task SendMessageAsync(HttpEndpoints endpoints, obje /// /// 由消息id获取一条消息 /// + /// 消息id + /// 好友id或群id /// /// - public static async Task GetMessageReceiverByIdAsync(string messageId) where T : MessageReceiverBase + public static async Task GetMessageReceiverByIdAsync(string messageId, string target) where T : MessageReceiverBase { var response = await HttpEndpoints.MessageFromId.GetAsync(new { - id = messageId + messageId, + target }); - return JsonConvert.DeserializeObject(response); + var data = response.Fetch("data")!; + var receiver = JsonConvert.DeserializeObject(data)!; + receiver.MessageChain = data.Fetch("messageChain").DeserializeMessageChain(); + + return receiver; } - + + /// + /// 获取漫游消息(目前仅支持好友) + /// + /// 好友id或群id + /// 起始时间, UTC+8 时间戳, 单位为秒. 可以为 0, 即表示从可以获取的最早的消息起. 负数将会被看是 0. + /// 结束时间, UTC+8 时间戳, 单位为秒. 可以为 long.MaxValue, 即表示到可以获取的最晚的消息为止. 低于 timeStart 的值将会被看作是 timeStart 的值. + /// + public static async Task> GetRoamingMessagesAsync(string target, long timeStart = 0, long timeEnd = 0) + { + var response = await HttpEndpoints.RoamingMessages.PostJsonAsync(new + { + timeStart, + timeEnd, + target = target.ToInt64(), + }); + return JsonConvert.DeserializeObject>(response.Fetch("data")); + } + /// /// 发送好友消息 /// - /// + /// /// /// - public static async Task SendFriendMessageAsync(string target, params MessageBase[] chain) + public static async Task SendFriendMessageAsync(string friendId, MessageChain chain) { var payload = new { - target, + target = friendId, messageChain = chain }; @@ -62,7 +93,7 @@ public static async Task SendFriendMessageAsync(string target, params Me /// /// /// - public static async Task SendFriendMessageAsync(this Friend friend, params MessageBase[] chain) + public static async Task SendFriendMessageAsync(this Friend friend, MessageChain chain) { return await SendFriendMessageAsync(friend.Id, chain); } @@ -70,14 +101,14 @@ public static async Task SendFriendMessageAsync(this Friend friend, para /// /// 发送群消息 /// - /// + /// /// /// - public static async Task SendGroupMessageAsync(string target, params MessageBase[] chain) + public static async Task SendGroupMessageAsync(string groupId, MessageChain chain) { var payload = new { - target, + target = groupId, messageChain = chain }; @@ -90,7 +121,7 @@ public static async Task SendGroupMessageAsync(string target, params Mes /// /// /// - public static async Task SendGroupMessageAsync(this Group group, params MessageBase[] chain) + public static async Task SendGroupMessageAsync(this Group group, MessageChain chain) { return await SendGroupMessageAsync(group.Id, chain); } @@ -102,7 +133,7 @@ public static async Task SendGroupMessageAsync(this Group group, params /// /// /// - public static async Task SendTempMessageAsync(string qq, string group, params MessageBase[] chain) + public static async Task SendTempMessageAsync(string qq, string group, MessageChain chain) { var payload = new { @@ -120,7 +151,7 @@ public static async Task SendTempMessageAsync(string qq, string group, p /// /// /// - public static async Task SendTempMessageAsync(this Member member, params MessageBase[] chain) + public static async Task SendTempMessageAsync(this Member member, MessageChain chain) { return await SendTempMessageAsync(member.Id, member.Group.Id, chain); } @@ -147,11 +178,13 @@ public static async Task SendNudgeAsync(string target, string subject, MessageRe /// 撤回消息 /// /// 消息id - public static async Task RecallAsync(string messageId) + /// 好友id或群id + public static async Task RecallAsync(string messageId, string target) { var payload = new { - target = messageId + target, + messageId }; await HttpEndpoints.Recall.PostJsonAsync(payload); @@ -165,7 +198,7 @@ public static async Task RecallAsync(string messageId) /// /// public static async Task QuoteFriendMessageAsync(string target, string messageId, - params MessageBase[] chain) + MessageChain chain) { var payload = new { @@ -185,19 +218,19 @@ public static async Task QuoteFriendMessageAsync(string target, string m /// /// public static async Task QuoteFriendMessageAsync(this Friend friend, string messageId, - params MessageBase[] chain) + MessageChain chain) { return await QuoteFriendMessageAsync(friend.Id, messageId, chain); } /// - /// 回复群消息 + /// 回复消息 /// /// /// /// /// - public static async Task QuoteGroupMessageAsync(string target, string messageId, params MessageBase[] chain) + public static async Task QuoteGroupMessageAsync(string target, string messageId, MessageChain chain) { var payload = new { @@ -217,7 +250,7 @@ public static async Task QuoteGroupMessageAsync(string target, string me /// /// public static async Task QuoteGroupMessageAsync(this Group group, string messageId, - params MessageBase[] chain) + MessageChain chain) { return await QuoteGroupMessageAsync(group.Id, messageId, chain); } @@ -231,7 +264,7 @@ public static async Task QuoteGroupMessageAsync(this Group group, string /// /// public static async Task QuoteTempMessageAsync(string memberId, string group, string messageId, - params MessageBase[] chain) + MessageChain chain) { var payload = new { @@ -252,156 +285,10 @@ public static async Task QuoteTempMessageAsync(string memberId, string g /// /// public static async Task QuoteTempMessageAsync(this Member member, string messageId, - params MessageBase[] chain) - { - return await QuoteTempMessageAsync(member.Group.Id, member.Group.Id, messageId); - } - - #endregion - - #region Exposed overloads - - /// - /// 发送好友消息 - /// - /// - /// - /// - public static async Task SendFriendMessageAsync(string target, string message) - { - return await SendFriendMessageAsync(target, message.Append()); - } - - /// - /// 发送好友消息 - /// - /// - /// - /// - public static async Task SendFriendMessageAsync(this Friend target, string message) - { - return await SendFriendMessageAsync(target, message.Append()); - } - - - /// - /// 发送临时消息 - /// - /// - /// - /// - /// - public static async Task SendTempMessageAsync(string target, string group, string message) - { - return await SendTempMessageAsync(target, group, message.Append()); - } - - /// - /// 发送临时消息 - /// - /// - /// - /// - public static async Task SendTempMessageAsync(this Member member, string message) - { - return await SendTempMessageAsync(member, message.Append()); - } - - /// - /// 发送群消息 - /// - /// - /// - /// - public static async Task SendGroupMessageAsync(string target, string message) - { - return await SendGroupMessageAsync(target, message.Append()); - } - - /// - /// 发送群消息 - /// - /// - /// - /// - public static async Task SendGroupMessageAsync(this Group group, string message) - { - return await SendGroupMessageAsync(group, message.Append()); - } - - /// - /// 引用好友消息 - /// - /// - /// - /// - /// - public static async Task QuoteFriendMessageAsync(string target, string messageId, string message) - { - return await QuoteFriendMessageAsync(target, messageId, message.Append()); - } - - /// - /// 引用好友消息 - /// - /// - /// - /// - /// - public static async Task QuoteFriendMessageAsync(this Friend target, string messageId, string message) - { - return await QuoteFriendMessageAsync(target, messageId, message.Append()); - } - - /// - /// 引用群消息 - /// - /// - /// - /// - /// - public static async Task QuoteGroupMessageAsync(string target, string messageId, string message) - { - return await QuoteGroupMessageAsync(target, messageId, message.Append()); - } - - /// - /// 引用群消息 - /// - /// - /// - /// - /// - public static async Task QuoteGroupMessageAsync(this Group target, string messageId, string message) - { - return await QuoteGroupMessageAsync(target, messageId, message.Append()); - } - - /// - /// 引用临时消息 - /// - /// - /// - /// - /// - /// - public static async Task QuoteTempMessageAsync(string memberId, string group, string messageId, - string message) - { - return await QuoteTempMessageAsync(memberId, group, messageId, message.Append()); - } - - /// - /// 引用临时消息 - /// - /// - /// - /// - /// - public static async Task QuoteTempMessageAsync(this Member member, string messageId, string message) + MessageChain chain) { - return await QuoteTempMessageAsync(member, messageId, message.Append()); + return await QuoteTempMessageAsync(member.Id, member.Group.Id, messageId, chain); } #endregion -} \ No newline at end of file +} diff --git a/Mirai.Net/Sessions/Http/Managers/RequestManager.cs b/Mirai.Net/Sessions/Http/Managers/RequestManager.cs index 9bd7a9f..ada530b 100644 --- a/Mirai.Net/Sessions/Http/Managers/RequestManager.cs +++ b/Mirai.Net/Sessions/Http/Managers/RequestManager.cs @@ -1,11 +1,14 @@ -using System.Threading.Tasks; -using Mirai.Net.Data.Events.Concretes.Request; +using Mirai.Net.Data.Events.Concretes.Request; using Mirai.Net.Data.Sessions; using Mirai.Net.Data.Shared; using Mirai.Net.Utils.Internal; +using System.Threading.Tasks; namespace Mirai.Net.Sessions.Http.Managers; +/// +/// 请求管理器 +/// public static class RequestManager { /// diff --git a/Mirai.Net/Sessions/MiraiBot.cs b/Mirai.Net/Sessions/MiraiBot.cs index 6ca45fa..1f92eae 100644 --- a/Mirai.Net/Sessions/MiraiBot.cs +++ b/Mirai.Net/Sessions/MiraiBot.cs @@ -1,13 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.WebSockets; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; -using Flurl; +using Flurl; +using Manganese.Text; using Mirai.Net.Data.Events; using Mirai.Net.Data.Events.Concretes.Message; using Mirai.Net.Data.Exceptions; @@ -19,6 +11,13 @@ using Mirai.Net.Sessions.Http.Managers; using Mirai.Net.Utils.Internal; using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading.Tasks; using Websocket.Client; namespace Mirai.Net.Sessions; @@ -28,6 +27,9 @@ namespace Mirai.Net.Sessions; /// public class MiraiBot : IDisposable { + /// + /// 销毁当前对象 + /// public async void Dispose() { await ReleaseAsync(); @@ -36,6 +38,9 @@ public async void Dispose() #region Exposed + /// + /// 启动bot + /// public async Task LaunchAsync() { Instance = this; @@ -59,32 +64,16 @@ public async Task LaunchAsync() [JsonIgnore] internal string HttpSessionKey { get; set; } - [JsonIgnore] - private string _address; [JsonIgnore] private string _qq; [JsonIgnore] private WebsocketClient _client; /// - /// mirai-api-http本地服务器地址,比如:localhost:114514 + /// mirai-api-http本地服务器地址,比如:localhost:114514,或者构造一个ConnectConfig对象 /// 传入错误的地址将会抛出异常 /// - public string Address - { - get => _address.TrimEnd('/').Empty("http://").Empty("https://"); - set - { - if (!value.Contains(":")) throw new InvalidAddressException($"错误的地址: {value}"); - - var split = value.Split(':'); - - if (split.Length != 2) throw new InvalidAddressException($"错误的地址: {value}"); - if (!split.Last().IsInteger()) throw new InvalidAddressException($"错误的地址: {value}"); - - _address = value; - } - } + public ConnectConfig Address { get; set; } /// /// 建立连接的QQ账号 @@ -92,38 +81,56 @@ public string Address public string QQ { get => _qq; - set => _qq = value.IsIntegerOrThrow(new InvalidQQException("错误的QQ号")); + set => _qq = value.ThrowIfNotInt64("错误的QQ号").ToString(); } /// /// Mirai.Net总是需要一个VerifyKey /// public string VerifyKey { get; set; } - + + /// + /// 群列表 + /// [JsonIgnore] - public Lazy> Groups => + public Lazy> Groups => new(() => AccountManager.GetGroupsAsync().GetAwaiter().GetResult()); - + + /// + /// 好友列表 + /// [JsonIgnore] - public Lazy> Friends => + public Lazy> Friends => new(() => AccountManager.GetFriendsAsync().GetAwaiter().GetResult()); #endregion #region Handlers + /// + /// 接收到事件 + /// [JsonIgnore] public IObservable EventReceived => _eventReceivedSubject.AsObservable(); private readonly Subject _eventReceivedSubject = new(); + /// + /// 收到消息 + /// [JsonIgnore] public IObservable MessageReceived => _messageReceivedSubject.AsObservable(); private readonly Subject _messageReceivedSubject = new(); + /// + /// 接收到未知类型的Websocket消息 + /// [JsonIgnore] public IObservable UnknownMessageReceived => _unknownMessageReceived.AsObservable(); private readonly Subject _unknownMessageReceived = new(); + /// + /// Websocket断开连接 + /// [JsonIgnore] public IObservable DisconnectionHappened => _disconnectionHappened.AsObservable(); @@ -180,7 +187,7 @@ private async Task ReleaseAsync() /// private async Task StartWebsocketListenerAsync() { - var url = $"ws://{Address}/all" + var url = $"ws://{Address.WebsocketAddress}/all" .SetQueryParam("verifyKey", VerifyKey) .SetQueryParam("qq", QQ) .ToUri(); @@ -199,68 +206,53 @@ private async Task StartWebsocketListenerAsync() .Where(message => message.MessageType == WebSocketMessageType.Text) .Subscribe(message => { - var type = GetRespondMessageType(message); var data = message.Text.Fetch("data"); - - switch (type) + if (data == null || data.IsNullOrEmpty()) { - case WebsocketMessageTypes.Message: - var receiver = ReflectionUtils.GetMessageReceiverBase(data); - - var messageChain = data - .Fetch("messageChain") - .ToJArray() - .Select(token => ReflectionUtils.GetMessageBase(token.ToString())) - .ToList(); - - receiver.MessageChain = messageChain; - - if (receiver.MessageChain.OfType().Any(x => x.Target == Instance.QQ)) - { - _eventReceivedSubject.OnNext(new AtEvent - { - Receiver = (receiver as GroupMessageReceiver)! - }); - } - - _messageReceivedSubject.OnNext(receiver); - break; - case WebsocketMessageTypes.Event: - _eventReceivedSubject.OnNext(ReflectionUtils.GetEventBase(data)); - break; - case WebsocketMessageTypes.Unknown: - _unknownMessageReceived.OnNext(data); - break; - default: - throw new ArgumentOutOfRangeException(); + throw new InvalidWebsocketReponseException("Websocket传回错误响应"); } + + ProcessWebSocketData(data); }); } - /// - /// 获取websocket收到的消息是什么类型的 - /// - /// - /// 消息,事件,未知 - private static WebsocketMessageTypes GetRespondMessageType(ResponseMessage message) + private void ProcessWebSocketData(string data) { - if (message.MessageType != WebSocketMessageType.Text || message.Text.IsNullOrEmpty()) - return WebsocketMessageTypes.Unknown; + var dataType = data.Fetch("type"); + if (dataType == null || dataType.IsNullOrEmpty()) + { + throw new InvalidWebsocketReponseException("Websocket传回错误的响应"); + } - try + if (dataType.Contains("Message")) { - var json = message.Text.Fetch("data").ToJObject(); + var receiver = ReflectionUtils.GetMessageReceiverBase(data); + + var rawChain = data.Fetch("messageChain"); + if (rawChain == null || rawChain.IsNullOrEmpty()) + { + throw new InvalidResponseException("Websocket传回错误的响应"); + } + + receiver.MessageChain = rawChain.DeserializeMessageChain(); - if (!json.ContainsKey("type")) - return WebsocketMessageTypes.Unknown; + if (receiver.MessageChain.OfType().Any(x => x.Target == Instance.QQ)) + { + _eventReceivedSubject.OnNext(new AtEvent + { + Receiver = (receiver as GroupMessageReceiver)! + }); + } - return json.Fetch("type").Contains("Message") - ? WebsocketMessageTypes.Message - : WebsocketMessageTypes.Event; + _messageReceivedSubject.OnNext(receiver); + } + else if (dataType.Contains("Event")) + { + _eventReceivedSubject.OnNext(ReflectionUtils.GetEventBase(data)); } - catch + else { - return WebsocketMessageTypes.Unknown; + _unknownMessageReceived.OnNext(data); } } diff --git a/Mirai.Net/Utils/Internal/DispatchUtils.cs b/Mirai.Net/Utils/Internal/DispatchUtils.cs new file mode 100644 index 0000000..a702572 --- /dev/null +++ b/Mirai.Net/Utils/Internal/DispatchUtils.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; + +namespace Mirai.Net.Utils.Internal; + +/// +/// 调度工具类 +/// +public static class DispatchUtils +{ + /// + /// 在等待指定的时间后执行异步操作 + /// + /// + /// + public static void ExecuteScheduledActionAsync(TimeSpan duration, Func action) + { + Task.Run(async () => + { + await Task.Delay(duration); + await action(); + }); + } + + /// + /// 在等待指定的时间后执行异步操作 + /// + /// + /// + public static void ExecuteScheduledActionAsync(int duration, Func action) + { + ExecuteScheduledActionAsync(TimeSpan.FromMilliseconds(duration), action); + } + + /// + /// 在等待指定的事件后执行同步操作 + /// + /// + /// + public static void ExecuteScheduledAction(TimeSpan duration, Action action) + { + Task.Run(async () => + { + await Task.Delay(duration); + action(); + }); + } + + /// + /// 在等待指定的时间后执行同步操作 + /// + /// + /// + public static void ExecuteScheduledAction(int duration, Action action) + { + ExecuteScheduledAction(TimeSpan.FromMilliseconds(duration), action); + } +} \ No newline at end of file diff --git a/Mirai.Net/Utils/Internal/EnumeratedTypeUtils.cs b/Mirai.Net/Utils/Internal/EnumeratedTypeUtils.cs index 3e187ab..40baf4a 100644 --- a/Mirai.Net/Utils/Internal/EnumeratedTypeUtils.cs +++ b/Mirai.Net/Utils/Internal/EnumeratedTypeUtils.cs @@ -4,7 +4,7 @@ namespace Mirai.Net.Utils.Internal; -public static class EnumeratedTypeUtils +internal static class EnumeratedTypeUtils { /// /// 获取某枚举值的Description attribute值,如果没有这个特性则直接返回该值的ToString diff --git a/Mirai.Net/Utils/Internal/ExceptionUtils.cs b/Mirai.Net/Utils/Internal/ExceptionUtils.cs index 864fb12..e17a722 100644 --- a/Mirai.Net/Utils/Internal/ExceptionUtils.cs +++ b/Mirai.Net/Utils/Internal/ExceptionUtils.cs @@ -1,12 +1,10 @@ -using System; +using Manganese.Text; using System.Collections.Generic; using System.Linq; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; namespace Mirai.Net.Utils.Internal; -public static class ExceptionUtils +internal static class ExceptionUtils { /// /// 获取状态码对应的原因 @@ -15,24 +13,25 @@ public static class ExceptionUtils /// internal static string OfErrorMessage(this string json) { - var code = json.Fetch("code").IsIntegerOrThrow(new ArgumentException("错误的状态码")); + // todo: use is integer + var code = json.Fetch("code")?.ThrowIfNotInt64("错误的状态码"); - var vocabulary = new Dictionary + return code switch { - { "0", "正确" }, - { "1", "错误的verify key" }, - { "2", "指定的Bot不存在" }, - { "3", "Session失效或不存在" }, - { "4", "Session未认证(未激活)" }, - { "5", "发送消息目标不存在(指定对象不存在)" }, - { "6", "指定文件不存在" }, - { "10", "Bot没有对应操作的限权" }, - { "20", "Bot被禁言" }, - { "30", "消息过长" }, - { "400", "错误的访问" }, - { "500", "上传文件错误" } + 0 => "成功", // Success : StateCode(0, "success") // 成功 + 1 => "错误的verify key", // AuthKeyFail : StateCode(1, "Auth Key错误") + 2 => "指定的Bot不存在", // NoBot : StateCode(2, "指定Bot不存在") + 3 => "Session失效或不存在", // IllegalSession : StateCode(3, "Session失效或不存在") + 4 => "Session未认证", // NotVerifySession : StateCode(4, "Session未认证") + 5 => "指定对象不存在", // NoElement : StateCode(5, "指定对象不存在") + 6 => "指定操作不支持", // NoOperateSupport : StateCode(6, "指定操作不支持") + 10 => "Bot没有对应操作的权限", // PermissionDenied : StateCode(10, "无操作权限") + 20 => "Bot被禁言", // BotMuted : StateCode(20, "Bot被禁言") + 30 => "消息过长", // MessageTooLarge : StateCode(30, "消息过长") + 400 => "无效参数", // InvalidParameter : StateCode(400, "无效参数") + 500 => "内部错误", // class InternalError() : StateCode(500, "") + _ => null // default case if code does not match any known codes }; - - return vocabulary.FirstOrDefault(x => x.Key == code).Value; } + } \ No newline at end of file diff --git a/Mirai.Net/Utils/Internal/MiraiHttpUtils.cs b/Mirai.Net/Utils/Internal/MiraiHttpUtils.cs index 891571f..6dbbbe5 100644 --- a/Mirai.Net/Utils/Internal/MiraiHttpUtils.cs +++ b/Mirai.Net/Utils/Internal/MiraiHttpUtils.cs @@ -1,16 +1,29 @@ -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; -using AHpx.Extensions.StringExtensions; -using Flurl; +using Flurl; using Flurl.Http; +using Manganese.Text; using Mirai.Net.Data.Exceptions; using Mirai.Net.Data.Sessions; using Mirai.Net.Sessions; +using Newtonsoft.Json; +using System; +using System.Threading.Tasks; +using NullValueHandling = Newtonsoft.Json.NullValueHandling; namespace Mirai.Net.Utils.Internal; -internal static class MiraiHttpUtils +/// +/// 用户自定义错误处理 +/// +public delegate void ErrorHandler(Exception ex); +/// +/// 由于特定插件有自定义Endpoint的需求,因此有必要将PostJsonAsync与GetAsync整体暴露为public +/// +public static class MiraiHttpUtils { + /// + /// 用户自定义错误处理器 + /// + public static ErrorHandler HttpErrorHandler { get; set; } #region Guarantee /// @@ -22,21 +35,23 @@ internal static void EnsureSuccess(this string json, string appendix = null) { var obj = json.ToJObject(); - if (obj.ContainsKey("code")) - { - var code = obj.Fetch("code"); - if (code != "0") - { - var message = $"原因: {json.OfErrorMessage()}"; + var code = obj["code"]?.ToString(); + + if (code is null or "0") return; - if (appendix.IsNotNullOrEmpty()) - message += $"\r\n备注: {appendix}"; - else - message += $"\r\n备注: {MiraiBot.Instance.ToJsonString()}"; + var message = $"原因: {json.OfErrorMessage()}\r\n备注: "; + message += string.IsNullOrEmpty(appendix) + ? MiraiBot.Instance.ToJsonString() + : appendix; - throw new InvalidResponseException(message); + var exception = new InvalidResponseException(message) + { + Data = { + ["code"] = code, + ["msg"] = obj.TryGetValue("msg", out var value) ? value.ToString() : null } - } + }; + throw exception; } #endregion @@ -48,24 +63,40 @@ internal static void EnsureSuccess(this string json, string appendix = null) /// /// 是否添加session头 /// - internal static async Task GetAsync(string url, bool withSessionKey = true) + public static async Task GetAsync(string url, bool withSessionKey = true) { - var result = withSessionKey - ? await url - .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") - .GetAsync() - : await url.GetAsync(); + try + { + var result = withSessionKey + ? await url + .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") + .GetAsync() + : await url.GetAsync(); - var re = await result.GetStringAsync(); - re.EnsureSuccess($"url={url}"); + var re = await result.GetStringAsync(); + re.EnsureSuccess($"url={url}"); - return re; + return re; + } + catch (Exception e) + { + if (HttpErrorHandler != null) + { + e.Data["method"] = "get"; + e.Data["url"] = url; + HttpErrorHandler.Invoke(e); // 如果错误处理器不为 null,则调用 + } + + else + throw; // 否则,重新抛出异常 + return null; + } } internal static async Task GetAsync(this HttpEndpoints endpoints, object extra = null, bool withSessionKey = true) { - var url = $"http://{MiraiBot.Instance.Address}/{endpoints.GetDescription()}"; + var url = $"http://{MiraiBot.Instance.Address.HttpAddress}/{endpoints.GetDescription()}"; if (extra != null) url = url.SetQueryParams(extra); @@ -79,18 +110,41 @@ internal static async Task GetAsync(this HttpEndpoints endpoints, object /// /// 加入Authentication: session xxx 请求头 /// - internal static async Task PostJsonAsync(string url, object json, bool withSessionKey = true) + public static async Task PostJsonAsync(string url, object json, bool withSessionKey = true) { - var result = withSessionKey - ? await url - .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") - .PostJsonAsync(json) - : await url.PostJsonAsync(json); - - var re = await result.GetStringAsync(); - re.EnsureSuccess($"url={url}\r\npayload={json.ToJsonString()}"); + try + { + var result = withSessionKey + ? await url + .WithHeader("Authorization", $"session {MiraiBot.Instance.HttpSessionKey}") + .PostStringAsync(json.ToJsonString(new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + })) + : await url.PostStringAsync(json.ToJsonString(new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + })); + + var re = await result.GetStringAsync(); + re.EnsureSuccess($"url={url}\r\npayload={json.ToJsonString()}"); + + return re; + } + catch (Exception e) + { + if (HttpErrorHandler != null) + { + e.Data["method"] = "post"; + e.Data["url"] = url; + e.Data["json"] = json; + HttpErrorHandler.Invoke(e); // 如果错误处理器不为 null,则调用 + } - return re; + else + throw; // 否则,重新抛出异常 + return null; + } } /// @@ -102,7 +156,7 @@ internal static async Task PostJsonAsync(string url, object json, bool w internal static async Task PostJsonAsync(this HttpEndpoints endpoint, object json, bool withSessionKey = true) { - var url = $"http://{MiraiBot.Instance.Address}/{endpoint.GetDescription()}"; + var url = $"http://{MiraiBot.Instance.Address.HttpAddress}/{endpoint.GetDescription()}"; var result = await PostJsonAsync(url, json, withSessionKey); return result; diff --git a/Mirai.Net/Utils/Internal/ReflectionUtils.cs b/Mirai.Net/Utils/Internal/ReflectionUtils.cs index 287aa28..5b08eec 100644 --- a/Mirai.Net/Utils/Internal/ReflectionUtils.cs +++ b/Mirai.Net/Utils/Internal/ReflectionUtils.cs @@ -1,23 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AHpx.Extensions.JsonExtensions; +using Manganese.Text; using Mirai.Net.Data.Events; +using Mirai.Net.Data.Events.Concretes; using Mirai.Net.Data.Messages; using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Messages.Receivers; +using Mirai.Net.Utils.Scaffolds; using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Mirai.Net.Utils.Internal; -public static class ReflectionUtils +internal static class ReflectionUtils { /// /// 获取某个命名空间下所有类的默认实例 /// /// /// - public static IEnumerable GetDefaultInstances(string @namespace) where T : class + internal static IEnumerable GetDefaultInstances(string @namespace) where T : class { return Assembly .GetExecutingAssembly() @@ -25,28 +28,106 @@ public static IEnumerable GetDefaultInstances(string @namespace) where T : .Where(type => type.FullName != null) .Where(type => type.FullName.Contains(@namespace)) .Where(type => !type.IsAbstract) - .Select(type => Activator.CreateInstance(type) as T); + .Select(type => + { + if (Activator.CreateInstance(type) is T instance) + { + return instance; + } + + return null; + }) + .Where(i => i != null); + } + + #region Type Cache + + /// + /// 将指定的类型集合转换为字典,
+ /// 不需要在每次获取类型时调用类型的GetType方法, 提升每次获取实例类型时的性能。 + ///
+ /// 实例集合 + /// 作为字典键的枚举类型 + /// 作为字典值:枚举值对应类型的Type + /// + private static Dictionary InitTypeHashSet(IEnumerable enumerable) + where T : Enum + where R : class + { + var dict = new Dictionary(); + + foreach (var item in enumerable) + { + T key; + Type type; + switch (item) + { + case MessageReceiverBase receiverBase: + key = (T) Enum.Parse(typeof(T), receiverBase.Type.ToString()); + type = receiverBase.GetType(); + break; + case MessageBase messageBase: + key = (T) Enum.Parse(typeof(T), messageBase.Type.ToString()); + type = messageBase.GetType(); + break; + case EventBase eventBase: + key = (T) Enum.Parse(typeof(T), eventBase.Type.ToString()); + type = eventBase.GetType(); + break; + default: + throw new ArgumentException("该方法不支持此类型使用!", typeof(R).FullName); + } + + if (!dict.ContainsKey(key)) + { + dict.Add(key, type); + } + } + + return dict; } - + /// /// 默认消息接收器实例 /// private static readonly IEnumerable MessageReceiverBases = GetDefaultInstances( "Mirai.Net.Data.Messages.Receivers"); - + + /// + /// 默认消息接收器类型字典(K: 接收器的类别, V: 实例的Type) + /// + private static readonly Dictionary MessageReceiverTypeDict = + InitTypeHashSet(MessageReceiverBases); + + /// /// 默认消息实例 /// private static readonly IEnumerable MessageBases = GetDefaultInstances("Mirai.Net.Data.Messages.Concretes"); + /// + /// 默认消息实例类型字典(K: 消息的类别, V: 实例的Type) + /// + private static readonly Dictionary MessageBaseTypeDict = + InitTypeHashSet(MessageBases); + + /// /// 默认事件实例 /// private static readonly IEnumerable EventBases = GetDefaultInstances("Mirai.Net.Data.Events.Concretes"); - + + /// + /// 默认事件实例类型字典(K: 事件类别, V: 实例Type) + /// + private static readonly Dictionary EventTypeDict = InitTypeHashSet(EventBases); + + #endregion + + /// /// 根据json动态解析对应的消息子类 /// @@ -54,42 +135,49 @@ public static IEnumerable GetDefaultInstances(string @namespace) where T : /// internal static MessageBase GetMessageBase(string data) { - var root = JsonConvert.DeserializeObject(data); - - if (root!.Type == Messages.Quote) + try { - var quote = JsonConvert.DeserializeObject(data); + var raw = JsonConvert.DeserializeObject(data); - quote!.Origin = data.FetchJToken("origin") - .Select(x => GetMessageBase(x.ToString())) - .ToArray(); - - return quote; - } + if (raw!.Type == Messages.Quote) + { + var quote = JsonConvert.DeserializeObject(data); - if (root!.Type == Messages.Forward) - { - var forward = JsonConvert.DeserializeObject(data); + quote!.Origin = data.FetchJToken("origin")! + .Select(x => GetMessageBase(x.ToString())) + .ToArray(); - forward!.NodeList = data.FetchJToken("nodeList") - .Select(x => - { - var node = x.ToObject(); - node!.MessageChain = x.FetchJToken("messageChain").Select(z => GetMessageBase(z.ToString())) - .ToArray(); + return quote; + } - return node; - }) - .ToArray(); + if (raw!.Type == Messages.Forward) + { + var forward = JsonConvert.DeserializeObject(data); - return forward; - } + forward!.NodeList = data.FetchJToken("nodeList")! + .Select(x => + { + var node = x.ToObject(); + node!.MessageChain = x.Fetch("messageChain")!.DeserializeMessageChain(); + + return node; + }) + .ToArray(); - return JsonConvert.DeserializeObject(data, - MessageBases.First(message => message.Type == root!.Type) - .GetType()) as MessageBase; + return forward; + } + + // 因为有异常捕获来处理转换失败的情况, 如果获取不到类型那一定得抛出异常, 所以此处直接用索引获取实例类型. + return JsonConvert.DeserializeObject(data, MessageBaseTypeDict[raw!.Type]) as MessageBase; + } + catch + { + var re = JsonConvert.DeserializeObject(data); + re!.RawJson = data; + return re; + } } - + /// /// 根据json动态解析正确的消息接收器子类 /// @@ -97,11 +185,21 @@ internal static MessageBase GetMessageBase(string data) /// internal static MessageReceiverBase GetMessageReceiverBase(string data) { - var root = JsonConvert.DeserializeObject(data); + try + { + var raw = JsonConvert.DeserializeObject(data); - return JsonConvert.DeserializeObject(data, - MessageReceiverBases.First(receiver => receiver.Type == root!.Type) - .GetType()) as MessageReceiverBase; + // 因为有异常捕获来处理转换失败的情况, 如果获取不到类型那一定得抛出异常, 所以此处直接用索引获取. + var type = MessageReceiverTypeDict[raw!.Type]; + + return JsonConvert.DeserializeObject(data, type) as MessageReceiverBase; + } + catch + { + var re = JsonConvert.DeserializeObject(data); + re!.RawJson = data; + return re; + } } /// @@ -111,10 +209,30 @@ internal static MessageReceiverBase GetMessageReceiverBase(string data) /// internal static EventBase GetEventBase(string data) { - var root = JsonConvert.DeserializeObject(data); + try + { + var raw = JsonConvert.DeserializeObject(data); - return JsonConvert.DeserializeObject(data, - EventBases.First(message => message.Type == root!.Type) - .GetType()) as EventBase; + // 因为有异常捕获来处理转换失败的情况, 如果获取不到类型那一定得抛出异常, 所以此处直接用索引获取实例的类型. + return JsonConvert.DeserializeObject(data, EventTypeDict[raw!.Type]) as EventBase; + } + catch + { + var re = JsonConvert.DeserializeObject(data); + re!.RawJson = data; + return re; + } + } + + /// + /// 反序列化消息链 + /// + /// + /// + internal static MessageChain DeserializeMessageChain(this string data) + { + return data.ToJArray() + .Select(token => ReflectionUtils.GetMessageBase(token.ToString())) + .ToMessageChain(); } } \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/CommandScaffold.cs b/Mirai.Net/Utils/Scaffolds/CommandScaffold.cs deleted file mode 100644 index 9b12787..0000000 --- a/Mirai.Net/Utils/Scaffolds/CommandScaffold.cs +++ /dev/null @@ -1,424 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AHpx.Extensions.StringExtensions; -using Mirai.Net.Data.Commands; -using Mirai.Net.Data.Exceptions; -using Mirai.Net.Data.Messages; -using Mirai.Net.Data.Messages.Concretes; -using Mirai.Net.Data.Modules; -using Mirai.Net.Modules; - -namespace Mirai.Net.Utils.Scaffolds; - -public static class CommandScaffold -{ - /// - /// 解析命令到实体类 - /// - /// - /// - /// - /// - /// - public static T ParseCommand(this string origin) where T : class, new() - { - origin = origin.Trim(); - - var re = new T(); - var type = re.GetType(); - var commandInfo = type.GetCustomAttribute(); - - var properties = type.GetProperties(); - var args = properties - .Select(x => (x, x.GetCustomAttribute())) - .Where(x => x.Item2 != null) - .ToList(); - - #region Pre check - - if (!HasValidIdentifier(commandInfo, origin)) - throw new InvalidCommandException($"此字符串无法解析为命令:\r\n{origin}"); - - // if (args.Count(x => x.Item2.IsDefault) > 1) - // throw new InvalidCommandEntityException($"只能有一个默认参数"); - - #endregion - - foreach (var (propertyInfo, argumentInfo) in args) - { - var argumentSyntax = $"{commandInfo.Separator}{argumentInfo.Name}"; - var argumentMeta = $"{argumentInfo.Name}/{propertyInfo.Name}"; - - var propertyType = propertyInfo.PropertyType; - - if (origin.Split(' ').Count(x => x == argumentSyntax) > 1) - throw new InvalidCommandException($"命令参数{argumentInfo.Name}重复"); - - // if (argumentInfo.IsDefault && !origin.Contains(argumentSyntax)) - // { - // var commandSyntax = $"{commandInfo.Identifier}{commandInfo.Name}"; - // - // var left = commandSyntax.Length; - // var right = origin.Contains(commandInfo.Separator) - // ? origin.IndexOf(commandInfo.Separator, left, StringComparison.Ordinal) - left - // : origin.Length - left; - // - // propertyInfo.SetValue(re, origin.Substring(left, right)); - // - // continue; - // } - - switch (argumentInfo.IsRequired) - { - case true when argumentInfo.Default is not null: - throw new InvalidCommandEntityException( - $"命令实体不能同时设置为必须参数和默认值: {argumentMeta}"); - case true when !origin.Contains(argumentSyntax): - throw new InvalidCommandException($"缺失必要参数: {argumentMeta}"); - case true when propertyInfo.PropertyType == typeof(bool): - // throw new InvalidCommandException($"选项参数{argumentMeta}不可以是必须参数"); - // case true when argumentInfo.IsDefault: - throw new InvalidCommandException($"必须参数{argumentMeta}不可以是默认参数"); - case false when argumentInfo.Default is not null: - propertyInfo.SetValue(re, argumentInfo.Default); - break; - } - - switch (origin.Contains(argumentSyntax)) - { - case true when propertyType == typeof(bool): - propertyInfo.SetValue(re, true); - break; - case true when propertyType == typeof(IEnumerable) || - propertyType == typeof(List) || - propertyType == typeof(string[]): - - var arg = GetMiddleContent(commandInfo, origin, argumentSyntax) - .Split(' ') - .Where(x => x.IsNotNullOrEmpty()).ToList(); - - arg.RemoveAt(0); - - if (propertyType == typeof(string[])) - { - propertyInfo.SetValue(re, arg.ToArray()); - break; - } - propertyInfo.SetValue(re, arg); - break; - case true when propertyType == typeof(string): - propertyInfo.SetValue(re, GetMiddleContent(commandInfo, origin, argumentSyntax)); - break; - case true when propertyType == typeof(int) || - propertyType == typeof(long): - var intArg = GetMiddleContent(commandInfo, origin, argumentSyntax) - .IsIntegerOrThrow(new InvalidCommandException($"参数{argumentMeta}必须为整数")); - - Console.WriteLine(propertyType == typeof(int)); - if (int.TryParse(intArg, out var outInt)) - { - propertyInfo.SetValue(re, outInt); - break; - } - - if (long.TryParse(intArg, out var outLong)) - propertyInfo.SetValue(re, outLong); - break; - case true when propertyType == typeof(double): - var doubleArg = GetMiddleContent(commandInfo, origin, argumentSyntax); - - if (!double.TryParse(doubleArg, out var d)) - throw new InvalidCommandException($"参数{argumentMeta}必须为双精度浮点数"); - - propertyInfo.SetValue(re, d); - break; - } - } - - return re; - } - - public static bool CanExecute(this string origin) where T : class, new() - { - var entity = new T(); - var type = entity.GetType(); - var properties = type.GetProperties(); - var commandInfo = type.GetCustomAttribute(); - var args = properties - .Select(x => (x, x.GetCustomAttribute())) - .Where(x => x.Item2 != null) - .ToList(); - - if (!HasValidIdentifier(commandInfo, origin)) - return false; - - // if (args.Count(x => x.Item2.IsDefault) > 1) - // return false; - - foreach (var (propertyInfo, argumentInfo) in args) - { - var argumentSyntax = $"{commandInfo.Separator}{argumentInfo.Name}"; - - var propertyType = propertyInfo.PropertyType; - - if (origin.Split(' ').Count(x => x == argumentSyntax) > 1) - return false; - - switch (argumentInfo.IsRequired) - { - case true when argumentInfo.Default is not null: - return false; - case true when !origin.Contains(argumentSyntax): - return false; - case true when propertyInfo.PropertyType == typeof(bool): - return false; - // case true when argumentInfo.IsDefault: - // return false; - } - - switch (origin.Contains(argumentSyntax)) - { - case true when propertyType == typeof(int) || - propertyType == typeof(long): - var intArg = GetMiddleContent(commandInfo, origin, argumentSyntax); - - if (intArg.IsNotInteger()) - return false; - - return int.TryParse(intArg, out _) || long.TryParse(intArg, out _); - case true when propertyType == typeof(double): - var doubleArg = GetMiddleContent(commandInfo, origin, argumentSyntax); - - return double.TryParse(doubleArg, out _); - } - } - - return true; - } - - /// - /// 判断某命令的起始符是否合法 - /// - /// - /// - /// - private static bool HasValidIdentifier(CommandEntityAttribute commandInfo, string origin) - { - var syntax = commandInfo.Alias - .Aggregate($"{commandInfo.Identifier}{commandInfo.Name}", (a, b) => $"{a};{commandInfo.Identifier}{b}") - .Split(';'); - - return syntax.Any(s => - { - var index = origin.IndexOf(commandInfo.Separator, 1, StringComparison.Ordinal); - - if (index == -1) - return origin.Trim() == s; - - return origin.Substring(0, index).Trim() == s; - }); - } - - /// - /// 获取两个参数之间的内容 - /// - /// - /// - /// - /// - private static string GetMiddleContent(CommandEntityAttribute entity, string origin, string syntax) - { - var left = origin.IndexOf(syntax, StringComparison.Ordinal); - var right = origin.IndexOf(entity.Separator, left + 1, StringComparison.Ordinal); - var rawArg = right != -1 ? origin.Substring(left, right - left) : origin.Substring(left); - - rawArg = rawArg.Empty(syntax).Trim(); - return rawArg; - } - - /// - /// 消息链是否可以执行命令 - /// - /// - /// - /// - [Obsolete] - public static bool CanExecute(this IEnumerable originalChain, CommandTriggerAttribute trigger) - { - return CanExecute(originalChain, trigger, out _); - } - - /// - /// 消息链是否可以执行命令 - /// - /// - /// - /// 哪个消息是可以执行的 - /// - [Obsolete] - public static bool CanExecute(this IEnumerable originalChain, CommandTriggerAttribute trigger, - out MessageBase executedBy) - { - var chain = originalChain.ToList(); - if (chain.Any(x => x.Type == Messages.Plain)) - foreach (var message in chain.OfType()) - if (trigger.IsCommand(message.Text)) - { - executedBy = message; - return true; - } - - trigger.EqualName = false; - foreach (var s in chain.Where(s => trigger.IsCommand(s.ToJsonString()))) - { - executedBy = s; - return true; - } - - executedBy = null; - return false; - } - - /// - /// 判断一个字符串是否为命令 - /// - /// - /// - /// - [Obsolete] - public static bool IsCommand(this CommandTriggerAttribute trigger, string s) - { - var expectation = $"{trigger.Prefix}{trigger.Name}"; - - return trigger.EqualName ? s.StartsWith(expectation) : s.Contains(expectation); - } - - /// - /// 解析命令,Dictionary的key为参数名,value为参数值 - /// - /// - /// - /// - [Obsolete] - public static IDictionary ParseCommand(this CommandTriggerAttribute trigger, string s) - { - var split = s - .Empty($"{trigger.Prefix}{trigger.Name}") - .Trim().Split(' ') - .Where(x => x.IsNotNullOrEmpty()) - .ToList(); - - var re = new Dictionary(); - var indexes = new List(); - - split - .Where(x => x.StartsWith(trigger.ArgsSeparator)) - .ToList() - .ForEach(x => indexes.Add(split.IndexOf(x))); - - foreach (var index in indexes) - { - int next; - if (indexes.IndexOf(index) + 1 >= indexes.Count) - next = split.Count - 1; - else - next = indexes[indexes.IndexOf(index) + 1]; - - var range = next != split.Count - 1 - ? split.GetRange(index, next - index) - : split.GetRange(index, split.Count - index); - - re.Add(range.First().TrimStart(trigger.ArgsSeparator.ToCharArray()), - range.GetRange(1, range.Count - 1).ToArray()); - } - - return re; - } - - /// - /// 解析命令 - /// - /// - /// - /// - [Obsolete] - public static IDictionary ParseCommand(this ICommandModule commandModule, string command) - { - return commandModule.GetCommandTrigger().ParseCommand(command); - } - - /// - /// 检查某命令是否有参数 - /// - /// - /// - /// - [Obsolete] - public static bool HasParameters(this CommandTriggerAttribute trigger, string s) - { - return trigger.ParseCommand(s).Count != 0; - } - - /// - /// 获取本模块的CommandTrigger - /// - /// - /// - [Obsolete] - public static CommandTriggerAttribute GetCommandTrigger(this ICommandModule commandModule) - { - return commandModule.GetType().GetMethod(nameof(commandModule.Execute))! - .GetCustomAttribute(); - } - - /// - /// 执行集合内的全部命令模块,除非它没开启 - /// - /// - /// - [Obsolete] - public static void ExecuteCommandModules(this MessageReceiverBase receiver, IEnumerable modules) - { - foreach (var module in modules.Where(x => x.IsEnable is not false)) - if (receiver.MessageChain.CanExecute(module.GetCommandTrigger(), out var message)) - module.Execute(receiver, message); - } - - /// - /// 加载指定命名空间的命令模块 - /// - [Obsolete] - public static IEnumerable LoadCommandModules(string space) - { - var assembly = Assembly.GetEntryAssembly(); - var types =assembly.GetTypes() - .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface) - .Where(x => x.GetInterfaces().Any(x => x == typeof(ICommandModule))) - .Where(x => x!.FullName!.Contains(space)) - .ToList(); - - foreach (var type in types) yield return Activator.CreateInstance(type) as ICommandModule; - } - - /// - /// 加载与某模块同命名空间的所有模块 - /// - /// 某模块 - /// - [Obsolete] - public static IEnumerable LoadCommandModules() where T : ICommandModule - { - var basic = typeof(T); - - var types = Assembly.GetAssembly(basic).GetTypes() - .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface) - .Where(x => x.GetInterfaces().Any(x => x == typeof(ICommandModule))) - .Where(x => x!.FullName!.Contains(basic.Namespace!)) - .ToList(); - - foreach (var type in types) yield return Activator.CreateInstance(type) as ICommandModule; - } -} \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/MessageChain.cs b/Mirai.Net/Utils/Scaffolds/MessageChain.cs new file mode 100644 index 0000000..4928a78 --- /dev/null +++ b/Mirai.Net/Utils/Scaffolds/MessageChain.cs @@ -0,0 +1,249 @@ +using Manganese.Array; +using Manganese.Text; +using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Messages.Receivers; +using Mirai.Net.Utils.Scaffolds; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +// ReSharper disable once CheckNamespace +namespace Mirai.Net.Data.Messages; + +/// +/// 消息链 +/// +public partial class MessageChain : List +{ + /// + /// AddRange + /// + /// + public MessageChain(IEnumerable collection) : base(collection) + { + } + + /// + /// + /// + public MessageChain() : base() + { + } + + /// + /// 运行时安全的Mirai码 + /// + public string MiraiCode + { + get + { + System.Text.StringBuilder builder = new(); + + this.ForEach(x => + { + try + { + builder.Append(x.SerializeToMiraiCode()); + } + catch + {} + }); + + return builder.ToString(); + } + } + + /// + /// 获取消息链中的纯文本消息 + /// + /// 如果没有文本消息返回空字符串 + public string GetPlainMessage() + { + var plain = this.OfType().ToList(); + if (!plain.Any()) + return string.Empty; + + return plain.Select(x => x.Text).JoinToString(""); + } + +#nullable enable + + /// + /// 获取该消息的消息来源 如果有多个来源返回最后一个 如果没有来源则返回null + /// + /// + public SourceMessage? GetSourceMessage() => this.OfType().LastOrDefault(); + + /// + /// 获取该消息的消息引用 如果有多个引用返回最后一个 如果没有引用则返回null + /// + /// + public QuoteMessage? GetQuoteMessage() => this.OfType().LastOrDefault(); + +#nullable disable + + /// + /// 获取消息链中的文本消息并且保持原有连贯性 + /// + /// 如果没有文本消息返回空列表 + public List GetSeparatedPlainMessage() + { + var plain = this.OfType().ToList(); + if (!plain.Any()) + return new List(); + + return plain.Select(x => x.Text).ToList(); + } + + /// + /// 将本消息链发送到指定接收器 + /// + /// + /// + public async Task SendToAsync(GroupMessageReceiver receiver) + { + return await receiver.SendMessageAsync(this); + } + + /// + /// 将本消息链发送到指定接收器 + /// + /// + /// + public async Task SendToAsync(FriendMessageReceiver receiver) + { + return await receiver.SendMessageAsync(this); + } + + /// + /// 将本消息链发送到指定接收器 + /// + /// + /// + public async Task SendToAsync(TempMessageReceiver receiver) + { + return await receiver.SendMessageAsync(this); + } + + /// + /// 将该消息链序列化为mirai码 不会捕获异常 + /// + /// + public string SerializeToMiraiCode() + { + System.Text.StringBuilder builder = new(); + + this.ForEach(x => builder.Append(x.SerializeToMiraiCode())); + + return builder.ToString(); + } + + /// + /// 自动转换单个消息为消息链 + /// + /// + /// + public static implicit operator MessageChain(MessageBase messageBase) + { + return messageBase.ToMessageChain(); + } + + /// + /// 转换string为单文本消息链 + /// + /// + /// + public static implicit operator MessageChain(string message) + { + return new PlainMessage(message).ToMessageChain(); + } + + /// + /// 拼接两个消息链 + /// + /// + /// + /// + public static MessageChain operator +(MessageChain chain1, MessageChain chain2) + { + var chain = new MessageChain(); + chain.AddRange(chain1); + chain.AddRange(chain2); + return chain; + } + + /// + /// 拼接消息链和消息 + /// + /// + /// + /// + public static MessageChain operator +(MessageChain chain, MessageBase msg) + { + chain.Add(msg); + return chain; + } + + /// + /// 返回两个消息链是否相等 + /// + /// + /// + /// + public static bool operator ==(MessageChain left, MessageChain right) + { + left = left.Where(x => x is not SourceMessage).ToMessageChain(); + + right = right.Where(x => x is not SourceMessage).ToMessageChain(); + + if (left.Count != right.Count) return false; + + for (int i = 0; i < left.Count; i++) + { + if (left[i].Type != right[i].Type) return false; + + // 这段代码用了一种极其诡异的方法判断是否相等 + // 首先检查能否转换为图片消息或者语音消息然后挨个判断四个属性 都不相等就返回false 有一个相等就返回true + // 然后看不能转换的时候利用record直接判左右相等 + // 最后把前面所有的一切套进if里顺便加反转 如果为true什么都不做 如果为false直接返回false + if (!((left[i], right[i]) switch + { + (ImageMessage leftmsg, ImageMessage rightmsg) => !(leftmsg.ImageId != rightmsg.ImageId && leftmsg.Url != rightmsg.Url && leftmsg.Path != rightmsg.Path && leftmsg.Base64 != rightmsg.Base64), + + (VoiceMessage leftmsg, VoiceMessage rightmsg) => !(leftmsg.VoiceId != rightmsg.VoiceId && leftmsg.Url != rightmsg.Url && leftmsg.Path != rightmsg.Path && leftmsg.Base64 != rightmsg.Base64), + + (var leftmsg, var rightmsg) => leftmsg == rightmsg + })) return false; + } + + // 如果还有代码没有直接返回false能走到这里那么估计完全相等了直接返回true + return true; + } + + /// + /// 返回两个消息链是否不等 + /// + /// + /// + /// + public static bool operator !=(MessageChain left, MessageChain right) => !(left == right); + + /// + /// + /// + /// + /// + public override bool Equals(object msg) + { + return (object)this == msg; + } + + /// + /// + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } +} \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/MessageChainBuilder.cs b/Mirai.Net/Utils/Scaffolds/MessageChainBuilder.cs new file mode 100644 index 0000000..fae6242 --- /dev/null +++ b/Mirai.Net/Utils/Scaffolds/MessageChainBuilder.cs @@ -0,0 +1,325 @@ +using Mirai.Net.Data.Messages; +using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Shared; + +namespace Mirai.Net.Utils.Scaffolds; + +/// +/// 消息链建造者 +/// +public class MessageChainBuilder +{ + private readonly MessageChain _chain = new(); + + /// + /// 清除已追加的消息 + /// + /// + public MessageChainBuilder Clear() + { + _chain.Clear(); + return this; + } + + /// + /// 追加自定义的消息 + /// + /// + /// + public MessageChainBuilder Append(MessageBase messageBase) + { + _chain.Add(messageBase); + return this; + } + + /// + /// 追加一条文本消息 + /// + /// + /// + public MessageChainBuilder Plain(string text) + { + _chain.Add(new PlainMessage(text)); + return this; + } + + /// + /// 追加一条at消息 + /// + /// + /// + public MessageChainBuilder At(string qq) + { + _chain.Add(new AtMessage(qq)); + return this; + } + + /// + /// 追加一条at消息 + /// + /// + /// + public MessageChainBuilder At(Member member) + { + return At(member.Id); + } + + /// + /// 追加一条app消息 + /// + /// + /// + public MessageChainBuilder App(string content) + { + _chain.Add(new AppMessage + { + Content = content + }); + return this; + } + + /// + /// 追加一套at全体成员消息 + /// + /// + public MessageChainBuilder AtAll() + { + _chain.Add(new AtAllMessage()); + + return this; + } + + /// + /// 追加一条骰子消息 + /// + /// + /// + public MessageChainBuilder Dice(string value) + { + _chain.Add(new DiceMessage + { + Value = value + }); + + return this; + } + + /// + /// 追加图片消息 + /// + /// + /// + public MessageChainBuilder ImageFromUrl(string url) + { + _chain.Add(new ImageMessage + { + Url = url + }); + + return this; + } + + /// + /// 追加图片消息 + /// + /// + /// + public MessageChainBuilder ImageFromBase64(string base64) + { + _chain.Add(new ImageMessage + { + Base64 = base64 + }); + + return this; + } + + /// + /// 追加图片消息 + /// + /// + /// + public MessageChainBuilder ImageFromPath(string file) + { + _chain.Add(new ImageMessage + { + Path = file + }); + + return this; + } + + /// + /// 追加图片消息 + /// + /// + /// + public MessageChainBuilder ImageFromId(string id) + { + _chain.Add(new ImageMessage + { + ImageId = id + }); + + return this; + } + + /// + /// 追加图片消息 + /// + /// + /// + public MessageChainBuilder FlashImageFromUrl(string url) + { + _chain.Add(new FlashImageMessage + { + Url = url + }); + + return this; + } + + /// + /// 追加闪照消息 + /// + /// + /// + public MessageChainBuilder FlashImageFromBase64(string base64) + { + _chain.Add(new FlashImageMessage + { + Base64 = base64 + }); + + return this; + } + + /// + /// 追加闪照消息 + /// + /// + /// + public MessageChainBuilder FlashImageFromPath(string file) + { + _chain.Add(new FlashImageMessage + { + Path = file + }); + + return this; + } + + /// + /// 追加闪照消息 + /// + /// + /// + public MessageChainBuilder FlashImageFromId(string id) + { + _chain.Add(new FlashImageMessage + { + ImageId = id + }); + + return this; + } + + /// + /// 追加json消息 + /// + /// + /// + public MessageChainBuilder Json(string json) + { + _chain.Add(new JsonMessage + { + Json = json + }); + + return this; + } + + /// + /// 追加xml消息 + /// + /// + /// + public MessageChainBuilder Xml(string xml) + { + _chain.Add(new XmlMessage + { + Xml = xml + }); + + return this; + } + + /// + /// 追加一条语音消息 + /// + /// + /// + public MessageChainBuilder VoiceFromPath(string path) + { + _chain.Add(new VoiceMessage + { + Path = path + }); + + return this; + } + + /// + /// 追加一条语音消息 + /// + /// + /// + public MessageChainBuilder VoiceFromBase64(string base64) + { + _chain.Add(new VoiceMessage + { + Base64 = base64 + }); + + return this; + } + + /// + /// 追加一条语音消息 + /// + /// + /// + public MessageChainBuilder VoiceFromId(string id) + { + _chain.Add(new VoiceMessage + { + VoiceId = id + }); + + return this; + } + + /// + /// 追加一条语音消息 + /// + /// + /// + public MessageChainBuilder VoiceFromUrl(string url) + { + _chain.Add(new VoiceMessage + { + Url = url + }); + + return this; + } + + /// + /// 建造消息链 + /// + /// + public MessageChain Build() + { + return _chain; + } +} \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/MessageScaffold.cs b/Mirai.Net/Utils/Scaffolds/MessageScaffold.cs index 9b1a96b..cd5007a 100644 --- a/Mirai.Net/Utils/Scaffolds/MessageScaffold.cs +++ b/Mirai.Net/Utils/Scaffolds/MessageScaffold.cs @@ -1,8 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using AHpx.Extensions.StringExtensions; -using Mirai.Net.Data.Messages; -using Mirai.Net.Data.Messages.Concretes; +using Mirai.Net.Data.Messages; +using Mirai.Net.Sessions.Http.Managers; +using Mirai.Net.Utils.Internal; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Mirai.Net.Utils.Scaffolds; @@ -11,133 +12,90 @@ namespace Mirai.Net.Utils.Scaffolds; /// public static class MessageScaffold { - public static MessageBase[] Append(this string origin, params MessageBase[] append) + /// + /// 把枚举接口转换为高层MessageChain对象 + /// + /// + /// + /// + public static MessageChain ToMessageChain(this IEnumerable source) where TSource : MessageBase { - var re = new List { new PlainMessage(origin) }; - re.AddRange(append); - - return re.ToArray(); - } - - public static MessageBase[] Append(this string origin, IEnumerable append) - { - var re = new List { new PlainMessage(origin) }; - re.AddRange(append); - - return re.ToArray(); - } - - public static MessageBase[] Append(this string origin, string append) - { - var re = new List { new PlainMessage(origin), new PlainMessage(append) }; - - return re.ToArray(); - } - - public static MessageBase[] Append(this MessageBase messageBase, params MessageBase[] append) - { - var re = new List { messageBase }; - re.AddRange(append); - - return re.ToArray(); - } - - public static MessageBase[] Append(this MessageBase messageBase, IEnumerable append) - { - var re = new List { messageBase }; - re.AddRange(append); - - return re.ToArray(); + return new MessageChain(source); } - public static MessageBase[] Append(this MessageBase messageBase, string append) + /// + /// 把单个消息对象转换成MessageChain对象 + /// + /// + /// + /// + public static MessageChain ToMessageChain(this TSource origin) where TSource : MessageBase { - var re = new List { messageBase, new PlainMessage(append) }; - - return re.ToArray(); + return new MessageChain { origin }; } - public static MessageBase[] Append(this IEnumerable bases, params MessageBase[] append) + /// + /// 转义成兼容mirai码的文本 + /// + /// + public static string EscapeMiraiCode(this string origin) { - var re = bases.ToList(); - re.AddRange(append); - - return re.ToArray(); + var result = origin + .Replace("[", @"\[") + .Replace("]", @"\]") + .Replace(":", @"\:") + .Replace(",", @"\,") + .Replace("\\", @"\\"); + + return result; } - public static MessageBase[] Append(this IEnumerable bases, string append) - { - var re = bases.ToList(); - re.Add(new PlainMessage(append)); - - return re.ToArray(); - } - - public static MessageBase[] Append(this IEnumerable bases, IEnumerable append) + /// + /// 在指定的时间之后撤回消息 + /// + /// + /// 好友id或群id + /// + public static async Task RecallAfter(this Task messageIdTask, string target, TimeSpan duration) { - var re = bases.ToList(); - re.AddRange(append); - - return re.ToArray(); + var messageId = await messageIdTask; + DispatchUtils.ExecuteScheduledActionAsync(duration, async () => + { + await MessageManager.RecallAsync(messageId, target); + }); } - public static bool Contains(this IEnumerable bases, string message) + /// + /// 在指定的时间之后撤回消息 + /// + /// + /// 好友id或群id + /// + public static async Task RecallAfter(this Task messageIdTask, string target, int milliseconds) { - return bases.Select(x => x.ToJsonString()).Any(x => x.Contains(message)); + await messageIdTask.RecallAfter(target, TimeSpan.FromMilliseconds(milliseconds)); } - - public static bool Contains(this IEnumerable bases, string message, out MessageBase messageBase) - { - foreach (var @base in bases) - { - var json = @base.ToJsonString(); - if (json.Contains(message)) - { - messageBase = @base; - return true; - } - } - messageBase = null; - return false; - } - - public static bool Contains(this IEnumerable bases, string message, out IEnumerable origin) + /// + /// 在指定时间之后发送消息,这是个同步方法 + /// + /// + /// + public static void SendAfter(this Task messageTask, TimeSpan duration) { - var messageBases = bases.ToList(); - foreach (var @base in messageBases) + DispatchUtils.ExecuteScheduledActionAsync(duration, async () => { - var json = @base.ToJsonString(); - if (json.Contains(message)) - { - origin = messageBases; - return true; - } - } - - origin = null; - return false; + await messageTask; + }); } - public static string GetPlainMessage(this IEnumerable messageChain) - { - var messageBases = messageChain.ToList(); - if (messageBases.OfType().Any()) - { - return messageBases - .OfType() - .Select(x => x.Text) - .Aggregate((s, s1) => s + s1) - .Trim(); - } - - return null; - } - - public static IEnumerable GetSeparatedPlainMessage(this IEnumerable messageChain) + /// + /// 在指定时间之后发送消息,这是个同步方法 + /// + /// + /// + public static void SendAfter(this Task messageTask, int milliseconds) { - return messageChain - .OfType() - .Select(x => x.Text); + messageTask.SendAfter(TimeSpan.FromMilliseconds(milliseconds)); } } \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/MiraiScaffold.cs b/Mirai.Net/Utils/Scaffolds/MiraiScaffold.cs index 996dc6e..a51c82d 100644 --- a/Mirai.Net/Utils/Scaffolds/MiraiScaffold.cs +++ b/Mirai.Net/Utils/Scaffolds/MiraiScaffold.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AHpx.Extensions.JsonExtensions; +using Manganese.Text; using Mirai.Net.Data.Events.Concretes.Request; using Mirai.Net.Data.Messages; using Mirai.Net.Data.Messages.Concretes; @@ -12,9 +9,17 @@ using Mirai.Net.Sessions; using Mirai.Net.Sessions.Http.Managers; using Mirai.Net.Utils.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; namespace Mirai.Net.Utils.Scaffolds; +/// +/// mirai相关拓展方法 +/// public static class MiraiScaffold { #region MiraiBot extensions @@ -72,54 +77,54 @@ public static bool IsFriend(this MiraiBot bot, Friend friend) return bot.Friends.Value.Any(x => x.Id == friend.Id); } - #endregion - - #region Legacy command module extensions - /// - /// 执行命令模块 + /// An encapsulation of 'OfType.Subscribe' for GroupMessageReceiver /// /// - /// + /// /// - [Obsolete] - public static IObservable WithCommandModules(this IObservable observable, - params ICommandModule[] modules) + public static IDisposable SubscribeGroupMessage(this IObservable observable, Action action) { - observable.Subscribe(x => { x.ExecuteCommandModules(modules); }); - - return observable; + return observable.OfType().Subscribe(action); } /// - /// 执行指定类型同命名空间下所有命令模块(除非它没开启) + /// An encapsulation of 'OfType.Subscribe' for GroupMessageReceiver /// /// + /// /// - [Obsolete] - public static IObservable WithCommandModules( - this IObservable observable) where T : ICommandModule + public static IDisposable SubscribeGroupMessageAsync(this IObservable observable, Func action) { - var particular = CommandScaffold.LoadCommandModules(); - observable.Subscribe(x => { x.ExecuteCommandModules(particular); }); - - return observable; + return observable.OfType().Subscribe(r => + { + action(r); + }); } /// - /// 执行指定命名空间下所有命令模块(除非它没开启) + /// An encapsulation of 'OfType.Subscribe' for FriendMessageReceiver /// /// - /// + /// /// - [Obsolete] - public static IObservable WithCommandModules(this IObservable observable, - string @namespace) + public static IDisposable SubscribeFriendMessage(this IObservable observable, Action action) { - var particular = CommandScaffold.LoadCommandModules(@namespace); - observable.Subscribe(x => { x.ExecuteCommandModules(particular); }); + return observable.OfType().Subscribe(action); + } - return observable; + /// + /// An encapsulation of 'OfType.Subscribe' for GroupMessageReceiver + /// + /// + /// + /// + public static IDisposable SubscribeFriendMessageAsync(this IObservable observable, Func action) + { + return observable.OfType().Subscribe(r => + { + action(r); + }); } #endregion @@ -137,6 +142,18 @@ public static T Concretize(this MessageReceiverBase @base) where T : MessageR return (T)@base; } + /// + /// 使用模块 + /// + /// + /// + /// + public static IObservable WithModules(this IObservable source, List modules) + { + source.Subscribe(modules.Raise); + return source; + } + #endregion #region Message extension @@ -148,7 +165,7 @@ public static T Concretize(this MessageReceiverBase @base) where T : MessageR /// /// public static async Task SendMessageAsync(this GroupMessageReceiver receiver, - params MessageBase[] chain) + MessageChain chain) { return await MessageManager .SendGroupMessageAsync(receiver.Sender.Group.Id, chain); @@ -161,7 +178,7 @@ public static async Task SendMessageAsync(this GroupMessageReceiver rece /// /// public static async Task SendMessageAsync(this FriendMessageReceiver receiver, - params MessageBase[] chain) + MessageChain chain) { return await MessageManager .SendFriendMessageAsync(receiver.Sender.Id, chain); @@ -173,61 +190,53 @@ public static async Task SendMessageAsync(this FriendMessageReceiver rec /// /// /// - public static async Task SendMessageAsync(this TempMessageReceiver receiver, params MessageBase[] chain) + public static async Task SendMessageAsync(this TempMessageReceiver receiver, MessageChain chain) { return await MessageManager .SendTempMessageAsync(receiver.Sender.Id, receiver.Sender.Group.Id, chain); } /// - /// 发送群消息 - /// - /// - /// - /// - public static async Task SendMessageAsync(this GroupMessageReceiver receiver, string message) - { - return await MessageManager - .SendGroupMessageAsync(receiver.Sender.Group.Id, message); - } - - /// - /// 发送好友消息 + /// 撤回收到的消息 /// /// - /// - /// - public static async Task SendMessageAsync(this FriendMessageReceiver receiver, string message) + public static async Task RecallAsync(this GroupMessageReceiver receiver) { - return await MessageManager - .SendFriendMessageAsync(receiver.Sender.Id, message); + var id = receiver.MessageChain.OfType().First().MessageId; + await MessageManager + .RecallAsync(id, receiver.GroupId); } /// - /// 发送临时消息 + /// 撤回收到的消息 /// /// - /// - /// - public static async Task SendMessageAsync(this TempMessageReceiver receiver, string message) + public static async Task RecallAsync(this FriendMessageReceiver receiver) { - return await MessageManager - .SendTempMessageAsync(receiver.Sender.Id, receiver.Sender.Group.Id, message); + var id = receiver.MessageChain.OfType().First().MessageId; + await MessageManager + .RecallAsync(id, receiver.FriendId); } /// /// 撤回收到的消息 /// /// - public static async Task RecallAsync(this MessageReceiverBase receiver) + public static async Task RecallAsync(this TempMessageReceiver receiver) { var id = receiver.MessageChain.OfType().First().MessageId; await MessageManager - .RecallAsync(id); + .RecallAsync(id, receiver.Sender.Id); } + /// + /// 回复消息 + /// + /// + /// + /// public static async Task QuoteMessageAsync(this FriendMessageReceiver receiver, - params MessageBase[] chain) + MessageChain chain) { var id = receiver.MessageChain.OfType().First().MessageId; @@ -235,8 +244,14 @@ public static async Task QuoteMessageAsync(this FriendMessageReceiver re .QuoteFriendMessageAsync(receiver.Sender.Id, id, chain); } + /// + /// 回复消息 + /// + /// + /// + /// public static async Task QuoteMessageAsync(this GroupMessageReceiver receiver, - params MessageBase[] chain) + MessageChain chain) { var id = receiver.MessageChain.OfType().First().MessageId; @@ -244,8 +259,14 @@ public static async Task QuoteMessageAsync(this GroupMessageReceiver rec .QuoteGroupMessageAsync(receiver.Sender.Group.Id, id, chain); } + /// + /// 回复消息 + /// + /// + /// + /// public static async Task QuoteMessageAsync(this TempMessageReceiver receiver, - params MessageBase[] chain) + MessageChain chain) { var id = receiver.MessageChain.OfType().First().MessageId; @@ -253,30 +274,6 @@ public static async Task QuoteMessageAsync(this TempMessageReceiver rece .QuoteTempMessageAsync(receiver.Sender.Id, receiver.Sender.Group.Id, id, chain); } - public static async Task QuoteMessageAsync(this FriendMessageReceiver receiver, string message) - { - var id = receiver.MessageChain.OfType().First().MessageId; - - return await MessageManager - .QuoteFriendMessageAsync(receiver.Sender.Id, id, message); - } - - public static async Task QuoteMessageAsync(this GroupMessageReceiver receiver, string message) - { - var id = receiver.MessageChain.ToList().OfType().First().MessageId; - - return await MessageManager - .QuoteGroupMessageAsync(receiver.Sender.Group.Id, id, message); - } - - public static async Task QuoteMessageAsync(this TempMessageReceiver receiver, string message) - { - var id = receiver.MessageChain.OfType().First().MessageId; - - return await MessageManager - .QuoteTempMessageAsync(receiver.Sender.Id, receiver.Sender.Group.Id, id, message); - } - #endregion #region Request extensions @@ -287,18 +284,49 @@ public static async Task QuoteMessageAsync(this TempMessageReceiver rece /// /// /// + [Obsolete("请使用直接Approve/RejectAndBlock/RejectAsync 等方法")] public static async Task Handle(this NewFriendRequestedEvent @event, NewFriendRequestHandlers handler, string message = "") { await RequestManager.HandleNewFriendRequestedAsync(@event, handler, message); } + /// + /// 同意好友请求 + /// + /// + public static async Task ApproveAsync(this NewFriendRequestedEvent @event) + { + await RequestManager.HandleNewFriendRequestedAsync(@event, NewFriendRequestHandlers.Approve); + } + + /// + /// 拒绝好友请求 + /// + /// 事件 + /// 回复的消息 + public static async Task RejectAsync(this NewFriendRequestedEvent @event, string message = "") + { + await RequestManager.HandleNewFriendRequestedAsync(@event, NewFriendRequestHandlers.Reject, message); + } + + /// + /// 拒绝好友请求且屏蔽对方 + /// + /// 事件 + /// 回复的消息 + public static async Task RejectAndBlockAsync(this NewFriendRequestedEvent @event, string message = "") + { + await RequestManager.HandleNewFriendRequestedAsync(@event, NewFriendRequestHandlers.RejectAndBlock, message); + } + /// /// 处理新成员加群请求 /// /// /// /// + [Obsolete("请使用直接Approve/RejectAndBlock/RejectAsync 等方法")] public static async Task Handle(this NewMemberRequestedEvent requestedEvent, NewMemberRequestHandlers handler, string message = "") { @@ -306,18 +334,86 @@ await RequestManager .HandleNewMemberRequestedAsync(requestedEvent, handler, message); } + /// + /// 同意加群请求 + /// + /// + public static async Task ApproveAsync(this NewMemberRequestedEvent requestedEvent) + { + await RequestManager.HandleNewMemberRequestedAsync(requestedEvent, NewMemberRequestHandlers.Approve); + } + + /// + /// 拒绝加群请求 + /// + /// 事件源 + /// 回复消息 + public static async Task RejectAsync(this NewMemberRequestedEvent requestedEvent, string message = "") + { + await RequestManager.HandleNewMemberRequestedAsync(requestedEvent, NewMemberRequestHandlers.Reject, message); + } + + /// + /// 忽略加群请求 + /// + /// + public static async Task DismissAsync(this NewMemberRequestedEvent requestedEvent) + { + await RequestManager.HandleNewMemberRequestedAsync(requestedEvent, NewMemberRequestHandlers.Dismiss); + } + + /// + /// 拒绝加群请求并屏蔽 + /// + /// + /// 回复消息 + public static async Task RejectAndBlockAsync(this NewMemberRequestedEvent requestedEvent, string message = "") + { + await RequestManager.HandleNewMemberRequestedAsync(requestedEvent, NewMemberRequestHandlers.RejectAndBlock, message); + } + + /// + /// 忽略加群请求并屏蔽 + /// + /// + public static async Task DismissAndBlockAsync(this NewMemberRequestedEvent requestedEvent) + { + await RequestManager.HandleNewMemberRequestedAsync(requestedEvent, NewMemberRequestHandlers.DismissAndBlock); + } + /// /// 处理bot被邀请进群请求 /// /// /// /// - public static async Task Handle(NewInvitationRequestedEvent requestedEvent, + [Obsolete("请使用直接Approve/RejectAndBlock/RejectAsync 等方法")] + public static async Task Handle(this NewInvitationRequestedEvent requestedEvent, NewInvitationRequestHandlers handler, string message) { await RequestManager .HandleNewInvitationRequestedAsync(requestedEvent, handler, message); } + /// + /// 同意邀请 + /// + /// + public static async Task ApproveAsync(this NewInvitationRequestedEvent requestedEvent) + { + await RequestManager.HandleNewInvitationRequestedAsync(requestedEvent, NewInvitationRequestHandlers.Approve, + ""); + } + + /// + /// 拒绝邀请 + /// + /// + public static async Task RejectAsync(this NewInvitationRequestedEvent requestedEvent) + { + await RequestManager.HandleNewInvitationRequestedAsync(requestedEvent, NewInvitationRequestHandlers.Reject, + ""); + } + #endregion } \ No newline at end of file diff --git a/Mirai.Net/Utils/Scaffolds/ModuleScaffold.cs b/Mirai.Net/Utils/Scaffolds/ModuleScaffold.cs index 0aee09b..a4beecc 100644 --- a/Mirai.Net/Utils/Scaffolds/ModuleScaffold.cs +++ b/Mirai.Net/Utils/Scaffolds/ModuleScaffold.cs @@ -1,30 +1,32 @@ -using System; +using Mirai.Net.Data.Messages; +using Mirai.Net.Modules; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Mirai.Net.Data.Messages; -using Mirai.Net.Modules; namespace Mirai.Net.Utils.Scaffolds; +/// +/// 模块化拓展方法 +/// public static class ModuleScaffold { /// /// 获取泛型参数同一个命名空间下的所有模块 /// /// - public static IEnumerable GetModules(this T module) + public static List GetModules(this T module) where T : IModule { var basic = typeof(T); - + var types = Assembly.GetAssembly(basic).GetTypes() .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface) .Where(x => x.GetInterfaces().Any(x => x == typeof(IModule))) .Where(x => x!.FullName!.Contains(basic.Namespace!)) .ToList(); - foreach (var type in types) - yield return Activator.CreateInstance(type) as IModule; + return types.Select(t => Activator.CreateInstance(t) as IModule).ToList(); } /// @@ -32,7 +34,7 @@ public static IEnumerable GetModules(this T module) /// /// /// - public static void SubscribeModule(this IEnumerable modules, MessageReceiverBase @base) + public static void Raise(this List modules, MessageReceiverBase @base) { foreach (var module in modules) { diff --git a/README.md b/README.md index f3ada6c..a05de9c 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,41 @@ -# Mirai.Net 2.4.0-Pre1 +# Mirai.Net 2.5.2 -Mirai.Net 是基于[mirai-api-http]实现的轻量级[mirai]社区 sdk。 此项目遵循 -[AGPL-3.0 LICENSE](https://github.com/AHpxChina/Mirai.Net/blob/master/LICENSE) -开源。 +Mirai.NET 是基于 [mirai-api-http] 实现的 C# 版轻量级 [mirai] 社区 SDK。 -本项目的 QQ 群: `1042821169`,需要技术支持或者有什么问题(甚至水群聊天),欢迎加入群聊探讨。 +此项目遵循 [AGPL-3.0](https://github.com/AHpxChina/Mirai.Net/blob/master/LICENSE) 协议开源。 -项目文档: https://sinoahpx.github.io/Mirai.Net.Documents +QQ 群: [752379554](https://jq.qq.com/?_wv=1027&k=gdWqppEO)欢迎加入群聊探讨 ~~以及水群聊天~~ -如果你觉得这是个很酷的项目的话,不妨考虑给它点一个Star,如果你还觉得挺酷但还不够好的话,也欢迎提交Pull Request和Issue。 +文档:[Mirai.Net Documents](https://sinoahpx.github.io/Mirai.Net.Documents) + +如果你这个项目还不错,不妨考虑给它点一个 Star。 + +如果你还觉得挺酷但还不够好的话,也欢迎提交 Pull Request 和 Issue。 ## 速览 - 基于 [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) 开发,支持跨平台。 -- 适配最新的[mirai-api-http]插件。 -- 实现了[mirai-api-http]的`Http Adapter`和`Websocket Adapter` - - `Http Adapter`用来进行发送操作。 - - `Websocket Adapter`用来进行接收操作。 +- 适配最新的 [mirai-api-http] 插件。 +- 实现了 [mirai-api-http] 的 `Http Adapter` 和 `Websocket Adapter` + - `Http Adapter` 用来进行发送操作。 + - `Websocket Adapter` 用来进行接收操作。 - 基于 [Rx.NET](https://github.com/dotnet/reactive) 的推送系统。 - 有一堆好用的脚手架和拓展方法。 - 提供了简单的模块化和命令系统实现。 - 源代码结构 - Mirai.Net,主项目 - Mirai.Net.Test,控制台测试项目 - - Mirai.Net.UnitTest,单元测试项目(现在没啥用了) - -
- 实现的接口列表 - -_斜体的标注的接口是不稳定的_ - -~~删除线标注的接口是未实现的~~ - -- 账号信息 - - 获取好友列表 - - 获取群列表 - - 获取群成员列表 - - 获取 Bot 资料 - - 获取好友资料 - - 获取群成员资料 -- 消息发送和撤回 - - 发送好友消息 - - 发送群消息 - - 发送临时会话消息 - - 发送头像戳一戳消息 - - 撤回消息 -- 文件操作 - - _查看文件列表_ - - _获取文件信息_ - - _创建文件夹_ - - ~~删除文件~~ - - ~~移动文件~~ - - ~~重命名文件~~ -- 多媒体内容上传 - - 图片文件上传 - - 语音文件上传 - - 群文件上传 -- 账号管理 - - 删除好友 -- 群管理 - - 禁言群成员 - - 解除群成员禁言 - - 移除群成员 - - 退出群聊 - - 全体禁言 - - 解除全体禁言 - - 设置群精华消息 - - 获取群设置 - - 修改群设置 - - 获取群员设置 - - 修改群员设置 -- 事件处理 - - 添加好友申请 - - 用户入群申请 - - Bot 被邀请入群申请 - -
- -
- 支持的消息类型 - -- Quote - 回复消息 -- At - @消息 -- AtAll - @全体成员 -- Face - QQ 表情 -- Plain - 纯文本 -- Image - 图片 -- FlashImage - 闪照 -- Voice - 语音 -- Xml - XML 消息 -- Json - JSON 消息 -- App - App 消息 -- Poke - 戳一戳 -- Dice - 不知道是啥玩意 -- MusicShare - 音乐分享 -- ForwardMessage - 转发消息 -- File - 文件 -
+ - Mirai.Net.UnitTest,单元测试项目(现在没啥用了) ## 快速上手 -**(以下仅为一些简单示例,如果需要更详细的说明,请移步[文档]。有时候文档跟不上版本请[进群提问](#Mirai.Net 2.4.0-Pre1))** +**(以下仅为一些简单示例,如果需要更详细的说明,请移步[文档]。有时候文档跟不上版本请[进群提问](#mirainet-239)** ### 安装 - 使用 Nuget 安装(推荐) - - Nuget 包管理器: `Install-Package Mirai.Net -Version 2.4.0-Pre1` - - .NET CLI: `dotnet add package Mirai.Net --version 2.4.0-Pre1` + - Nuget 包管理器: `Install-Package Mirai.Net` + - .NET CLI: `dotnet add package Mirai.Net` - **或者在 IDE 的可视化界面搜索`Mirai.Net`安装最新版。** - 自己克隆这个仓库的默认分支,然后自己编译,然后自己添加 dll 引用。 @@ -132,7 +61,7 @@ using Mirai.Net.Sessions; using var bot = new MiraiBot { Address = "localhost:8080", - QQ = xx, + QQ = "xx", VerifyKey = "xx" }; ``` @@ -175,20 +104,20 @@ bot.EventReceived 发送消息的方法有两个参数: 发送到哪里和发送什么。所以第一个参数就是发消息的群号,第二个参数就是要发送的消息链(或者字符串)。 ```cs -await manager.SendGroupMessageAsync("xx", "Hello, World"); +await MessageManager.SendGroupMessageAsync("xx", "Hello, World"); ``` 或者: ```cs -await manager.SendGroupMessageAsync("xx", "Hello, ".Append(new AtMessage("xx")).Append(" !")); +await MessageManager.SendGroupMessageAsync("xx", new MessageChainBuilder().Plain("Hello, ").At("xx").Build()); ``` ## 贡献 -此项目欢迎任何人的[Pull Request](https://github.com/AHpxChina/Mirai.Net/pulls) 和[Issue](https://github.com/AHpxChina/Mirai.Net/issues),也欢迎 Star 和 Fork。 +此项目欢迎任何人的 [Pull Request](https://github.com/AHpxChina/Mirai.Net/pulls) 和 [Issue](https://github.com/AHpxChina/Mirai.Net/issues) 也欢迎 Star 和 Fork。 -如果你认为文档不够好,也欢迎对[文档仓库](https://github.com/SinoAHpx/Mirai.Net.Documents)提交[Pull Request](https://github.com/AHpxChina/Mirai.Net.Documents/pulls)和[Issue](https://github.com/AHpxChina/Mirai.Net.Documents/issues) +如果你认为文档不够好,也欢迎对 [文档仓库](https://github.com/SinoAHpx/Mirai.Net.Documents) 提交 [Pull Request](https://github.com/AHpxChina/Mirai.Net.Documents/pulls) 和 [Issue](https://github.com/AHpxChina/Mirai.Net.Documents/issues)。 ## 致谢 @@ -199,7 +128,7 @@ await manager.SendGroupMessageAsync("xx", "Hello, ".Append(new AtMessage("xx")). - [Json.NET](http://json.net/) ~~这甚至是这个项目名称的灵感来源~~ - [Websocket.Client](https://github.com/Marfusios/websocket-client) - [Rx.NET](https://github.com/dotnet/reactive) -- [AHpx.Extensions](https://github.com/AHpxChina/AHpx.E*xtensions) +- [Manganese](https://github.com/SinoAHpx/Manganese) [mirai-api-http]: https://github.com/project-mirai/mirai-api-http [mirai]: https://github.com/mamoe/mirai