├── .gitattributes ├── ProjHyperai.code-workspace ├── .nuke ├── parameters.json └── build.schema.json ├── src ├── Hyperai.Units │ ├── Hyperai.Units.Abstractions │ │ ├── ActionDelegate.cs │ │ ├── IFilter.cs │ │ ├── UnitBase.cs │ │ ├── Attributes │ │ │ ├── FilterByAttribute.cs │ │ │ ├── ReceiveAttribute.cs │ │ │ └── ExtractAttribute.cs │ │ ├── MessageContext.cs │ │ ├── UnitFactory.cs │ │ ├── ActionEntry.cs │ │ ├── MessageContextExtensions.cs │ │ ├── IUnitService.cs │ │ └── Hyperai.Units.Abstractions.csproj │ ├── Hyperai.Units │ │ ├── Extensions.cs │ │ ├── Hyperai.Units.csproj │ │ └── UnitMiddleware.cs │ ├── Hyperai.Units.Tests │ │ └── Hyperai.Units.Tests.csproj │ ├── Hyperai.Units.Abstractions.Tests │ │ ├── Attributes │ │ │ └── ExtractAttributeTests.cs │ │ └── Hyperai.Units.Abstractions.Tests.csproj │ └── README.md ├── Hyperai │ ├── Hyperai.Abstractions │ │ ├── Messages │ │ │ ├── IMessageChainParser.cs │ │ │ ├── IMessageChainFormatter.cs │ │ │ ├── ConcreteModels │ │ │ │ ├── FileSources │ │ │ │ │ ├── IFileSource.cs │ │ │ │ │ ├── StreamSource.cs │ │ │ │ │ └── UrlSource.cs │ │ │ │ ├── AppContent.cs │ │ │ │ ├── JsonContent.cs │ │ │ │ ├── XmlContent.cs │ │ │ │ ├── AtAll.cs │ │ │ │ ├── Video.cs │ │ │ │ ├── Voice.cs │ │ │ │ ├── ContentBase.cs │ │ │ │ ├── ImageBaseExtensions.cs │ │ │ │ ├── Unknown.cs │ │ │ │ ├── ImageBase.cs │ │ │ │ ├── StreamedFileBase.cs │ │ │ │ ├── At.cs │ │ │ │ ├── Source.cs │ │ │ │ ├── Quote.cs │ │ │ │ ├── Music.cs │ │ │ │ ├── Node.cs │ │ │ │ ├── Flash.cs │ │ │ │ ├── Image.cs │ │ │ │ ├── Plain.cs │ │ │ │ ├── Poke.cs │ │ │ │ └── Face.cs │ │ │ ├── MessageChainBuilder.cs │ │ │ ├── MessageElement.cs │ │ │ ├── MessageChainExtensions.cs │ │ │ ├── MessageChainBuilderExtensions.cs │ │ │ └── MessageChain.cs │ │ ├── Events │ │ │ ├── IEventHandler.cs │ │ │ ├── MessageEventType.cs │ │ │ ├── RecallEventArgs.cs │ │ │ ├── GenericEventArgs.cs │ │ │ ├── MessageEventArgs.cs │ │ │ ├── FriendRecallEventArgs.cs │ │ │ ├── FriendMessageEventArgs.cs │ │ │ ├── GroupLeftEventArgs.cs │ │ │ ├── GroupMessageEventArgs.cs │ │ │ ├── DefaultEventHandler.cs │ │ │ ├── GroupJoinedEventArgs.cs │ │ │ ├── FriendRequestEventArgs.cs │ │ │ ├── GroupRecallEventArgs.cs │ │ │ ├── GroupAllMutedEventArgs.cs │ │ │ ├── GroupMemberUnmutedEventArgs.cs │ │ │ ├── GroupResponseEventArgs.cs │ │ │ ├── GroupRequestEventArgs.cs │ │ │ ├── FriendResponseEventArgs.cs │ │ │ ├── GroupNameChangedEventArgs.cs │ │ │ ├── GroupMemberTitleChangedEventArgs.cs │ │ │ ├── GroupPermissionChangedEventArgs.cs │ │ │ ├── GroupMemberMutedEventArgs.cs │ │ │ └── GroupMemberCardChangedEventArgs.cs │ │ ├── Services │ │ │ ├── ApiClientConnectionState.cs │ │ │ ├── IApiClient.cs │ │ │ └── ApiClientExtensions.cs │ │ ├── Relations │ │ │ ├── User.cs │ │ │ ├── RelationModel.cs │ │ │ ├── Friend.cs │ │ │ ├── Self.cs │ │ │ ├── Group.cs │ │ │ ├── Member.cs │ │ │ └── Signature.cs │ │ ├── Receipts │ │ │ ├── MessageReceipt.cs │ │ │ └── GenericReceipt.cs │ │ ├── Middlewares │ │ │ └── IMiddleware.cs │ │ └── Hyperai.Abstractions.csproj │ ├── Hyperai.Core │ │ ├── HyperaiServerOptions.cs │ │ ├── HyperaiServerOptionsBuilderExtensions.cs │ │ ├── ServiceCollectionExtensions.cs │ │ ├── HyperaiServerOptionsBuilder.cs │ │ ├── Hyperai.Core.csproj │ │ ├── Serialization │ │ │ ├── MessageElementFactory.cs │ │ │ ├── HyperCodeParser.cs │ │ │ └── HyperCodeFormatter.cs │ │ └── HyperaiServer.cs │ ├── Hyperai.Abstractions.Tests │ │ ├── Messages │ │ │ ├── MessageChainBuilderTests.cs │ │ │ ├── MessageChainTests.cs │ │ │ └── PlainTests.cs │ │ ├── Hyperai.Abstractions.Tests.csproj │ │ └── Relations │ │ │ └── SignatureTests.cs │ ├── Hyperai.Core.Tests │ │ ├── Hyperai.Core.Tests.csproj │ │ └── Serialization │ │ │ ├── HyperCodeFormatterTests.cs │ │ │ └── HyperCodeParserTests.cs │ └── README.md └── HyperaiShell │ ├── HyperaiShell.Foundation │ ├── Bots │ │ ├── BotCollection.cs │ │ ├── IBotBuilder.cs │ │ ├── IBotCollectionBuilder.cs │ │ └── BotBase.cs │ ├── Shared.cs │ ├── Plugins │ │ ├── IPluginRepository.cs │ │ ├── IPluginConfiguration.cs │ │ ├── IPluginProperty.cs │ │ ├── IPluginContext.cs │ │ ├── PluginMeta.cs │ │ └── PluginBase.cs │ ├── Services │ │ ├── IBlockService.cs │ │ ├── IBotService.cs │ │ ├── IAttachmentService.cs │ │ ├── IAuthorizationService.cs │ │ ├── ForAttachmentUpdateScope.cs │ │ └── AuthorizationServiceExtensions.cs │ ├── Authorization │ │ ├── NormalTicket.cs │ │ ├── LimitedUseTicket.cs │ │ ├── ExpiryTicket.cs │ │ ├── TicketBase.cs │ │ └── Attributes │ │ │ └── RequiredTicketAttribute.cs │ ├── Data │ │ └── IRepository.cs │ ├── ModelExtensions │ │ ├── BlacklistExtensions.cs │ │ ├── AttachmentExtensions.cs │ │ ├── MessageChainExtensions.cs │ │ ├── RelationExtensions.cs │ │ ├── AuthorizationExtensions.cs │ │ └── ClientExtensions.cs │ └── HyperaiShell.Foundation.csproj │ ├── HyperaiShell.App │ ├── Models │ │ ├── Attachment.cs │ │ ├── BlockedUser.cs │ │ ├── TicketBoxExtensions.cs │ │ └── TicketBox.cs │ ├── Data │ │ ├── AssemblyNameTypeNameBinder.cs │ │ ├── SearchingTypeNameBinder.cs │ │ ├── LiteDbRepository.cs │ │ └── LiteDbQueryable.cs │ ├── Plugins │ │ ├── PluginRepository.cs │ │ ├── PluginContext.cs │ │ ├── PluginConfiguration.cs │ │ └── PluginManager.cs │ ├── Hangfire │ │ └── Logging │ │ │ ├── HangfireLogProvider.cs │ │ │ └── HangfireLogger.cs │ ├── Logging │ │ └── ConsoleFormatters │ │ │ ├── EventArgsFormatter.cs │ │ │ ├── MessageElementFormatter.cs │ │ │ └── RelationFormatter.cs │ ├── appsettings.toml │ ├── Middlewares │ │ ├── BotMiddleware.cs │ │ ├── MiddlewareExtensions.cs │ │ ├── TranslatorMiddleware.cs │ │ ├── BlockMiddleware.cs │ │ └── LoggingMiddleware.cs │ ├── Bots │ │ ├── BotCollectionBuilder.cs │ │ └── BotBuilder.cs │ ├── Helpers │ │ └── PathHelper.cs │ ├── Services │ │ ├── BlockService.cs │ │ ├── AuthorizationService.cs │ │ ├── ServiceCollectionExtensions.cs │ │ ├── AttachmentService.cs │ │ └── BotService.cs │ ├── HyperaiShell.App.csproj │ ├── Bootstrapper.cs │ ├── Packages │ │ └── PackageManager.cs │ └── Program.cs │ └── README.md ├── .editorconfig └── .github └── FUNDING.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | -------------------------------------------------------------------------------- /ProjHyperai.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "src/ProjHyperai.sln" 4 | } -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/ActionDelegate.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Units 2 | { 3 | public delegate void ActionDelegate(MessageContext context); 4 | } 5 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/IFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Units 2 | { 3 | public interface IFilter 4 | { 5 | bool Check(MessageContext context); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,.yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/IMessageChainParser.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages 2 | { 3 | public interface IMessageChainParser 4 | { 5 | MessageChain Parse(string text); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/UnitBase.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Units 2 | { 3 | public abstract class UnitBase 4 | { 5 | public MessageContext Context { get; internal set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | public interface IEventHandler where T : GenericEventArgs 4 | { 5 | void Handle(T args); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/MessageEventType.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | public enum MessageEventType 4 | { 5 | Friend, 6 | Group, 7 | Stranger 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/IMessageChainFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages 2 | { 3 | public interface IMessageChainFormatter 4 | { 5 | string Format(MessageChain chain); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Services/ApiClientConnectionState.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Services 2 | { 3 | public enum ApiClientConnectionState 4 | { 5 | Connected, 6 | Disconnected 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Bots/BotCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace HyperaiShell.Foundation.Bots 4 | { 5 | public class BotCollection : Collection 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Shared.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace HyperaiShell.Foundation 4 | { 5 | public static class Shared 6 | { 7 | public static IHost Host { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/FileSources/IFileSource.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Hyperai.Messages.ConcreteModels.FileSources 4 | { 5 | public interface IFileSource 6 | { 7 | Stream OpenRead(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/HyperaiServerOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Hyperai 5 | { 6 | public class HyperaiServerOptions 7 | { 8 | public IReadOnlyList Middlewares { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/User.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Relations 2 | { 3 | public abstract class User : RelationModel 4 | { 5 | /// 6 | /// 用户昵称 7 | /// 8 | public string Nickname { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/IPluginRepository.cs: -------------------------------------------------------------------------------- 1 | using HyperaiShell.Foundation.Data; 2 | 3 | namespace HyperaiShell.Foundation.Plugins 4 | { 5 | public interface IPluginRepository : IPluginProperty where TPlugin : PluginBase 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/AppContent.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class AppContent : ContentBase 4 | { 5 | public AppContent(string content) 6 | { 7 | Content = content; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/JsonContent.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class JsonContent : ContentBase 4 | { 5 | public JsonContent(string content) 6 | { 7 | Content = content; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/XmlContent.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class XmlContent : ContentBase 4 | { 5 | public XmlContent(string content) 6 | { 7 | Content = content; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Bots/IBotBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.Foundation.Bots 4 | { 5 | public interface IBotBuilder 6 | { 7 | IBotBuilder Configure(Action configure); 8 | 9 | BotBase Build(IServiceProvider provider); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/IBlockService.cs: -------------------------------------------------------------------------------- 1 | namespace HyperaiShell.Foundation.Services 2 | { 3 | public interface IBlockService 4 | { 5 | void Ban(long id, string reason); 6 | 7 | void Deban(long id); 8 | 9 | bool IsBanned(long id, out string reason); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/AtAll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class AtAll : MessageElement 7 | { 8 | public override int GetHashCode() 9 | { 10 | return 0; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/IPluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace HyperaiShell.Foundation.Plugins 4 | { 5 | public interface IPluginConfiguration : IPluginProperty 6 | where TPlugin : PluginBase 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Models/Attachment.cs: -------------------------------------------------------------------------------- 1 | namespace HyperaiShell.App.Models 2 | { 3 | public class Attachment 4 | { 5 | public int Id { get; set; } 6 | public string Target { get; set; } 7 | public object Object { get; set; } 8 | public string TypeName { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Bots/IBotCollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.Foundation.Bots 4 | { 5 | public interface IBotCollectionBuilder 6 | { 7 | IBotBuilder Add() where TBot : BotBase; 8 | 9 | BotCollection Build(IServiceProvider provider); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/IPluginProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.Foundation.Plugins 4 | { 5 | public interface IPluginProperty where TPlugin : PluginBase 6 | { 7 | Type Plugin => typeof(TPlugin); 8 | TProperty Value { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Receipts/MessageReceipt.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Receipts 2 | { 3 | public class MessageReceipt : GenericReceipt 4 | { 5 | public long MessageId 6 | { 7 | get => (long)this[nameof(MessageId)]; 8 | set => this[nameof(MessageId)] = value; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Video.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Messages.ConcreteModels.FileSources; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | public class Video : StreamedFileBase 6 | { 7 | public Video(IFileSource source) 8 | { 9 | Source = source; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Voice.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Messages.ConcreteModels.FileSources; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | public class Voice : StreamedFileBase 6 | { 7 | public Voice(IFileSource source) 8 | { 9 | Source = source; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/RecallEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | /// 4 | /// 消息撤回事件 5 | /// 6 | public class RecallEventArgs : GenericEventArgs 7 | { 8 | /// 9 | /// 消息标志 10 | /// 11 | public long MessageId { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/ContentBase.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class ContentBase : MessageElement 4 | { 5 | public string Content { get; set; } 6 | 7 | public override int GetHashCode() 8 | { 9 | return Content.GetHashCode(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/HyperaiServerOptionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai 2 | { 3 | public static class HyperaiServerOptionsBuilderExtensions 4 | { 5 | public static HyperaiServerOptionsBuilder Use(this HyperaiServerOptionsBuilder builder) 6 | { 7 | return builder.Use(typeof(TMiddleware)); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Authorization/NormalTicket.cs: -------------------------------------------------------------------------------- 1 | namespace HyperaiShell.Foundation.Authorization 2 | { 3 | public class NormalTicket : TicketBase 4 | { 5 | public NormalTicket(string name) : base(name) 6 | { 7 | } 8 | 9 | public override bool Verify() 10 | { 11 | return true; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/IBotService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Hyperai.Events; 3 | using HyperaiShell.Foundation.Bots; 4 | 5 | namespace HyperaiShell.Foundation.Services 6 | { 7 | public interface IBotService 8 | { 9 | IBotCollectionBuilder Builder { get; } 10 | 11 | Task PushAsync(GenericEventArgs args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GenericEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 一般事件的所有基类 7 | /// 8 | public class GenericEventArgs 9 | { 10 | /// 11 | /// 发生该事件的事件 12 | /// 13 | public DateTime Time { get; set; } = DateTime.Now; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Data/IRepository.cs: -------------------------------------------------------------------------------- 1 | using LiteDB; 2 | 3 | namespace HyperaiShell.Foundation.Data 4 | { 5 | public interface IRepository 6 | { 7 | void Store(T ins); 8 | 9 | bool Update(T ins); 10 | 11 | bool Delete(object id); 12 | 13 | ILiteQueryable Query(); 14 | 15 | bool Upsert(T ins); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Models/BlockedUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.App.Models 4 | { 5 | public class BlockedUser 6 | { 7 | public int Id { get; set; } 8 | public long UserId { get; set; } 9 | public string Reason { get; set; } 10 | public DateTime Enrollment { get; set; } 11 | public bool IsBanned { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/MessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Messages; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 消息发送事件 7 | /// 8 | public abstract class MessageEventArgs : GenericEventArgs 9 | { 10 | /// 11 | /// 消息 12 | /// 13 | public MessageChain Message { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/ImageBaseExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | 4 | namespace Hyperai.Messages.ConcreteModels 5 | { 6 | public static class ImageBaseExtensions 7 | { 8 | public static async Task OpenReadAsync(this ImageBase image) 9 | { 10 | return await Task.Run(image.OpenRead); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/FriendRecallEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 收: 好友消息撤回 7 | /// 8 | public sealed class FriendRecallEventArgs : RecallEventArgs 9 | { 10 | /// 11 | /// 目标好友 12 | /// 13 | public Friend WhoseMessage { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/IPluginContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Data; 3 | using Microsoft.Extensions.Configuration; 4 | 5 | namespace HyperaiShell.Foundation.Plugins 6 | { 7 | public interface IPluginContext 8 | { 9 | PluginMeta Meta { get; } 10 | Lazy Configuration { get; } 11 | Lazy Repository { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/FriendMessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 向好友发送消息 7 | /// 收: 收到好友消息 8 | /// 9 | public sealed class FriendMessageEventArgs : MessageEventArgs 10 | { 11 | /// 12 | /// 消息目标 13 | /// 14 | public Friend User { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Unknown.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class Unknown : MessageElement 4 | { 5 | public Unknown(string data) 6 | { 7 | Data = data; 8 | } 9 | 10 | public string Data { get; set; } 11 | 12 | public override int GetHashCode() 13 | { 14 | return Data.GetHashCode(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Data/AssemblyNameTypeNameBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using LiteDB; 3 | 4 | namespace HyperaiShell.App.Data 5 | { 6 | public class AssemblyNameTypeNameBinder : ITypeNameBinder 7 | { 8 | public string GetName(Type type) 9 | { 10 | return type.AssemblyQualifiedName; 11 | } 12 | 13 | public Type GetType(string name) 14 | { 15 | return Type.GetType(name); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupLeftEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 踢出成员 7 | /// 收: 有成员退出 8 | /// 9 | public sealed class GroupLeftEventArgs : GenericEventArgs 10 | { 11 | public Member Who { get; set; } 12 | public bool IsKicked { get; set; } 13 | public Member Operator { get; set; } 14 | 15 | public Group Group { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Authorization/LimitedUseTicket.cs: -------------------------------------------------------------------------------- 1 | namespace HyperaiShell.Foundation.Authorization 2 | { 3 | public class LimitedUseTicket : TicketBase 4 | { 5 | public LimitedUseTicket(string name, int count) : base(name) 6 | { 7 | Count = count; 8 | } 9 | 10 | public int Count { get; private set; } 11 | 12 | public override bool Verify() 13 | { 14 | return Count-- > 0; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/IAttachmentService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | 4 | namespace HyperaiShell.Foundation.Services 5 | { 6 | public interface IAttachmentService 7 | { 8 | void Attach(T ins, RelationModel toWhom); 9 | 10 | void Detach(RelationModel toWhom); 11 | 12 | T Retrieve(RelationModel fromWhom); 13 | 14 | ForAttachmentUpdateScope For(RelationModel model, out T ins, Func generator); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/ImageBase.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public abstract class ImageBase : StreamedFileBase 4 | { 5 | public string ImageId { get; set; } 6 | 7 | public override int GetHashCode() 8 | { 9 | return ImageId.GetHashCode(); 10 | } 11 | 12 | 13 | public override string ToString() 14 | { 15 | return $"<{GetType().Name.ToUpper()} {ImageId}>"; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Plugins/PluginRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Data; 3 | using HyperaiShell.Foundation.Plugins; 4 | 5 | namespace HyperaiShell.App.Plugins 6 | { 7 | public class PluginRepository : IPluginRepository where TPlugin : PluginBase 8 | { 9 | public Type BelongingTo => typeof(TPlugin); 10 | 11 | public IRepository Value => 12 | PluginManager.Instance.GetContextOfPlugin(BelongingTo).Repository.Value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Plugins/PluginContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Data; 3 | using HyperaiShell.Foundation.Plugins; 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace HyperaiShell.App.Plugins 7 | { 8 | public sealed class PluginContext : IPluginContext 9 | { 10 | public PluginMeta Meta { get; internal set; } 11 | public Lazy Configuration { get; internal set; } 12 | public Lazy Repository { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Middlewares/IMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Events; 2 | using Hyperai.Services; 3 | 4 | namespace Hyperai.Middlewares 5 | { 6 | public interface IMiddleware 7 | { 8 | /// 9 | /// 执行中间件功能 10 | /// 11 | /// 取得事件的 头部 12 | /// 待处理的事件参数 13 | /// 是否阻断事件处理管线, false 意味着否 14 | bool Run(IApiClient sender, GenericEventArgs args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Hangfire/Logging/HangfireLogProvider.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.Logging; 2 | using HyperaiShell.Foundation; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace HyperaiShell.App.Hangfire.Logging 7 | { 8 | public class HangfireLogProvider : ILogProvider 9 | { 10 | public ILog GetLogger(string name) 11 | { 12 | return new HangfireLogger(Shared.Host.Services.GetRequiredService>()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/Attributes/FilterByAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Units.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public class FilterByAttribute : Attribute 7 | { 8 | public FilterByAttribute(IFilter filter, string message = null) 9 | { 10 | Filter = filter; 11 | FailureMessage = message; 12 | } 13 | 14 | public IFilter Filter { get; set; } 15 | public string FailureMessage { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Authorization/ExpiryTicket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.Foundation.Authorization 4 | { 5 | public class ExpiryTicket : TicketBase 6 | { 7 | public ExpiryTicket(string name, DateTime expiration) : base(name) 8 | { 9 | ExpirationDate = expiration; 10 | } 11 | 12 | public DateTime ExpirationDate { get; } 13 | 14 | public override bool Verify() 15 | { 16 | return ExpirationDate >= DateTime.Now; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/PluginMeta.cs: -------------------------------------------------------------------------------- 1 | namespace HyperaiShell.Foundation.Plugins 2 | { 3 | public readonly struct PluginMeta 4 | { 5 | public string Identity { get; } 6 | public string FileBase { get; } 7 | public string SpaceDirectory { get; } 8 | 9 | public PluginMeta(string identity, string fileBase, string configDirectory) 10 | { 11 | Identity = identity; 12 | FileBase = fileBase; 13 | SpaceDirectory = configDirectory; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Plugins/PluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Plugins; 3 | using Microsoft.Extensions.Configuration; 4 | 5 | namespace HyperaiShell.App.Plugins 6 | { 7 | public class PluginConfiguration : IPluginConfiguration where TPlugin : PluginBase 8 | { 9 | public Type BelongingTo => 10 | typeof(TPlugin); 11 | 12 | public IConfiguration Value => 13 | PluginManager.Instance.GetContextOfPlugin(typeof(TPlugin)).Configuration.Value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/RelationModel.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Relations 2 | { 3 | public abstract class RelationModel 4 | { 5 | /// 表示事实上的一个对象,不同实例可以有同一个 . 6 | public long Identity { get; set; } 7 | 8 | /// 区分模型与模型, 同一个用户在不同群则 具有不同的值 9 | public abstract string Identifier { get; } 10 | 11 | public override int GetHashCode() 12 | { 13 | return Identifier.GetHashCode(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/Attributes/ReceiveAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Events; 3 | 4 | namespace Hyperai.Units.Attributes 5 | { 6 | [AttributeUsage(AttributeTargets.Method)] 7 | public class ReceiveAttribute : Attribute 8 | { 9 | public ReceiveAttribute(MessageEventType type) 10 | { 11 | Type = type; 12 | } 13 | 14 | public ReceiveAttribute() : this(MessageEventType.Friend) 15 | { 16 | } 17 | 18 | public MessageEventType Type { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupMessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 发送群消息 7 | /// 收: 收到群消息 8 | /// 9 | public sealed class GroupMessageEventArgs : MessageEventArgs 10 | { 11 | /// 12 | /// 发送者 13 | /// 14 | public Member User { get; set; } 15 | 16 | /// 17 | /// 所在群 18 | /// 19 | public Group Group { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/Friend.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Relations 2 | { 3 | /// 4 | /// 表示位于好友列表中的用户 5 | /// 6 | public class Friend : User 7 | { 8 | public override string Identifier => Identity.ToString(); 9 | 10 | /// 11 | /// 备注 12 | /// 13 | public string Remark { get; set; } 14 | 15 | public override string ToString() 16 | { 17 | return $"{Remark ?? "NULL"}({Identifier ?? "UNKNOWN"})"; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Authorization/TicketBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace HyperaiShell.Foundation.Authorization 4 | { 5 | public abstract class TicketBase 6 | { 7 | protected TicketBase(string name) 8 | { 9 | Name = name; 10 | Pattern = new Regex(Regex.Escape(name).Replace(@"\*\*", "[a-z0-9.]+").Replace(@"\*", "[a-z0-9]+")); 11 | } 12 | 13 | public string Name { get; } 14 | public Regex Pattern { get; } 15 | 16 | public abstract bool Verify(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/StreamedFileBase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Hyperai.Messages.ConcreteModels.FileSources; 3 | 4 | namespace Hyperai.Messages.ConcreteModels 5 | { 6 | public abstract class StreamedFileBase : MessageElement 7 | { 8 | public IFileSource Source { get; set; } 9 | 10 | public Stream OpenRead() 11 | { 12 | return Source.OpenRead(); 13 | } 14 | 15 | public override int GetHashCode() 16 | { 17 | return 0; 18 | // File 都不能比较,判定相等 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/IAuthorizationService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Hyperai.Relations; 3 | using HyperaiShell.Foundation.Authorization; 4 | 5 | namespace HyperaiShell.Foundation.Services 6 | { 7 | public interface IAuthorizationService 8 | { 9 | void PutTicket(RelationModel model, TicketBase ticket); 10 | 11 | bool CheckTicket(RelationModel model, string specificName); 12 | 13 | void RemoveTicket(RelationModel model, string name); 14 | 15 | IEnumerable GetTickets(RelationModel model); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/MessageContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Events; 3 | using Hyperai.Messages; 4 | using Hyperai.Relations; 5 | using Hyperai.Services; 6 | 7 | namespace Hyperai.Units 8 | { 9 | public sealed class MessageContext 10 | { 11 | public User User { get; set; } 12 | public Group Group { get; set; } 13 | public MessageEventType Type { get; set; } 14 | public MessageChain Message { get; set; } 15 | public DateTime SentAt { get; set; } 16 | public IApiClient Client { get; set; } 17 | public Self Me { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/At.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class At : MessageElement 7 | { 8 | public At(long targetId) 9 | { 10 | TargetId = targetId; 11 | } 12 | 13 | public long TargetId { get; set; } 14 | 15 | public override int GetHashCode() 16 | { 17 | return TargetId.GetHashCode(); 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return $""; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/FileSources/StreamSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Hyperai.Messages.ConcreteModels.FileSources 5 | { 6 | public sealed class StreamSource : IFileSource, IDisposable 7 | { 8 | public StreamSource(Stream data) 9 | { 10 | Data = data; 11 | } 12 | 13 | public Stream Data { get; set; } 14 | 15 | public void Dispose() 16 | { 17 | Data?.Dispose(); 18 | } 19 | 20 | public Stream OpenRead() 21 | { 22 | return Data; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Source.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class Source : MessageElement 7 | { 8 | public Source(long id) 9 | { 10 | MessageId = id; 11 | } 12 | 13 | public long MessageId { get; set; } 14 | 15 | public override int GetHashCode() 16 | { 17 | return MessageId.GetHashCode(); 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return $""; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Logging/ConsoleFormatters/EventArgsFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Ac682.Extensions.Logging.Console; 3 | using Hyperai.Events; 4 | 5 | namespace HyperaiShell.App.Logging.ConsoleFormatters 6 | { 7 | public class EventArgsFormatter : IObjectLoggingFormatter 8 | { 9 | public bool IsTypeAvailable(Type type) 10 | { 11 | return type.IsAssignableTo(typeof(GenericEventArgs)); 12 | } 13 | 14 | public string Format(object obj, Type type, string format = null) 15 | { 16 | return $"[yellow]{obj.GetType().Name}[/]"; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/appsettings.toml: -------------------------------------------------------------------------------- 1 | [Application] 2 | SelectedProfile = "gocqhttp" 3 | SentryEnabled = false 4 | DashboardEnabled = false # not working 5 | Daddy = 10001 6 | 7 | [Sentry] 8 | Dsn = "YOUR_DSN" 9 | MinimumEventLevel = "Warning" 10 | AttachStackTrace = true 11 | 12 | [[Clients]] 13 | Name = "gocqhttp" 14 | ClientTypeDefined = "Ac682.Hyperai.Clients.CQHTTP.CQClient,Ac682.Hyperai.Clients.CQHTTP" 15 | OptionsTypeDefined = "Ac682.Hyperai.Clients.CQHTTP.CQClientOptions,Ac682.Hyperai.Clients.CQHTTP" 16 | [Clients.Options] 17 | HttpPort = 6259 18 | WebSocketPort = 6260 19 | Host = "HOST" 20 | AccessToken = "ACCESS_TOKEN" 21 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Quote.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class Quote : MessageElement 7 | { 8 | public Quote(long messageId) 9 | { 10 | MessageId = messageId; 11 | } 12 | 13 | public long MessageId { get; set; } 14 | 15 | public override int GetHashCode() 16 | { 17 | return MessageId.GetHashCode(); 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return $""; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Hyperai 5 | { 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddHyperaiServer(this IServiceCollection services, 9 | Action configure) 10 | { 11 | var builder = new HyperaiServerOptionsBuilder(); 12 | configure(builder); 13 | return services 14 | .AddSingleton(builder.Build()) 15 | .AddHostedService(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/DefaultEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Services; 3 | 4 | namespace Hyperai.Events 5 | { 6 | public class DefaultEventHandler : IEventHandler where T : GenericEventArgs 7 | { 8 | private readonly Action _action; 9 | private readonly IApiClient _client; 10 | 11 | public DefaultEventHandler(IApiClient client, Action action) 12 | { 13 | _client = client; 14 | _action = action; 15 | } 16 | 17 | public void Handle(T args) 18 | { 19 | _action(_client, args); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupJoinedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 收: 新成员加入 7 | /// 8 | public sealed class GroupJoinedEventArgs : GenericEventArgs 9 | { 10 | /// 11 | /// 新成员 12 | /// 13 | public Member Who { get; set; } 14 | 15 | /// 16 | /// 目标群 17 | /// 18 | public Group Group { get; set; } 19 | 20 | /// 21 | /// 操作管理员 22 | /// 23 | public Member Operator { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/FriendRequestEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | /// 4 | /// 发: 向目标发送好友请求 5 | /// 收: 收到目标的好友请求 6 | /// 7 | public sealed class FriendRequestEventArgs : GenericEventArgs 8 | { 9 | /// 10 | /// 发送请求的人 11 | /// 12 | public long Who { get; set; } 13 | 14 | /// 15 | /// 验证消息 16 | /// 17 | public string Comment { get; set; } 18 | 19 | /// 20 | /// 好友请求标记 21 | /// 22 | public string Flag { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Plugins/PluginBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Bots; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace HyperaiShell.Foundation.Plugins 7 | { 8 | public abstract class PluginBase 9 | { 10 | public virtual IPluginContext Context { get; set; } 11 | 12 | public abstract void ConfigureBots(IBotCollectionBuilder bots, IConfiguration config); 13 | 14 | public abstract void ConfigureServices(IServiceCollection services); 15 | 16 | public abstract void OnStarted(IServiceProvider provider, IConfiguration config); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | ko_fi: ac682 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | custom: https://afdian.net/@nanjiu 13 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Music.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class Music : MessageElement 4 | { 5 | public enum MusicSource 6 | { 7 | QqMusic, 8 | Music163, 9 | XiaMi 10 | } 11 | 12 | public Music(MusicSource type, string id) 13 | { 14 | Type = type; 15 | MusicId = id; 16 | } 17 | 18 | public string MusicId { get; set; } 19 | 20 | public MusicSource Type { get; set; } 21 | 22 | public override int GetHashCode() 23 | { 24 | return MusicId.GetHashCode(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Node.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages.ConcreteModels 2 | { 3 | public class Node : MessageElement 4 | { 5 | public Node(long userId, string userDisplayName, MessageChain message) 6 | { 7 | UserId = userId; 8 | UserDisplayName = userDisplayName; 9 | Message = message; 10 | } 11 | 12 | public long UserId { get; set; } 13 | public string UserDisplayName { get; set; } 14 | public MessageChain Message { get; set; } 15 | 16 | public override int GetHashCode() 17 | { 18 | return 0; 19 | // 没法比较默认判相等 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupRecallEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 撤回一条群内消息 7 | /// 收: 群消息撤回 8 | /// 9 | public sealed class GroupRecallEventArgs : RecallEventArgs 10 | { 11 | /// 12 | /// 消息的发送者 13 | /// 14 | public Member WhoseMessage { get; set; } 15 | 16 | /// 17 | /// 所在群 18 | /// 19 | public Group Group { get; set; } 20 | 21 | /// 22 | /// 操作者 23 | /// 24 | public Member Operator { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupAllMutedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 开启或关闭禁言 7 | /// 收: 群被管理员开启或关闭禁言 8 | /// 9 | public sealed class GroupAllMutedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 开启或关闭禁言的管理员 13 | /// 14 | public Member Operator { get; set; } 15 | 16 | /// 17 | /// 目标群 18 | /// 19 | public Group Group { get; set; } 20 | 21 | /// 22 | /// 开启或是关闭 23 | /// 24 | public bool IsEnded { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupMemberUnmutedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 解除群成员的禁言 7 | /// 收: 群成员被管理员解除禁言 8 | /// 9 | public sealed class GroupMemberUnmutedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 操作者 13 | /// 14 | public Member Operator { get; set; } 15 | 16 | /// 17 | /// 所在群 18 | /// 19 | public Group Group { get; set; } 20 | 21 | /// 22 | /// 目标群成员 23 | /// 24 | public Member Whom { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Data/SearchingTypeNameBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using LiteDB; 4 | 5 | namespace HyperaiShell.App.Data 6 | { 7 | public class SearchingTypeNameBinder : ITypeNameBinder 8 | { 9 | public string GetName(Type type) 10 | { 11 | return $"{type.FullName},{type.Assembly.GetName().Name}"; 12 | } 13 | 14 | public Type GetType(string name) 15 | { 16 | var typeName = name.Substring(0, name.IndexOf(',')); 17 | var assName = name.Substring(typeName.Length + 1); 18 | return AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetName().Name == assName).GetType(typeName); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Middlewares/BotMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Events; 2 | using Hyperai.Middlewares; 3 | using Hyperai.Services; 4 | using HyperaiShell.Foundation.Services; 5 | using Sentry; 6 | 7 | namespace HyperaiShell.App.Middlewares 8 | { 9 | public class BotMiddleware : IMiddleware 10 | { 11 | private readonly IHub _hub; 12 | private readonly IBotService _service; 13 | 14 | public BotMiddleware(IBotService service, IHub hub) 15 | { 16 | _service = service; 17 | _hub = hub; 18 | } 19 | 20 | public bool Run(IApiClient sender, GenericEventArgs args) 21 | { 22 | _service.PushAsync(args).Wait(); 23 | return true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Flash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Hyperai.Messages.ConcreteModels.FileSources; 4 | 5 | namespace Hyperai.Messages.ConcreteModels 6 | { 7 | [Serializable] 8 | public class Flash : ImageBase 9 | { 10 | public Flash(string imageId, IFileSource source) 11 | { 12 | ImageId = imageId; 13 | Source = source; 14 | } 15 | 16 | public static Flash FromUrl(string id, Uri url) 17 | { 18 | return new Flash(id, new UrlSource(url)); 19 | } 20 | 21 | public static Flash FromStream(string id, Stream stream) 22 | { 23 | return new Flash(id, new StreamSource(stream)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Image.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Hyperai.Messages.ConcreteModels.FileSources; 4 | 5 | namespace Hyperai.Messages.ConcreteModels 6 | { 7 | [Serializable] 8 | public class Image : ImageBase 9 | { 10 | public Image(string imageId, IFileSource source) 11 | { 12 | ImageId = imageId; 13 | Source = source; 14 | } 15 | 16 | public static Image FromUrl(string id, Uri url) 17 | { 18 | return new Image(id, new UrlSource(url)); 19 | } 20 | 21 | public static Image FromStream(string id, Stream stream) 22 | { 23 | return new Image(id, new StreamSource(stream)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/UnitFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Hyperai.Units 5 | { 6 | public class UnitFactory 7 | { 8 | static UnitFactory() 9 | { 10 | var _ = new UnitFactory(); 11 | } 12 | 13 | public UnitFactory() 14 | { 15 | Instance = this; 16 | } 17 | 18 | public static UnitFactory Instance { get; set; } 19 | 20 | public UnitBase CreateUnit(Type type, MessageContext context, IServiceProvider provider) 21 | { 22 | var unit = (UnitBase)ActivatorUtilities.CreateInstance(provider, type); 23 | unit.Context = context; 24 | return unit; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupResponseEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | /// 4 | /// 发: 响应入群申请 5 | /// 6 | public sealed class GroupResponseEventArgs : GenericEventArgs 7 | { 8 | public enum ResponseOperation 9 | { 10 | Approve, 11 | Reject 12 | } 13 | 14 | /// 15 | /// 请求标志 16 | /// 17 | public string Flag { get; set; } 18 | 19 | /// 20 | /// 操作 21 | /// 22 | public ResponseOperation Operation { get; set; } 23 | 24 | /// 25 | /// 拒绝理由 26 | /// 27 | public string Reason { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupRequestEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | /// 4 | /// 发: 申请加入群 5 | /// 收: 收到入群的申请 6 | /// 7 | public sealed class GroupRequestEventArgs : GenericEventArgs 8 | { 9 | /// 10 | /// 群号 11 | /// 12 | public long GroupId { get; set; } 13 | 14 | /// 15 | /// 发起人 16 | /// 17 | public long Who { get; set; } 18 | 19 | /// 20 | /// 验证消息 21 | /// 22 | public string Comment { get; set; } 23 | 24 | /// 25 | /// 请求标志 26 | /// 27 | public string Flag { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/ActionEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Hyperai.Events; 4 | 5 | namespace Hyperai.Units 6 | { 7 | public struct ActionEntry 8 | { 9 | public MessageEventType Type { get; } 10 | public MethodInfo Action { get; } 11 | public Type Unit { get; } 12 | public object State { get; set; } 13 | 14 | public ActionEntry(MessageEventType type, MethodInfo action, Type unit, object state) 15 | { 16 | Type = type; 17 | Action = action; 18 | Unit = unit; 19 | State = state; 20 | } 21 | 22 | public override string ToString() 23 | { 24 | return $"{Unit.Name}.{Action.Name}@{Type}"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/Self.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Hyperai.Relations 5 | { 6 | /// 7 | /// 表示当前登录的账号 8 | /// 9 | public sealed class Self : User 10 | { 11 | public override string Identifier => Identity.ToString(); 12 | 13 | /// 14 | /// 自己所加过的群 15 | /// 16 | public Lazy> Groups { get; set; } 17 | 18 | /// 19 | /// 自己所加过的好友 20 | /// 21 | public Lazy> Friends { get; set; } 22 | 23 | public override string ToString() 24 | { 25 | return $"{Nickname ?? "NULL"}({Identifier ?? "UNKNOWN"})"; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/FriendResponseEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Events 2 | { 3 | /// 4 | /// 发: 响应好友请求 5 | /// 收: 好友请求被响应 6 | /// 7 | public sealed class FriendResponseEventArgs : GenericEventArgs 8 | { 9 | public enum ResponseOperation 10 | { 11 | Ignore, 12 | Approve, 13 | Reject 14 | } 15 | 16 | /// 17 | /// 被通过申请的人 18 | /// 19 | public long Who { get; set; } 20 | 21 | /// 22 | /// 好友请求标记 23 | /// 24 | public string Flag { get; set; } 25 | 26 | /// 27 | /// 好友请求的结果 28 | /// 29 | public ResponseOperation Operation { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupNameChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 修改群名字 7 | /// 收: 群名字改变 8 | /// 9 | public sealed class GroupNameChangedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 原名字 13 | /// 14 | public string Original { get; set; } 15 | 16 | /// 17 | /// 新名字 18 | /// 19 | public string Present { get; set; } 20 | 21 | /// 22 | /// 所在群 23 | /// 24 | public Group Group { get; set; } 25 | 26 | /// 27 | /// 修改者 28 | /// 29 | public Member Operator { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/MessageChainBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IBuilder; 3 | 4 | namespace Hyperai.Messages 5 | { 6 | public sealed class MessageChainBuilder : IBuilder 7 | { 8 | private readonly List components = new(); 9 | 10 | public MessageChain Build() 11 | { 12 | var chain = new MessageChain(components); 13 | return chain; 14 | } 15 | 16 | public MessageChainBuilder Add(MessageElement element) 17 | { 18 | components.Add(element); 19 | return this; 20 | } 21 | 22 | public MessageChainBuilder AddRange(IEnumerable elements) 23 | { 24 | components.AddRange(elements); 25 | return this; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupMemberTitleChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 修改群成员头衔 7 | /// 收: 群成员获得群主授予的头衔 8 | /// 9 | public sealed class GroupMemberTitleChangedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 原头衔 13 | /// 14 | public string Original { get; set; } 15 | 16 | /// 17 | /// 新头衔 18 | /// 19 | public string Present { get; set; } 20 | 21 | /// 22 | /// 所在群 23 | /// 24 | public Group Group { get; set; } 25 | 26 | /// 27 | /// 目标群成员 28 | /// 29 | public Member Who { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupPermissionChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 修改群成员的权限 7 | /// 收: 群成员权限改变 8 | /// 9 | public sealed class GroupPermissionChangedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 目标成员 13 | /// 14 | public Member Whom { get; set; } 15 | 16 | /// 17 | /// 原权限 18 | /// 19 | public GroupRole Original { get; set; } 20 | 21 | /// 22 | /// 新权限 23 | /// 24 | public GroupRole Present { get; set; } 25 | 26 | /// 27 | /// 所在群 28 | /// 29 | public Group Group { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Bots/BotCollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using HyperaiShell.Foundation.Bots; 4 | 5 | namespace HyperaiShell.App.Bots 6 | { 7 | public class BotCollectionBuilder : IBotCollectionBuilder 8 | { 9 | private readonly List botBuilders = new(); 10 | 11 | IBotBuilder IBotCollectionBuilder.Add() 12 | { 13 | var builder = new BotBuilder(typeof(TBot)); 14 | botBuilders.Add(builder); 15 | return builder; 16 | } 17 | 18 | public BotCollection Build(IServiceProvider provider) 19 | { 20 | var collection = new BotCollection(); 21 | foreach (var builder in botBuilders) collection.Add(builder.Build(provider)); 22 | return collection; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Hyperai.Units 4 | { 5 | public static class Extensions 6 | { 7 | /// 8 | /// 添加 Units 服务但不会搜索程序集里的 9 | /// 10 | /// service collection 11 | /// original service collection 12 | public static IServiceCollection AddUnits(this IServiceCollection services) 13 | { 14 | services.AddSingleton(); 15 | return services; 16 | } 17 | 18 | public static HyperaiServerOptionsBuilder UseUnits(this HyperaiServerOptionsBuilder builder) 19 | { 20 | builder.Use(); 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupMemberMutedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | 4 | namespace Hyperai.Events 5 | { 6 | /// 7 | /// 发: 禁言某个群成员 8 | /// 收: 群成员被管理员禁言 9 | /// 10 | public sealed class GroupMemberMutedEventArgs : GenericEventArgs 11 | { 12 | /// 13 | /// 操作的管理员 14 | /// 15 | public Member Operator { get; set; } 16 | 17 | /// 18 | /// 所在群 19 | /// 20 | public Group Group { get; set; } 21 | 22 | /// 23 | /// 禁言时常 24 | /// 25 | public TimeSpan Duration { get; set; } 26 | 27 | /// 28 | /// 目标群员 29 | /// 30 | public Member Whom { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Helpers/PathHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HyperaiShell.App.Helpers 4 | { 5 | public static class PathHelper 6 | { 7 | public static string Absolute2Relative(string rel) 8 | { 9 | return Absolute2Relative( 10 | Environment.CurrentDirectory.EndsWith('/') || Environment.CurrentDirectory.EndsWith('\\') 11 | ? Environment.CurrentDirectory 12 | : Environment.CurrentDirectory + '/', rel); 13 | } 14 | 15 | public static string Absolute2Relative(string b, string r) 16 | { 17 | return new Uri(new Uri(b, UriKind.Absolute), r).LocalPath; 18 | } 19 | 20 | public static string ToAbsolutePath(this string path) 21 | { 22 | return Absolute2Relative(path); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Plain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class Plain : MessageElement 7 | { 8 | /// 9 | /// 构造一个 对象 10 | /// 11 | /// 表示的纯文本 12 | /// 13 | public Plain(string text) 14 | { 15 | Text = text ?? throw new ArgumentNullException("Text cannot be null."); 16 | } 17 | 18 | public string Text { get; set; } 19 | 20 | public override int GetHashCode() 21 | { 22 | return Text.GetHashCode(); 23 | } 24 | 25 | public override string ToString() 26 | { 27 | return Text; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Logging/ConsoleFormatters/MessageElementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Ac682.Extensions.Logging.Console; 3 | using Hyperai.Messages; 4 | using Hyperai.Messages.ConcreteModels; 5 | 6 | namespace HyperaiShell.App.Logging.ConsoleFormatters 7 | { 8 | public class MessageElementFormatter : IObjectLoggingFormatter 9 | { 10 | public bool IsTypeAvailable(Type type) 11 | { 12 | return type.IsAssignableTo(typeof(MessageElement)); 13 | } 14 | 15 | public string Format(object obj, Type type, string format = null) 16 | { 17 | return obj switch 18 | { 19 | Plain plain => $"[green]\"{plain.Text}\"[/]", 20 | MessageElement ele => $"[darkcyan]{ele}[/]", 21 | _ => "[grey](UNKNOW)[/]" 22 | }; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/MessageContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Hyperai.Events; 3 | using Hyperai.Messages; 4 | using Hyperai.Relations; 5 | using Hyperai.Services; 6 | 7 | namespace Hyperai.Units 8 | { 9 | public static class MessageContextExtensions 10 | { 11 | public static async Task ReplyAsync(this MessageContext context, MessageChain message) 12 | { 13 | switch (context.Type) 14 | { 15 | case MessageEventType.Friend: 16 | await context.Client.SendFriendMessageAsync((Friend)context.User, message); 17 | break; 18 | 19 | case MessageEventType.Group: 20 | await context.Client.SendGroupMessageAsync(context.Group, message); 21 | break; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions.Tests/Messages/MessageChainBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Hyperai.Abstractions.Tests.Messages 7 | { 8 | [TestClass] 9 | public class MessageChainBuilderTests 10 | { 11 | [TestMethod] 12 | public void Build_SameComponents_ReturnsRightChain() 13 | { 14 | // Arrange 15 | var builder = new MessageChainBuilder(); 16 | builder.AddPlain("While I am a cat."); 17 | var comp = new MessageChain(new List { new Plain("While I am a cat.") }); 18 | // Act 19 | var chain = builder.Build(); 20 | // Assert 21 | Assert.IsTrue(chain.ChainEquals(comp)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Bots/BotBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Bots; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace HyperaiShell.App.Bots 6 | { 7 | public class BotBuilder : IBotBuilder 8 | { 9 | private readonly Type _botType; 10 | private Action _configure; 11 | 12 | public BotBuilder(Type botType) 13 | { 14 | _botType = botType; 15 | } 16 | 17 | public BotBase Build(IServiceProvider provider) 18 | { 19 | var bot = (BotBase)ActivatorUtilities.CreateInstance(provider, _botType); 20 | _configure?.Invoke(bot); 21 | 22 | return bot; 23 | } 24 | 25 | public IBotBuilder Configure(Action configure) 26 | { 27 | _configure = configure; 28 | return this; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/HyperaiServerOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Hyperai.Middlewares; 4 | using IBuilder; 5 | 6 | namespace Hyperai 7 | { 8 | public class HyperaiServerOptionsBuilder : IBuilder 9 | { 10 | private readonly List middlewares = new(); 11 | 12 | public HyperaiServerOptions Build() 13 | { 14 | return new HyperaiServerOptions 15 | { 16 | Middlewares = middlewares.AsReadOnly() 17 | }; 18 | } 19 | 20 | public HyperaiServerOptionsBuilder Use(Type middleware) 21 | { 22 | if (!typeof(IMiddleware).IsAssignableFrom(middleware)) 23 | throw new ArgumentException("Type should implements IMiddleware interface."); 24 | middlewares.Add(middleware); 25 | return this; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/Group.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Hyperai.Relations 5 | { 6 | /// 7 | /// 表示一个群,通常是自己所加过的 8 | /// 9 | public sealed class Group : RelationModel 10 | { 11 | public override string Identifier => Identity.ToString(); 12 | 13 | /// 14 | /// 群名 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// 群成员列表(会构成循环引用) 20 | /// 21 | public Lazy> Members { get; set; } 22 | 23 | /// 24 | /// 群主 25 | /// 26 | public Lazy Owner { get; set; } 27 | 28 | public override string ToString() 29 | { 30 | return $"{Name ?? "NULL"}({Identifier ?? "UNKNOWN"})"; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Tests/Hyperai.Units.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions.Tests/Attributes/ExtractAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Hyperai.Units.Attributes; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Hyperai.Units.Abstractions.Tests.Attributes 6 | { 7 | [TestClass] 8 | public class ExtractAttributeTests 9 | { 10 | [TestMethod] 11 | public void Ctor_GenerateNames() 12 | { 13 | // A & A 14 | var attr = new ExtractAttribute("!at [hyper.at({who})]"); 15 | // A 16 | Assert.IsTrue(attr.Names.SequenceEqual(new[] { "who" })); 17 | } 18 | 19 | [TestMethod] 20 | public void Regex_Match() 21 | { 22 | // A & A 23 | var attr = new ExtractAttribute("!ban {ban}"); 24 | var match = attr.Pattern.Match("!ban me!"); 25 | // A 26 | Assert.IsTrue(match.Success); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core.Tests/Hyperai.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 8.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions.Tests/Hyperai.Units.Abstractions.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Events/GroupMemberCardChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | 3 | namespace Hyperai.Events 4 | { 5 | /// 6 | /// 发: 修改群成员的群名片 7 | /// 收: 群成员的名片被自己或管理员修改 8 | /// 9 | public sealed class GroupMemberCardChangedEventArgs : GenericEventArgs 10 | { 11 | /// 12 | /// 原名片 13 | /// 14 | public string Original { get; set; } 15 | 16 | /// 17 | /// 新名片 18 | /// 19 | public string Present { get; set; } 20 | 21 | /// 22 | /// 自己或管理员 23 | /// 24 | public Member Operator { get; set; } 25 | 26 | /// 27 | /// 目标成员 28 | /// 29 | public Member WhoseName { get; set; } 30 | 31 | /// 32 | /// 所在群 33 | /// 34 | public Group Group { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Receipts/GenericReceipt.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Hyperai.Receipts 4 | { 5 | public class GenericReceipt 6 | { 7 | public bool Successful { get; set; } = true; 8 | public Dictionary Fields { get; set; } = new(); 9 | 10 | public object this[string key] 11 | { 12 | get 13 | { 14 | if (Fields.ContainsKey(key)) 15 | return Fields[key]; 16 | return null; 17 | } 18 | set 19 | { 20 | if (Fields.ContainsKey(key)) 21 | Fields[key] = value; 22 | else 23 | Fields.Add(key, value); 24 | } 25 | } 26 | 27 | public T Value(string key) 28 | { 29 | if (Fields.ContainsKey(key)) 30 | return (T)Fields[key]; 31 | return default; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions.Tests/Hyperai.Abstractions.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 8.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Logging/ConsoleFormatters/RelationFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Ac682.Extensions.Logging.Console; 3 | using Hyperai.Relations; 4 | 5 | namespace HyperaiShell.App.Logging.ConsoleFormatters 6 | { 7 | public class RelationFormatter : IObjectLoggingFormatter 8 | { 9 | public bool IsTypeAvailable(Type type) 10 | { 11 | return type.IsAssignableTo(typeof(RelationModel)); 12 | } 13 | 14 | public string Format(object obj, Type type, string format = null) 15 | { 16 | var name = obj switch 17 | { 18 | Friend friend => friend.Nickname, 19 | Member member => member.DisplayName, 20 | Group group => group.Name, 21 | RelationModel model => model.Identity.ToString(), 22 | _ => obj.GetType().Name 23 | }; 24 | return $"[purple]{name}[/][grey]([/][red]{((RelationModel)obj).Identity}[/][grey])[/]"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Middlewares/MiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Hyperai; 2 | 3 | namespace HyperaiShell.App.Middlewares 4 | { 5 | public static class MiddlewareExtensions 6 | { 7 | public static HyperaiServerOptionsBuilder UseBots(this HyperaiServerOptionsBuilder app) 8 | { 9 | app.Use(); 10 | return app; 11 | } 12 | 13 | public static HyperaiServerOptionsBuilder UseTranslator(this HyperaiServerOptionsBuilder app) 14 | { 15 | app.Use(); 16 | return app; 17 | } 18 | 19 | public static HyperaiServerOptionsBuilder UseLogging(this HyperaiServerOptionsBuilder app) 20 | { 21 | app.Use(); 22 | return app; 23 | } 24 | 25 | public static HyperaiServerOptionsBuilder UseBlacklist(this HyperaiServerOptionsBuilder app) 26 | { 27 | app.Use(); 28 | return app; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/FileSources/UrlSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace Hyperai.Messages.ConcreteModels.FileSources 6 | { 7 | [Serializable] 8 | public class UrlSource : IFileSource 9 | { 10 | public UrlSource(Uri url) 11 | { 12 | Url = url; 13 | } 14 | 15 | public Uri Url { get; set; } 16 | 17 | public bool IsRemote => Url.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) || 18 | Url.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase); 19 | 20 | public Stream OpenRead() 21 | { 22 | return Url.Scheme switch 23 | { 24 | var it when it == "http" || it == "https" => 25 | WebRequest.Create(Url).GetResponse().GetResponseStream(), 26 | "file" => File.OpenRead(Url.LocalPath), 27 | _ => throw new NotImplementedException() 28 | }; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Models/TicketBoxExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.Foundation.Authorization; 3 | 4 | namespace HyperaiShell.App.Models 5 | { 6 | public static class TicketBoxExtensions 7 | { 8 | public static void PutExpiryTicket(this TicketBox box, string name, DateTime expiration) 9 | { 10 | var ticket = new ExpiryTicket(name, expiration); 11 | box.Put(ticket); 12 | } 13 | 14 | public static void PutLimitedTicket(this TicketBox box, string name, int count) 15 | { 16 | var ticket = new LimitedUseTicket(name, count); 17 | box.Put(ticket); 18 | } 19 | 20 | public static void PutTicket(this TicketBox box, string name) 21 | { 22 | var ticket = new NormalTicket(name); 23 | box.Put(ticket); 24 | } 25 | 26 | public static void Remove(this TicketBox box, string ticketName) 27 | { 28 | box.Remove(box.FindSpecificTicket(ticketName)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Data/LiteDbRepository.cs: -------------------------------------------------------------------------------- 1 | using HyperaiShell.Foundation.Data; 2 | using LiteDB; 3 | 4 | namespace HyperaiShell.App.Data 5 | { 6 | public class LiteDbRepository : IRepository 7 | { 8 | private readonly LiteRepository _repository; 9 | 10 | public LiteDbRepository(LiteDatabase database) 11 | { 12 | _repository = new LiteRepository(database); 13 | } 14 | 15 | public void Store(T ins) 16 | { 17 | _repository.Insert(ins); 18 | } 19 | 20 | public bool Delete(object id) 21 | { 22 | return _repository.Delete(new BsonValue(id)); 23 | } 24 | 25 | public ILiteQueryable Query() 26 | { 27 | return _repository.Query(); 28 | } 29 | 30 | public bool Update(T ins) 31 | { 32 | return _repository.Update(ins); 33 | } 34 | 35 | public bool Upsert(T ins) 36 | { 37 | return _repository.Upsert(ins); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Data/LiteDbQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using LiteDB; 7 | 8 | namespace HyperaiShell.App.Data 9 | { 10 | public class LiteDbQueryable : IQueryable 11 | { 12 | internal readonly ILiteQueryable Queryable; 13 | 14 | public LiteDbQueryable(ILiteQueryable queryable) 15 | { 16 | Queryable = queryable; 17 | Expression = Expression.Constant(queryable); 18 | } 19 | 20 | public Type ElementType => typeof(T); 21 | 22 | public Expression Expression { get; } 23 | 24 | public IQueryProvider Provider => throw new NotImplementedException(); 25 | 26 | public IEnumerator GetEnumerator() 27 | { 28 | return Queryable.ToEnumerable().GetEnumerator(); 29 | } 30 | 31 | IEnumerator IEnumerable.GetEnumerator() 32 | { 33 | return Queryable.ToEnumerable().GetEnumerator(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/ForAttachmentUpdateScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | 4 | namespace HyperaiShell.Foundation.Services 5 | { 6 | public sealed class ForAttachmentUpdateScope : IDisposable 7 | { 8 | private readonly T _instance; 9 | private readonly IAttachmentService _service; 10 | private readonly RelationModel _toWhom; 11 | 12 | private bool isDisposed; 13 | 14 | public ForAttachmentUpdateScope(IAttachmentService service, T instance, RelationModel toWhom) 15 | { 16 | _service = service; 17 | _instance = instance; 18 | _toWhom = toWhom; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | Dispose(true); 24 | } 25 | 26 | private void Dispose(bool isDisposing) 27 | { 28 | if (!isDisposed && isDisposing) 29 | { 30 | if (_instance != null) _service.Attach(_instance, _toWhom); 31 | isDisposed = true; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Services/AuthorizationServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | using HyperaiShell.Foundation.Authorization; 4 | 5 | namespace HyperaiShell.Foundation.Services 6 | { 7 | public static class AuthorizationServiceExtensions 8 | { 9 | public static void PutLimited(this IAuthorizationService service, RelationModel model, string name, int count) 10 | { 11 | var ticket = new LimitedUseTicket(name, count); 12 | service.PutTicket(model, ticket); 13 | } 14 | 15 | public static void PutExpiry(this IAuthorizationService service, RelationModel model, string name, 16 | DateTime expiration) 17 | { 18 | var ticket = new ExpiryTicket(name, expiration); 19 | service.PutTicket(model, ticket); 20 | } 21 | 22 | public static void PutNormal(this IAuthorizationService service, RelationModel model, string name) 23 | { 24 | var ticket = new NormalTicket(name); 25 | service.PutTicket(model, ticket); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/IUnitService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Hyperai.Relations; 5 | 6 | namespace Hyperai.Units 7 | { 8 | public interface IUnitService 9 | { 10 | /// 11 | /// 搜索整个程序集查找并添加 到可用列表 12 | /// 13 | void SearchForUnits(); 14 | 15 | IEnumerable GetEntries(); 16 | 17 | /// 18 | /// 处理并分发一个消息上下文, 线程不安全 19 | /// 20 | /// 目标上下文, 其中的 不应该包含不可序列化元素和 元素 21 | void Handle(MessageContext context); 22 | 23 | /// 24 | /// 将一个通道加入到独占队列, 到达该通道的第一条消息将被目标委托所捕获. 这个过程是一次性的 25 | /// 26 | /// 所要占据的通道 27 | /// 目标委托 28 | /// 自加入起的一定时间后无效化 29 | void WaitOne(Signature channel, ActionDelegate action, TimeSpan timeout); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/MessageElement.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages 2 | { 3 | public abstract class MessageElement 4 | { 5 | public virtual string TypeName => GetType().Name; 6 | 7 | /// 8 | /// 将内容转换为字符串表示. 这将跳过无法表示的特殊类型, 或用符号代替. 9 | /// 10 | /// 11 | public override string ToString() 12 | { 13 | return string.Concat("<", TypeName.ToUpper(), ">"); 14 | } 15 | 16 | /// 17 | /// 获取内容的哈希并用于比较相等性 18 | /// 19 | /// 20 | public abstract override int GetHashCode(); 21 | 22 | public override bool Equals(object obj) 23 | { 24 | return GetHashCode().Equals(obj?.GetHashCode()); 25 | } 26 | 27 | public static bool operator ==(MessageElement a, MessageElement b) 28 | { 29 | return Equals(a, b); 30 | } 31 | 32 | public static bool operator !=(MessageElement a, MessageElement b) 33 | { 34 | return !(a == b); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/Hyperai.Units.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Hyperai.Units 6 | Hyperai.Units.Abstractions 7 | 2021.9.1 8 | GravityLab 9 | Unicore 10 | Hyperai.Units.Abstractions 11 | https://github.com/theGravityLab/Hyperai.Units 12 | Hyperai.Units 13 | GPL-3.0-only 14 | true 15 | true 16 | snupkg 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Hyperai.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Hyperai 6 | Hyperai.Abstractions 7 | 2021.9.1 8 | GravityLab 9 | Unicore 10 | Hyperai's interfaces, sealed and abstract classes. 11 | https://github.com/theGravityLab/Hyperai 12 | Hyperai 13 | GPL-3.0-only 14 | true 15 | true 16 | snupkg 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/Member.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Relations 4 | { 5 | public enum GroupRole 6 | { 7 | Owner, 8 | Member, 9 | Administrator 10 | } 11 | 12 | /// 13 | /// 群中的成员,必须位于一个群内 14 | /// 15 | public sealed class Member : User 16 | { 17 | public override string Identifier => $"{Identity}@{Group.Value.Identity}"; 18 | 19 | /// 20 | /// 头衔 21 | /// 22 | public string Title { get; set; } 23 | 24 | /// 25 | /// 群名片 26 | /// 27 | public string DisplayName { get; set; } 28 | 29 | /// 30 | /// 是否被禁言 31 | /// 32 | public bool IsMuted { get; set; } 33 | 34 | /// 35 | /// 所在群 36 | /// 37 | public Lazy Group { get; set; } 38 | 39 | /// 40 | /// 所在群中的角色 41 | /// 42 | public GroupRole Role { get; set; } 43 | 44 | public override string ToString() 45 | { 46 | return $"{DisplayName ?? "NULL"}({Identifier ?? "UNKNOWN"})"; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Poke.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | public enum PokeType 6 | { 7 | /// 8 | /// 戳一戳 9 | /// 10 | Poke = 1, 11 | 12 | /// 13 | /// 比心 14 | /// 15 | ShowLove, 16 | 17 | /// 18 | /// 点赞 19 | /// 20 | Like, 21 | 22 | /// 23 | /// 心碎 24 | /// 25 | Heartbroken, 26 | 27 | /// 28 | /// 666 29 | /// 30 | SixSixSix, 31 | 32 | /// 33 | /// 放大招 34 | /// 35 | FangDaZhao 36 | } 37 | 38 | [Serializable] 39 | public class Poke : MessageElement 40 | { 41 | public Poke(PokeType name) 42 | { 43 | Name = name; 44 | } 45 | 46 | public PokeType Name { get; private set; } 47 | 48 | public override int GetHashCode() 49 | { 50 | return Name.GetHashCode(); 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return $""; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/Hyperai.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Hyperai 6 | Hyperai.Core 7 | 2021.9.1 8 | GravityLab 9 | Unicore 10 | Hyperai.Core 11 | https://github.com/theGravityLab/Hyperai 12 | Hyperai 13 | GPL-3.0-only 14 | true 15 | snupkg 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units/Hyperai.Units.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Hyperai.Units 6 | 2021.9.1 7 | GravityLab 8 | Unicore 9 | Hyperai.Units 10 | https://github.com/theGravityLab/Hyperai.Units 11 | true 12 | GPL-3.0-only 13 | true 14 | snupkg 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Hangfire/Logging/HangfireLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hangfire.Logging; 3 | using Microsoft.Extensions.Logging; 4 | using LogLevel = Hangfire.Logging.LogLevel; 5 | 6 | namespace HyperaiShell.App.Hangfire.Logging 7 | { 8 | public class HangfireLogger : ILog 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public HangfireLogger(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null) 18 | { 19 | var message = messageFunc?.Invoke(); 20 | if (message == null) return true; 21 | _logger.Log(logLevel switch 22 | { 23 | LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, 24 | LogLevel.Info => Microsoft.Extensions.Logging.LogLevel.Information, 25 | LogLevel.Warn => Microsoft.Extensions.Logging.LogLevel.Warning, 26 | LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, 27 | LogLevel.Fatal => Microsoft.Extensions.Logging.LogLevel.Critical, 28 | LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace, 29 | _ => Microsoft.Extensions.Logging.LogLevel.None 30 | }, exception, message); 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Middlewares/TranslatorMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Hyperai.Events; 3 | using Hyperai.Messages; 4 | using Hyperai.Messages.ConcreteModels; 5 | using Hyperai.Middlewares; 6 | using Hyperai.Services; 7 | 8 | namespace HyperaiShell.App.Middlewares 9 | { 10 | public class TranslatorMiddleware : IMiddleware 11 | { 12 | private readonly IMessageChainParser _parser; 13 | 14 | public TranslatorMiddleware(IMessageChainParser parser) 15 | { 16 | _parser = parser; 17 | } 18 | 19 | public bool Run(IApiClient sender, GenericEventArgs args) 20 | { 21 | if (args is MessageEventArgs msgEvent) 22 | { 23 | var text = string.Join(string.Empty, msgEvent.Message.OfType().Select(x => x.Text)); 24 | if (text.Length > 8 && (text.StartsWith("```\r") || text.StartsWith("```\n")) && 25 | (text.EndsWith("\r```") || text.EndsWith("\n```"))) 26 | try 27 | { 28 | var msg = _parser.Parse(text[4..^4].Trim()); 29 | msgEvent.Message = msg; 30 | } 31 | catch 32 | { 33 | // not proper hyper code 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/BlacklistExtensions.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Relations; 2 | using HyperaiShell.Foundation.Services; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace HyperaiShell.Foundation.ModelExtensions 6 | { 7 | public static class BlacklsitExtensions 8 | { 9 | private static readonly IBlockService service; 10 | 11 | static BlacklsitExtensions() 12 | { 13 | service = Shared.Host.Services.GetRequiredService(); 14 | } 15 | 16 | /// 17 | /// 是否被搬 18 | /// 19 | /// 用户对象 20 | /// 21 | public static bool IsBanned(this User user) 22 | { 23 | return service.IsBanned(user.Identity, out _); 24 | } 25 | 26 | /// 27 | /// 搬它 28 | /// 29 | /// 用户 30 | /// 理由 31 | public static void Ban(this User user, string reason) 32 | { 33 | service.Ban(user.Identity, reason); 34 | } 35 | 36 | /// 37 | /// 反搬它 38 | /// 39 | /// 用户 40 | public static void Deban(this User user) 41 | { 42 | service.Deban(user.Identity); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Models/TicketBox.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using HyperaiShell.Foundation.Authorization; 4 | 5 | namespace HyperaiShell.App.Models 6 | { 7 | public class TicketBox 8 | { 9 | public List Tickets { get; set; } = new(); 10 | 11 | public bool Check(string name) 12 | { 13 | var mani = Tickets.Where(x => x.Pattern.Match(name).Success); 14 | var veri = false; 15 | var diposedTickets = new LinkedList(); 16 | foreach (var ticket in mani) 17 | { 18 | veri = ticket.Verify() || veri; // 不可短路 19 | if (!veri) diposedTickets.AddLast(ticket); 20 | } 21 | 22 | foreach (var ticket in diposedTickets) Tickets.Remove(ticket); 23 | return veri; 24 | } 25 | 26 | public void Put(TicketBase ticket) 27 | { 28 | Tickets.Add(ticket); 29 | } 30 | 31 | public void Remove(TicketBase ticket) 32 | { 33 | if (Tickets.Contains(ticket)) Tickets.Remove(ticket); 34 | } 35 | 36 | public TicketBase FindSpecificTicket(string ticketName) 37 | { 38 | return Tickets.FirstOrDefault(x => x.Name == ticketName); 39 | } 40 | 41 | public IEnumerable GetTickets() 42 | { 43 | return Tickets; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Services/BlockService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HyperaiShell.App.Models; 3 | using HyperaiShell.Foundation.Data; 4 | using HyperaiShell.Foundation.Services; 5 | 6 | namespace HyperaiShell.App.Services 7 | { 8 | public class BlockService : IBlockService 9 | { 10 | private readonly IRepository _repository; 11 | 12 | public BlockService(IRepository repository) 13 | { 14 | _repository = repository; 15 | } 16 | 17 | public void Ban(long id, string reason) 18 | { 19 | _repository.Upsert(new BlockedUser 20 | { UserId = id, Reason = reason, Enrollment = DateTime.Now, IsBanned = true }); 21 | } 22 | 23 | public void Deban(long id) 24 | { 25 | var user = _repository.Query().Where(x => x.UserId == id).FirstOrDefault(); 26 | if (user != null) 27 | { 28 | user.IsBanned = false; 29 | _repository.Update(user); 30 | } 31 | } 32 | 33 | public bool IsBanned(long id, out string reason) 34 | { 35 | var user = _repository.Query().Where(x => x.UserId == id).FirstOrDefault(); 36 | if (user == null) 37 | { 38 | reason = null; 39 | return false; 40 | } 41 | 42 | reason = user.IsBanned ? user.Reason : null; 43 | return user.IsBanned; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Services/IApiClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Hyperai.Events; 4 | using Hyperai.Receipts; 5 | 6 | namespace Hyperai.Services 7 | { 8 | public interface IApiClient : IDisposable 9 | { 10 | ApiClientConnectionState State { get; } 11 | 12 | /// 13 | /// 监听事件发布, 阻塞线程 14 | /// 15 | void Listen(); 16 | 17 | /// 18 | /// 连接 19 | /// 20 | void Connect(); 21 | 22 | /// 23 | /// 断开连接不再接收和分发事件 24 | /// 25 | void Disconnect(); 26 | 27 | /// 28 | /// 请求数据, 提供一个包含关键字段的模型, 返回同类型并包含请求数据的模型 29 | /// 30 | /// 模型类型 31 | /// 包含关键字段的模型 32 | /// 全新模型实例 33 | Task RequestAsync(T model); 34 | 35 | /// 36 | /// 监听某一类型的事件 37 | /// 38 | /// 监听的具体事件类型 39 | /// 时间处理过程 40 | void On(IEventHandler handler) where T : GenericEventArgs; 41 | 42 | /// 43 | /// 提交一个事件, 事件参数用 来表示 44 | /// 45 | /// 事件类型 46 | /// 包含具体参数的实例 47 | /// 回执 48 | Task SendAsync(TArgs args) where TArgs : GenericEventArgs; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Authorization/Attributes/RequiredTicketAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Hyperai.Units; 4 | using Hyperai.Units.Attributes; 5 | using HyperaiShell.Foundation.ModelExtensions; 6 | 7 | namespace HyperaiShell.Foundation.Authorization.Attributes 8 | { 9 | public class RequiredTicketAttribute : FilterByAttribute 10 | { 11 | private const string MESSAGE = "Operation denied: "; 12 | 13 | /// 14 | /// 检查是否具有某个特定的 15 | /// 16 | /// 票据(不含通配符 17 | public RequiredTicketAttribute(string specificName) : base(new CheckTicketFilter(new[] { specificName }), 18 | MESSAGE + specificName) 19 | { 20 | } 21 | 22 | /// 23 | /// 检查是否具有某个特定的 24 | /// 25 | /// 票据(不含通配符),多组取或 26 | public RequiredTicketAttribute(params string[] specificNames) : base(new CheckTicketFilter(specificNames), 27 | MESSAGE + string.Join(',', specificNames)) 28 | { 29 | } 30 | } 31 | 32 | internal class CheckTicketFilter : IFilter 33 | { 34 | private readonly IEnumerable names; 35 | 36 | public CheckTicketFilter(IEnumerable specificNames) 37 | { 38 | names = specificNames; 39 | } 40 | 41 | public bool Check(MessageContext context) 42 | { 43 | return names.Any(x => context.User.CheckPermission(x)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/Serialization/MessageElementFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Hyperai.Messages.ConcreteModels.FileSources; 5 | 6 | namespace Hyperai.Serialization 7 | { 8 | public static class MessageElementFactory 9 | { 10 | public static MessageElement Produce(string name, string code) 11 | { 12 | return name.ToLower() switch 13 | { 14 | "at" => new At(Convert.ToInt64(code)), 15 | "atall" => new AtAll(), 16 | "face" => new Face(Convert.ToInt32(code)), 17 | "flash" => new Flash(code.Substring(0, code.IndexOf(',')), 18 | new UrlSource(new Uri(code[(code.IndexOf(',') + 1)..], UriKind.Absolute))), 19 | "image" => new Image(code.Substring(0, code.IndexOf(',')), 20 | new UrlSource(new Uri(code[(code.IndexOf(',') + 1)..], UriKind.Absolute))), 21 | "plain" => new Plain(code), 22 | "poke" => new Poke(Enum.Parse(code)), 23 | "quote" => new Quote(Convert.ToInt32(code)), 24 | "source" => new Source(Convert.ToInt32(code)), 25 | "video" => new Video(new UrlSource(new Uri(code, UriKind.Absolute))), 26 | "voice" => new Voice(new UrlSource(new Uri(code, UriKind.Absolute))), 27 | "muisc" => new Music(Enum.Parse(code.Substring(0, code.IndexOf(','))), 28 | code[(code.IndexOf(',') + 1)..]), 29 | 30 | _ => throw new NotImplementedException() 31 | }; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core.Tests/Serialization/HyperCodeFormatterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Hyperai.Serialization; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace Hyperai.Core.Tests.Serialization 8 | { 9 | [TestClass] 10 | public class HyperCodeFormatterTests 11 | { 12 | [TestMethod] 13 | public void Format_Same() 14 | { 15 | // A 16 | var builder = new MessageChainBuilder(); 17 | builder.Add(new Source(1024)); 18 | builder.AddPlain("World"); 19 | builder.Add(Image.FromUrl("id", new Uri("https://example.com", UriKind.Absolute))); 20 | builder.AddPlain("Hello"); 21 | var chain = builder.Build(); 22 | var formatter = new HyperCodeFormatter(); 23 | // A 24 | var res = formatter.Format(chain); 25 | 26 | // A 27 | Assert.AreEqual("[hyper.source(1024)]World[hyper.image(id,https://example.com/)]Hello", res); 28 | } 29 | 30 | 31 | [TestMethod] 32 | public void Format_Escape_Same() 33 | { 34 | // A 35 | var builder = new MessageChainBuilder(); 36 | builder.Add(new Source(1024)); 37 | builder.AddPlain(@"[hyper.atall()]"); 38 | builder.AddAtAll(); 39 | var chain = builder.Build(); 40 | var formatter = new HyperCodeFormatter(); 41 | // A 42 | var res = formatter.Format(chain); 43 | 44 | // A 45 | Assert.AreEqual(@"[hyper.source(1024)]\[hyper.atall()][hyper.atall()]", res); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions.Tests/Messages/MessageChainTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Hyperai.Abstractions.Tests.Messages 7 | { 8 | [TestClass] 9 | public class MessageChainTests 10 | { 11 | [TestMethod] 12 | public void ChainEquals_WithSame_ReturnsTrue() 13 | { 14 | // Arrange 15 | var c1 = new MessageChain(new List { new Plain("While I am a cat.") }); 16 | var c2 = new MessageChain(new List { new Plain("While I am a cat.") }); 17 | // Act 18 | var assert = c1.ChainEquals(c2); 19 | // Assert 20 | Assert.IsTrue(assert); 21 | } 22 | 23 | [TestMethod] 24 | public void ChainEquals_WithOther_ReturnsFalse() 25 | { 26 | // Arrange 27 | var c1 = new MessageChain(new List { new Plain("While I am a cat.") }); 28 | var c2 = new MessageChain(new List { new Plain("While I am a dog.") }); 29 | // Act 30 | var assert = c1.ChainEquals(c2); 31 | // Assert 32 | Assert.IsFalse(assert); 33 | } 34 | 35 | [TestMethod] 36 | public void ChainEquals_WithNull_ReturnsFalse() 37 | { 38 | // Arrange 39 | var c1 = new MessageChain(new List { new Plain("While I am a cat.") }); 40 | // Act 41 | var assert = c1.ChainEquals(null); 42 | // Assert 43 | Assert.IsFalse(assert); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/HyperaiShell.Foundation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | HyperaiShell.Foundation 6 | 2021.9.1 7 | GravityLab 8 | Unicore 9 | Basics for HyperShell plugins 10 | https://github.com/theGravityLab/HyperaiShell 11 | HyperaiShell 12 | GPL-3.0-only 13 | true 14 | snupkg 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Middlewares/BlockMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Events; 2 | using Hyperai.Middlewares; 3 | using Hyperai.Services; 4 | using HyperaiShell.Foundation.Services; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace HyperaiShell.App.Middlewares 8 | { 9 | public class BlockMiddleware : IMiddleware 10 | { 11 | private readonly ILogger _logger; 12 | private readonly IBlockService _service; 13 | 14 | public BlockMiddleware(IBlockService service, ILogger logger) 15 | { 16 | _service = service; 17 | _logger = logger; 18 | } 19 | 20 | public bool Run(IApiClient sender, GenericEventArgs args) 21 | { 22 | switch (args) 23 | { 24 | case FriendMessageEventArgs friendMessage: 25 | { 26 | var banned = _service.IsBanned(friendMessage.User.Identity, out var reason); 27 | if (banned) 28 | _logger.LogInformation("Message rejected ({FriendId}) for {Reason}", 29 | friendMessage.User.Identity, reason); 30 | 31 | return !banned; 32 | } 33 | case GroupMessageEventArgs groupMessage: 34 | { 35 | var banned = _service.IsBanned(groupMessage.User.Identity, out var reason); 36 | if (banned) 37 | _logger.LogInformation("Message rejected: ({GroupId}) for {Reason}", 38 | groupMessage.Group.Identity, reason); 39 | 40 | return !banned; 41 | } 42 | default: 43 | return true; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/HyperaiServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Hyperai.Events; 5 | using Hyperai.Middlewares; 6 | using Hyperai.Services; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | 10 | namespace Hyperai 11 | { 12 | public class HyperaiServer : IHostedService 13 | { 14 | private readonly IApiClient _client; 15 | private readonly HyperaiServerOptions _options; 16 | private readonly IServiceProvider _provider; 17 | 18 | public HyperaiServer(IApiClient client, IServiceProvider provider, HyperaiServerOptions options) 19 | { 20 | _client = client; 21 | _provider = provider; 22 | _options = options; 23 | } 24 | 25 | public Task StartAsync(CancellationToken cancellationToken) 26 | { 27 | Task.Run(() => 28 | { 29 | _client.Connect(); 30 | _client.On((sender, args) => 31 | { 32 | using var scope = _provider.CreateScope(); 33 | foreach (var type in _options.Middlewares) 34 | { 35 | var middleware = 36 | ActivatorUtilities.CreateInstance(_provider, type) as IMiddleware; 37 | if (!middleware!.Run(sender, args)) break; 38 | } 39 | }); 40 | _client.Listen(); 41 | }, cancellationToken); 42 | return Task.CompletedTask; 43 | } 44 | 45 | public Task StopAsync(CancellationToken cancellationToken) 46 | { 47 | _client.Disconnect(); 48 | return Task.CompletedTask; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Plugins/PluginManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using HyperaiShell.Foundation.Plugins; 4 | 5 | namespace HyperaiShell.App.Plugins 6 | { 7 | public class PluginManager 8 | { 9 | private readonly IDictionary plugins = new Dictionary(); 10 | 11 | private PluginManager() 12 | { 13 | // PackageManager.Instance.AssemblyLoaded = SearchPluginBase; 14 | // 不应该挂钩子,应该在一个统一的时间点去遍历找 PluginBase 15 | } 16 | 17 | public static PluginManager Instance { get; } = new(); 18 | 19 | public void RegisterPlugin(Type plugin, IPluginContext context) 20 | { 21 | if (!plugin.IsSubclassOf(typeof(PluginBase))) 22 | throw new ArgumentException("Not derives from PluginBase", nameof(plugin)); 23 | 24 | plugins.Add(plugin, context); 25 | } 26 | 27 | public PluginBase Activate(Type type) 28 | { 29 | if (plugins.ContainsKey(type)) 30 | { 31 | var plugin = (PluginBase)Activator.CreateInstance(type); 32 | plugin.Context = plugins[type]; 33 | return plugin; 34 | } 35 | 36 | throw new InvalidOperationException("Argument type for a plugin has not registered yet."); 37 | } 38 | 39 | public void ActivateAll(Action configure) 40 | { 41 | foreach (var type in GetManagedPlugins()) configure(Activate(type)); 42 | } 43 | 44 | public IEnumerable GetManagedPlugins() 45 | { 46 | return plugins.Keys; 47 | } 48 | 49 | public IPluginContext GetContextOfPlugin(Type plugin) 50 | { 51 | return plugins[plugin]; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/MessageChainExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Hyperai.Messages.ConcreteModels; 3 | 4 | namespace Hyperai.Messages 5 | { 6 | public static class MessageChainExtensions 7 | { 8 | /// 9 | /// 是否能被作为引用对象, 用于区分手工搓的消息链和远端接收到的消息链 10 | /// 11 | /// 判断哪个消息链 12 | /// 能否被引用 13 | public static bool CanBeReplied(this MessageChain chain) 14 | { 15 | return chain.Any(x => x is Source); 16 | } 17 | 18 | /// 19 | /// 当能被引用时则产生一个包含引用信息的消息构造器 20 | /// 21 | /// 被引用的消息链 22 | /// 包含引用信息的消息构造器 23 | public static MessageChainBuilder MakeReply(this MessageChain chain) 24 | { 25 | var builder = new MessageChainBuilder(); 26 | builder.AddQuote(((Source)chain.First(x => x is Source)).MessageId); 27 | return builder; 28 | } 29 | 30 | /// 31 | /// 返回当前消息链的可读形式, 即去除了 元素 32 | /// 33 | /// 原链 34 | /// 不包含不便于程序阅读元素的新链 35 | public static MessageChain AsReadable(this MessageChain chain) 36 | { 37 | return new MessageChain(chain.Where(x => !(x is Source))); 38 | } 39 | 40 | /// 41 | /// 返回当前消息链的适用于发送形式, 去除仅用于接收的消息元素, 即去除了 42 | /// 43 | /// 原链 44 | /// 包含不便发送元素的新链 45 | public static MessageChain AsSendable(this MessageChain chain) 46 | { 47 | return new MessageChain(chain.Where(x => !(x is Source) && !(x is Quote))); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions.Tests/Relations/SignatureTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Hyperai.Abstractions.Tests.Relations 6 | { 7 | [TestClass] 8 | public class RelationMatcherTests 9 | { 10 | [TestMethod] 11 | public void FriendAndFriend_True() 12 | { 13 | var friend = new Friend { Identity = 123 }; 14 | 15 | var matcher = Signature.FromFriend(friend.Identity); 16 | 17 | Assert.IsTrue(matcher.Match(friend)); 18 | } 19 | 20 | [TestMethod] 21 | public void FriendAndMember_False() 22 | { 23 | var friend = new Friend { Identity = 123 }; 24 | 25 | var matcher = Signature.FromMember(321, friend.Identity); 26 | 27 | Assert.IsFalse(matcher.Match(friend)); 28 | } 29 | 30 | [TestMethod] 31 | public void MemberAndMember_True() 32 | { 33 | var member = new Member { Group = new Lazy(new Group { Identity = 321 }), Identity = 123 }; 34 | 35 | var matcher = Signature.FromMember(member.Group.Value.Identity, member.Identity); 36 | 37 | Assert.IsTrue(matcher.Match(member)); 38 | } 39 | 40 | [TestMethod] 41 | public void AnyMember_True() 42 | { 43 | var member = new Member { Group = new Lazy(new Group { Identity = 321 }), Identity = 123 }; 44 | 45 | var matcher = Signature.FromGroup(321); 46 | 47 | Assert.IsTrue(matcher.Match(member)); 48 | } 49 | 50 | [TestMethod] 51 | public void AnyMember_False() 52 | { 53 | var member = new Member { Group = new Lazy(new Group { Identity = 321 }), Identity = 123 }; 54 | 55 | var matcher = Signature.FromGroup(233); 56 | 57 | Assert.IsFalse(matcher.Match(member)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core.Tests/Serialization/HyperCodeParserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Hyperai.Messages.ConcreteModels.FileSources; 5 | using Hyperai.Serialization; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace Hyperai.Core.Tests.Serialization 9 | { 10 | [TestClass] 11 | public class HyperCodeParserTests 12 | { 13 | [TestMethod] 14 | public void Parse_Same() 15 | { 16 | // A 17 | var builder = new MessageChainBuilder(); 18 | builder.Add(new Source(1024)); 19 | builder.AddPlain("World"); 20 | builder.Add(new Image("{3A9B96FE-FDB8-D597-560A-3517A8031F5A}.mirai", 21 | new UrlSource(new Uri( 22 | "http://gchat.qpic.cn/gchatpic_new/2419328026/594429092-2161359014-3A9B96FEFDB8D597560A3517A8031F5A/0?term=2")))); 23 | builder.AddPlain("Hello"); 24 | var chain = builder.Build(); 25 | var parser = new HyperCodeParser(); 26 | // A 27 | var res = parser.Parse( 28 | "[hyper.source(1024)]World[hyper.image({3A9B96FE-FDB8-D597-560A-3517A8031F5A}.mirai,http://gchat.qpic.cn/gchatpic_new/2419328026/594429092-2161359014-3A9B96FEFDB8D597560A3517A8031F5A/0?term=2)]Hello"); 29 | 30 | // A 31 | Assert.IsTrue(chain.ChainEquals(res)); 32 | } 33 | 34 | [TestMethod] 35 | public void Parse_Unescape_Same() 36 | { 37 | var builder = new MessageChainBuilder(); 38 | builder.Add(new Source(1024)) 39 | .AddPlain(@"[hyper.atall()]") 40 | .AddAtAll(); 41 | var chain = builder.Build(); 42 | var parser = new HyperCodeParser(); 43 | 44 | var res = parser.Parse(@"[hyper.source(1024)]\[hyper.atall()][hyper.atall()]"); 45 | 46 | Assert.IsTrue(chain.ChainEquals(res)); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units.Abstractions/Attributes/ExtractAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace Hyperai.Units.Attributes 8 | { 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class ExtractAttribute : Attribute 11 | { 12 | /// 13 | /// 创建一个模板用于匹配消息 14 | /// 15 | /// 模板 16 | /// 是否裁剪前后空格和合并二个空格为一个 17 | public ExtractAttribute(string pattern, bool trimSpaces = false) 18 | { 19 | TrimSpaces = trimSpaces; 20 | RawString = pattern; 21 | var parameters = Regex.Matches(pattern, @"\{(?[A-Za-z0-9_]+)\}"); 22 | Names = parameters.Select(x => x.Groups["name"].Value).ToList(); 23 | pattern = '^' + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\{", "{") + '$'; 24 | // pattern = Regex.Replace(pattern, @"\{([A-Za-z0-9_]+)\}", @"([\S]+)"); 25 | var nameMatches = Regex.Matches(pattern, @"\{([A-Za-z0-9_]+)\}"); 26 | var patternBuilder = new StringBuilder(); 27 | var addedLength = 0; 28 | for (var i = 0; i < nameMatches.Count; i++) 29 | { 30 | var match = nameMatches[i]; 31 | patternBuilder.Append(pattern[addedLength..match.Index]); 32 | patternBuilder.Append(i == nameMatches.Count - 1 ? @"([\S\s]+)" : @"([\S]+)"); 33 | addedLength = match.Index + match.Length; 34 | } 35 | 36 | if (addedLength < pattern.Length) patternBuilder.Append(pattern[addedLength..]); 37 | Pattern = new Regex(patternBuilder.ToString()); 38 | } 39 | 40 | public Regex Pattern { get; } 41 | public string RawString { get; } 42 | public bool TrimSpaces { get; } 43 | public IList Names { get; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Services/AuthorizationService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Hyperai.Relations; 4 | using HyperaiShell.App.Models; 5 | using HyperaiShell.Foundation.Authorization; 6 | using HyperaiShell.Foundation.ModelExtensions; 7 | using HyperaiShell.Foundation.Services; 8 | using Microsoft.Extensions.Configuration; 9 | 10 | namespace HyperaiShell.App.Services 11 | { 12 | public class AuthorizationService : IAuthorizationService 13 | { 14 | private readonly string _daddy; 15 | 16 | public AuthorizationService(IConfiguration configuration) 17 | { 18 | _daddy = configuration["Application:Daddy"]; 19 | } 20 | 21 | public bool CheckTicket(RelationModel model, string specificName) 22 | { 23 | if (specificName == "whoisyourdaddy" && _daddy != null && _daddy == model.Identity.ToString()) return true; 24 | 25 | var ticketBox = model.Retrieve(); 26 | if (ticketBox == null) return false; 27 | 28 | var pass = ticketBox.Check(specificName); 29 | model.Attach(ticketBox); 30 | return pass; 31 | } 32 | 33 | public void PutTicket(RelationModel model, TicketBase ticket) 34 | { 35 | using (model.For(out var ticketBox, () => new TicketBox())) 36 | { 37 | ticketBox.Put(ticket); 38 | } 39 | } 40 | 41 | public void RemoveTicket(RelationModel model, string name) 42 | { 43 | using (model.For(out TicketBox ticketBox)) 44 | { 45 | if (ticketBox != null) ticketBox.Remove(name); 46 | } 47 | } 48 | 49 | public IEnumerable GetTickets(RelationModel model) 50 | { 51 | var ticketBox = model.Retrieve(); 52 | if (ticketBox != null) return ticketBox.GetTickets(); 53 | return Enumerable.Empty(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Services/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Hyperai.Services; 4 | using HyperaiShell.Foundation.Services; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace HyperaiShell.App.Services 9 | { 10 | public static class ServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddClients(this IServiceCollection services, IConfiguration configuration) 13 | { 14 | var profileName = configuration["Application:SelectedProfile"]; 15 | var profile = configuration 16 | .GetSection("Clients") 17 | .GetChildren() 18 | .First(x => x["Name"] == profileName); 19 | var clientType = Type.GetType(profile["ClientTypeDefined"], true); 20 | var optionsType = Type.GetType(profile["OptionsTypeDefined"], true); 21 | var optionsSection = profile.GetSection("Options"); 22 | services.AddSingleton(typeof(IApiClient), clientType!); 23 | services.AddSingleton(optionsType, optionsSection.Get(optionsType)); 24 | 25 | return services; 26 | } 27 | 28 | public static IServiceCollection AddBots(this IServiceCollection services) 29 | { 30 | services.AddSingleton(); 31 | return services; 32 | } 33 | 34 | public static IServiceCollection AddAuthorizationService(this IServiceCollection services) 35 | { 36 | services.AddSingleton(); 37 | return services; 38 | } 39 | 40 | public static IServiceCollection AddAttachments(this IServiceCollection services) 41 | { 42 | services.AddSingleton(); 43 | return services; 44 | } 45 | 46 | public static IServiceCollection AddBlacklist(this IServiceCollection services) 47 | { 48 | services.AddSingleton(); 49 | return services; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/Bots/BotBase.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Events; 2 | using Hyperai.Relations; 3 | 4 | namespace HyperaiShell.Foundation.Bots 5 | { 6 | public abstract class BotBase 7 | { 8 | public virtual Self Me { get; set; } 9 | 10 | public virtual void OnEverything(object sender, GenericEventArgs args) 11 | { 12 | } 13 | 14 | public virtual void OnFriendMessage(object client, FriendMessageEventArgs args) 15 | { 16 | } 17 | 18 | public virtual void OnGroupMessage(object client, GroupMessageEventArgs args) 19 | { 20 | } 21 | 22 | public virtual void OnFriendRecall(object client, FriendRecallEventArgs args) 23 | { 24 | } 25 | 26 | public virtual void OnGroupRecall(object client, GroupRecallEventArgs args) 27 | { 28 | } 29 | 30 | public virtual void OnGroupLeft(object client, GroupLeftEventArgs args) 31 | { 32 | } 33 | 34 | public virtual void OnGroupJoined(object client, GroupJoinedEventArgs args) 35 | { 36 | } 37 | 38 | public virtual void OnGroupMemberMuted(object client, GroupMemberMutedEventArgs args) 39 | { 40 | } 41 | 42 | public virtual void OnGroupMemberUnmuted(object client, GroupMemberUnmutedEventArgs args) 43 | { 44 | } 45 | 46 | public virtual void OnGroupAllMuted(object client, GroupAllMutedEventArgs args) 47 | { 48 | } 49 | 50 | public virtual void OnGroupMemberCardChanged(object client, GroupMemberCardChangedEventArgs args) 51 | { 52 | } 53 | 54 | public virtual void OnGroupMemberTitleChanged(object client, GroupMemberTitleChangedEventArgs args) 55 | { 56 | } 57 | 58 | public virtual void OnGroupPermissionChanged(object client, GroupPermissionChangedEventArgs args) 59 | { 60 | } 61 | 62 | public virtual void OnGroupRequest(object client, GroupRequestEventArgs args) 63 | { 64 | } 65 | 66 | public virtual void OnFriendRequest(object client, FriendRequestEventArgs args) 67 | { 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/MessageChainBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Messages.ConcreteModels; 2 | using Hyperai.Messages.ConcreteModels.FileSources; 3 | 4 | namespace Hyperai.Messages 5 | { 6 | public static class MessageChainBuilderExtensions 7 | { 8 | public static MessageChainBuilder AddPlain(this MessageChainBuilder builder, string text) 9 | { 10 | var plain = new Plain(text); 11 | return builder.Add(plain); 12 | } 13 | 14 | public static MessageChainBuilder AddImage(this MessageChainBuilder builder, string imageId, 15 | IFileSource source) 16 | { 17 | var image = new Image(imageId, source); 18 | return builder.Add(image); 19 | } 20 | 21 | public static MessageChainBuilder AddFace(this MessageChainBuilder builder, FaceType type) 22 | { 23 | return AddFace(builder, (int)type); 24 | } 25 | 26 | public static MessageChainBuilder AddFace(this MessageChainBuilder builder, int faceId) 27 | { 28 | var face = new Face(faceId); 29 | return builder.Add(face); 30 | } 31 | 32 | public static MessageChainBuilder AddPoke(this MessageChainBuilder builder, PokeType type) 33 | { 34 | var poke = new Poke(type); 35 | return builder.Add(poke); 36 | } 37 | 38 | public static MessageChainBuilder AddFlash(this MessageChainBuilder builder, string imageId, 39 | IFileSource source) 40 | { 41 | var image = new Flash(imageId, source); 42 | return builder.Add(image); 43 | } 44 | 45 | public static MessageChainBuilder AddQuote(this MessageChainBuilder builder, long target) 46 | { 47 | var quote = new Quote(target); 48 | return builder.Add(quote); 49 | } 50 | 51 | public static MessageChainBuilder AddAt(this MessageChainBuilder builder, long who) 52 | { 53 | var at = new At(who); 54 | return builder.Add(at); 55 | } 56 | 57 | public static MessageChainBuilder AddAtAll(this MessageChainBuilder builder) 58 | { 59 | var atall = new AtAll(); 60 | return builder.Add(atall); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/MessageChain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Hyperai.Messages 8 | { 9 | /// 10 | /// 一份消息组件列表, 提供针对于消息组件的专有操作 11 | /// 12 | public sealed class MessageChain : IEnumerable 13 | { 14 | /// 15 | /// 使用已经确定的 集合创建一个 实例 16 | /// 17 | /// 组件集合 18 | public MessageChain(IEnumerable list) 19 | { 20 | Components = list ?? throw new ArgumentNullException(nameof(list), "List cannot be null."); 21 | Count = list.Count(); 22 | } 23 | 24 | private MessageChain() 25 | { 26 | } 27 | 28 | public int Count { get; } 29 | internal IEnumerable Components { get; set; } 30 | 31 | public IEnumerator GetEnumerator() 32 | { 33 | return Components.GetEnumerator(); 34 | } 35 | 36 | IEnumerator IEnumerable.GetEnumerator() 37 | { 38 | return Components.GetEnumerator(); 39 | } 40 | 41 | /// 42 | /// 获取消息的字符串表示, 特殊类型用的表示由 决定. 如果要序列化成支持反序列化的字符串请使用 43 | /// . 44 | /// 45 | /// 对象的字符串表示 46 | public override string ToString() 47 | { 48 | var sb = new StringBuilder(); 49 | foreach (var component in Components) sb.Append(component); 50 | return sb.ToString(); 51 | } 52 | 53 | /// 54 | /// 判断是否与另一个消息内容完全相同 55 | /// 56 | /// 对比的另一个实例 57 | /// 58 | public bool ChainEquals(MessageChain other) 59 | { 60 | if (other == null || other.Count != Count) return false; 61 | 62 | return Components.SequenceEqual(other.Components); 63 | } 64 | 65 | public static MessageChain Construct(params MessageElement[] components) 66 | { 67 | return new MessageChain(components); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/Serialization/HyperCodeParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.RegularExpressions; 3 | using Hyperai.Messages; 4 | 5 | namespace Hyperai.Serialization 6 | { 7 | public class HyperCodeParser : IMessageChainParser 8 | { 9 | internal static readonly Regex HyperRegex = 10 | new(@"\[hyper\.(?[a-z]+)\((?[a-z0-9A-Z_\\:/,.@\-=?&#{}\ ]*)\)\]"); 11 | 12 | public MessageChain Parse(string text) 13 | { 14 | var builder = new MessageChainBuilder(); 15 | var matches = HyperRegex.Matches(text); 16 | 17 | var addedLength = 0; 18 | 19 | foreach (Match match in matches) 20 | { 21 | if (!Validate(match.Index, text)) continue; 22 | var plain = text[addedLength..match.Index]; 23 | if (!string.IsNullOrEmpty(plain)) builder.AddPlain(Unescape(plain)); 24 | builder.Add(MessageElementFactory.Produce(match.Groups["name"].Value, match.Groups["code"].Value)); 25 | addedLength = match.Index + match.Length; 26 | } 27 | 28 | if (addedLength < text.Length) builder.AddPlain(Unescape(text[addedLength..])); 29 | return builder.Build(); 30 | } 31 | 32 | private static string Unescape(string text) 33 | { 34 | StringBuilder sb = new(); 35 | var addedLength = 0; 36 | 37 | var matches = HyperRegex.Matches(text); 38 | foreach (Match match in matches) 39 | { 40 | var count = CountBefore(match.Index, text); 41 | sb.Append(text[addedLength..(match.Index - (count / 2 + 1))]); 42 | sb.Append(match.Value); 43 | addedLength = match.Index + match.Length; 44 | } 45 | 46 | if (addedLength < text.Length) sb.Append(text[addedLength..]); 47 | return sb.ToString(); 48 | } 49 | 50 | internal static bool Validate(int pos, string text) 51 | { 52 | return CountBefore(pos, text) % 2 == 0; 53 | } 54 | 55 | internal static int CountBefore(int pos, string text) 56 | { 57 | // pos 是左括号的位置 58 | var start = pos; 59 | while (start > 0 && text[start - 1] == '\\') start--; 60 | return pos - start; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Hyperai/README.md: -------------------------------------------------------------------------------- 1 | # Hyperai 2 | 3 | ProjHyperai 基础库. 4 | 5 | 6 | 7 | [![Contributors][contributors-shield]][contributors-url] 8 | [![Forks][forks-shield]][forks-url] 9 | [![Stargazers][stars-shield]][stars-url] 10 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai?ref=badge_shield) 11 | 12 | 13 | 14 |

15 | 16 | Logo 17 | 18 |

19 | 20 | 21 |

ProjHyperai

22 |

23 | QQ/TG 机器人开发在这入门 24 |
25 | 本项目的文档 » 26 |
27 |
28 | 加入群聊 29 | · 30 | 报告问题 31 | · 32 | 提供建议 33 |

34 | 35 | 36 | ## 入门 | Kicked-in-door 37 | 38 | 安装包 39 | ```bash 40 | dotnet add package Hyperai.Abstractions Hyperai.Core 41 | ``` 42 | 43 | 然后在 `Hyperai` 名字空间下寻找你想要的. 44 | 45 | ## 引用 | Reference 46 | 47 | - [Best README template](https://github.com/shaojintian/Best_README_template/blob/master/README.md) 48 | - [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet) 49 | - [Image Shields](https://shields.io) 50 | - [Choose an Open Source License](https://choosealicense.com) 51 | - [Netlify](https://www.netlify.com/) 52 | 53 | 54 | [project-path]:theGravityLab/Hyperai 55 | [contributors-shield]: https://img.shields.io/github/contributors/theGravityLab/Hyperai?style=for-the-badge 56 | [contributors-url]: https://github.com/theGravityLab/Hyperai/graphs/contributors 57 | [forks-shield]: https://img.shields.io/github/forks/theGravityLab/Hyperai?style=for-the-badge 58 | [forks-url]: https://github.com/theGravityLab/Hyperai/network/members 59 | [stars-shield]: https://img.shields.io/github/stars/theGravityLab/Hyperai?style=for-the-badge 60 | [stars-url]: https://github.com/theGravityLab/Hyperai/stargazers 61 | 62 | ## License 63 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai?ref=badge_large) -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/HyperaiShell.App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | HyperaiShell.App.Program 7 | 2021.9.1 8 | https://github.com/theGravityLab/HyperaiShell 9 | GravityLab 10 | Unicore 11 | HyperaiShell 12 | GPL-3.0-only 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Relations/Signature.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Relations 2 | { 3 | public class Signature 4 | { 5 | public Signature(string expression) 6 | { 7 | Expression = expression; 8 | } 9 | 10 | public string Expression { get; set; } 11 | public long Destination { get; set; } 12 | 13 | public bool Match(Member member) 14 | { 15 | var prefix = Expression.Substring(0, Expression.IndexOf(':')); 16 | var postfix = Expression.Substring(prefix.Length + 1); 17 | 18 | if (prefix == "*") 19 | { 20 | if (long.TryParse(postfix, out var result)) return result == member.Identity; 21 | 22 | return false; 23 | } 24 | 25 | return prefix == member.Group.Value.Identity.ToString() && (postfix == "*" || 26 | member.Group.Value.Identity.ToString() == 27 | prefix && 28 | member.Identity.ToString() == postfix); 29 | } 30 | 31 | public bool Match(Friend friend) 32 | { 33 | var prefix = Expression.Substring(0, Expression.IndexOf(':')); 34 | var postfix = Expression.Substring(prefix.Length + 1); 35 | 36 | if (prefix == "*") return false; 37 | if (prefix == "_") 38 | { 39 | if (long.TryParse(postfix, out var result)) return result == friend.Identity; 40 | 41 | return postfix == "*"; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | public static Signature FromGroup(long groupId) 48 | { 49 | return new Signature($"{groupId}:*"); 50 | } 51 | 52 | public static Signature FromMember(long groupId, long memberId) 53 | { 54 | return new Signature($"{groupId}:{memberId}"); 55 | } 56 | 57 | public static Signature FromAnyGroup(long userId) 58 | { 59 | return new Signature($"*:{userId}"); 60 | } 61 | 62 | public static Signature FromAnyGroupAnyMember() 63 | { 64 | return new Signature("*:*"); 65 | } 66 | 67 | public static Signature FromFriend(long friendId) 68 | { 69 | return new Signature($"_:{friendId}"); 70 | } 71 | 72 | public static Signature FromAnyFriend() 73 | { 74 | return new Signature("_:*"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Core/Serialization/HyperCodeFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using Hyperai.Messages; 7 | using Hyperai.Messages.ConcreteModels; 8 | using Hyperai.Messages.ConcreteModels.FileSources; 9 | 10 | namespace Hyperai.Serialization 11 | { 12 | public class HyperCodeFormatter : IMessageChainFormatter 13 | { 14 | public string Format(MessageChain chain) 15 | { 16 | return string.Join(string.Empty, 17 | chain.Where(x => x.GetType().GetCustomAttribute() != null) 18 | .Select(PlainSelector)); 19 | 20 | string PlainSelector(MessageElement comp) 21 | { 22 | return comp switch 23 | { 24 | Plain it => Escape(it.Text), 25 | _ => $"[hyper.{comp.TypeName.ToLower()}({CodeSelector(comp)})]" 26 | }; 27 | } 28 | 29 | string CodeSelector(MessageElement comp) 30 | { 31 | return comp switch 32 | { 33 | At it => it.TargetId.ToString(), 34 | AtAll it => string.Empty, 35 | Face it => it.FaceId.ToString(), 36 | ImageBase { Source: UrlSource } it => 37 | $"{it.ImageId},{((UrlSource)it.Source).Url.AbsoluteUri}", 38 | Poke it => it.Name.ToString(), 39 | Quote it => it.MessageId.ToString(), 40 | Source it => it.MessageId.ToString(), 41 | Music it => $"{it.Type},{it.MusicId}", 42 | 43 | StreamedFileBase { Source: UrlSource } it => $"{((UrlSource)it.Source).Url.AbsoluteUri}", 44 | 45 | _ => throw new NotImplementedException() 46 | }; 47 | } 48 | } 49 | 50 | public string Escape(string text) 51 | { 52 | StringBuilder sb = new(); 53 | var addedLength = 0; 54 | 55 | var matches = HyperCodeParser.HyperRegex.Matches(text); 56 | foreach (Match match in matches) 57 | { 58 | var count = HyperCodeParser.CountBefore(match.Index, text); 59 | sb.Append(text[addedLength..match.Index]); 60 | sb.Append('\\', count * 2 + 1); 61 | sb.Append(match.Value); 62 | addedLength = match.Index + match.Length; 63 | } 64 | 65 | if (addedLength < text.Length) sb.Append(text[addedLength..]); 66 | return sb.ToString(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Middlewares/LoggingMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Events; 2 | using Hyperai.Middlewares; 3 | using Hyperai.Services; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace HyperaiShell.App.Middlewares 7 | { 8 | public class LoggingMiddleware : IMiddleware 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public LoggingMiddleware(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public bool Run(IApiClient client, GenericEventArgs eventArgs) 18 | { 19 | switch (eventArgs) 20 | { 21 | case GroupMessageEventArgs args: 22 | _logger.LogInformation("{ArgsType} received {Group}-{User}:\n{Message}", args, args.Group, 23 | args.User, args.Message); 24 | break; 25 | 26 | case FriendMessageEventArgs args: 27 | _logger.LogInformation("{ArgsType} received {User}:\n{Message}", args, args.User, args.Message); 28 | break; 29 | 30 | case GroupMemberMutedEventArgs args: 31 | _logger.LogInformation("{ArgsType} received {Group}:\n{User} by {Operator} for {Duration}", args, 32 | args.Group, args.Whom, args.Operator, args.Duration); 33 | break; 34 | 35 | case GroupJoinedEventArgs args: 36 | _logger.LogInformation("{ArgsType} received {Group}:\n{User} by {Operator}", args, args.Group, 37 | args.Who, args.Operator); 38 | break; 39 | 40 | case GroupMemberUnmutedEventArgs args: 41 | _logger.LogInformation("{ArgsType} received {Group}:\n{User} by {Operator}", args, args.Group, 42 | args.Whom, args.Operator); 43 | break; 44 | 45 | case FriendRecallEventArgs args: 46 | _logger.LogInformation("{ArgsType} received {Friend}\n{Message}", args, args.WhoseMessage, 47 | args.MessageId); 48 | break; 49 | 50 | case GroupRecallEventArgs args: 51 | _logger.LogInformation("{ArgsType} received {Group}-{User} by {Operator}\n{Message}", args, 52 | args.Group, args.WhoseMessage, args.Operator, args.MessageId); 53 | break; 54 | 55 | default: 56 | _logger.LogInformation("{ArgsType} received at {Time}", eventArgs, eventArgs.Time); 57 | break; 58 | } 59 | 60 | return true; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions.Tests/Messages/PlainTests.cs: -------------------------------------------------------------------------------- 1 | using Hyperai.Messages.ConcreteModels; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace Hyperai.Abstractions.Tests.Messages 5 | { 6 | [TestClass] 7 | public class PlainTests 8 | { 9 | [TestMethod] 10 | public void Equals_WithNull_ReturnsFalse() 11 | { 12 | // Arrange 13 | var p1 = new Plain("a dog"); 14 | // Act 15 | var assert = p1.Equals(null); 16 | // Assert 17 | Assert.IsFalse(assert); 18 | } 19 | 20 | [TestMethod] 21 | public void Equals_WithOther_ReturnsFalse() 22 | { 23 | // Arrange 24 | var p1 = new Plain("a dog"); 25 | var p2 = new Plain("a cat"); 26 | // Act 27 | var assert = p1.Equals(p2); 28 | // Assert 29 | Assert.IsFalse(assert); 30 | } 31 | 32 | [TestMethod] 33 | public void Equals_WithSame_ReturnsTrue() 34 | { 35 | // Arrange 36 | var p1 = new Plain("a dog"); 37 | var p2 = new Plain("a dog"); 38 | // Act 39 | var assert = p1.Equals(p2); 40 | // Assert 41 | Assert.IsTrue(assert); 42 | } 43 | 44 | [TestMethod] 45 | public void EqualsOperator_AnyAndNull_ReturnsFalse() 46 | { 47 | // Arrange 48 | var p1 = new Plain("a dog"); 49 | // Act 50 | var assert = p1 == null; 51 | // Assert 52 | Assert.IsFalse(assert); 53 | } 54 | 55 | [TestMethod] 56 | public void EqualsOperator_NullAndAny_ReturnsFalse() 57 | { 58 | // Arrange 59 | var p1 = new Plain("a dog"); 60 | // Act 61 | var assert = null == p1; 62 | // Assert 63 | Assert.IsFalse(assert); 64 | } 65 | 66 | [TestMethod] 67 | public void EqualsOperator_OneAndOne_ReturnsTrue() 68 | { 69 | // Arrange 70 | var p1 = new Plain("a dog"); 71 | var p2 = new Plain("a dog"); 72 | // Act 73 | var assert = p1 == p2; 74 | // Assert 75 | Assert.IsTrue(assert); 76 | } 77 | 78 | [TestMethod] 79 | public void EqualsOperator_OneAndOther_ReturnsFalse() 80 | { 81 | // Arrange 82 | var p1 = new Plain("a dog"); 83 | var p2 = new Plain("a cat"); 84 | // Act 85 | var assert = p1 == p2; 86 | // Assert 87 | Assert.IsFalse(assert); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/AttachmentExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | using HyperaiShell.Foundation.Services; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace HyperaiShell.Foundation.ModelExtensions 7 | { 8 | public static class AttachmentExtensions 9 | { 10 | private static readonly IAttachmentService service; 11 | 12 | static AttachmentExtensions() 13 | { 14 | service = Shared.Host.Services.GetRequiredService(); 15 | } 16 | 17 | /// 18 | /// 将一个对象附加到 上, 有则替换 19 | /// 20 | /// 类型 21 | /// 目标关系模型 22 | /// 实例 23 | public static void Attach(this RelationModel model, T ins) 24 | { 25 | service.Attach(ins, model); 26 | } 27 | 28 | /// 29 | /// 将 类型的对象从 上移除 30 | /// 31 | /// 类型 32 | /// 目标关系模型 33 | public static void Detach(this RelationModel model) 34 | { 35 | service.Detach(model); 36 | } 37 | 38 | /// 39 | /// 获取附加在 上的 对象 40 | /// 41 | /// 类型 42 | /// 目标关系模型 43 | /// 当对象不存在时返回该生成器创建的对象(不会附加 44 | /// 45 | public static T Retrieve(this RelationModel model, Func generator = null) 46 | { 47 | generator ??= () => default; 48 | var t = service.Retrieve(model); 49 | if (t != null) return t; 50 | 51 | t = generator(); 52 | if (t == null) return default; 53 | 54 | service.Attach(t, model); 55 | return t; 56 | } 57 | 58 | /// 59 | /// 获取 类型对象并使用 using 语句来自动提交更新 60 | /// 61 | /// 类型 62 | /// 目标关系模型 63 | /// 得到的实例 64 | /// 当对象不存在时返回该生成器创建的对象(会附加 65 | /// 66 | public static ForAttachmentUpdateScope For(this RelationModel model, out T ins, Func generator = null) 67 | { 68 | return service.For(model, out ins, generator); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/MessageChainExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Hyperai.Messages; 4 | using Hyperai.Messages.ConcreteModels; 5 | using Hyperai.Services; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace HyperaiShell.Foundation.ModelExtensions 9 | { 10 | public static class MessageChainExtensions 11 | { 12 | private static readonly IMessageChainFormatter _formatter; 13 | private static readonly IMessageChainParser _parser; 14 | private static readonly IApiClient _client; 15 | 16 | static MessageChainExtensions() 17 | { 18 | _formatter = Shared.Host.Services.GetRequiredService(); 19 | _parser = Shared.Host.Services.GetRequiredService(); 20 | _client = Shared.Host.Services.GetRequiredService(); 21 | } 22 | 23 | /// 24 | /// 使用默认 格式化消息链 25 | /// 26 | /// 消息链 27 | /// 消息文本 28 | public static string Flatten(this MessageChain message) 29 | { 30 | return _formatter.Format(message); 31 | } 32 | 33 | /// 34 | /// 使用默认 解析消息文本 35 | /// 36 | /// 消息文本 37 | /// 消息链 38 | public static MessageChain MakeMessageChain(this string code) 39 | { 40 | return _parser.Parse(code); 41 | } 42 | 43 | /// 44 | /// 撤回该消息, 如果该消息不含 则引发异常 45 | /// 46 | /// 包含 的消息链 47 | /// 48 | /// 49 | /// 50 | public static async Task RevokeAsync(this MessageChain chain) 51 | { 52 | await _client.RevokeMessageAsync(((Source)chain.First(x => x is Source)).MessageId); 53 | } 54 | 55 | /// 56 | /// 当其包含 Quote 时, 获取目标 57 | /// 58 | /// 包含 的消息链 59 | /// 源消息链 60 | public static async Task OfMessageRepliedByAsync(this MessageChain chain) 61 | { 62 | var quote = chain.First(x => x is Quote) as Quote; 63 | var id = MessageChain.Construct(new Source(quote!.MessageId)); 64 | var src = await _client.RequestAsync(id); 65 | return src; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/RelationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | using Hyperai.Services; 4 | using Hyperai.Units; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace HyperaiShell.Foundation.ModelExtensions 8 | { 9 | public static class RelationExtensions 10 | { 11 | private static readonly IApiClient client; 12 | private static readonly IUnitService unit; 13 | 14 | static RelationExtensions() 15 | { 16 | client = Shared.Host.Services.GetRequiredService(); 17 | unit = Shared.Host.Services.GetRequiredService(); 18 | } 19 | 20 | /// 21 | /// 在确定群内有该成员的前提下用其 id 获取成员其他信息 22 | /// 23 | /// 目标群 24 | /// TA滴 id 25 | /// 完整的群员信息 26 | public static Member GetMember(this Group group, long identity) 27 | { 28 | return client.RequestAsync(new Member { Identity = identity, Group = new Lazy(group) }).GetAwaiter() 29 | .GetResult(); 30 | } 31 | 32 | 33 | /// 34 | /// 监听该 的下一条消息 35 | /// 36 | /// 目标好友 37 | /// 当消息抵达时的操作 38 | /// 过期时间(ms) 39 | public static void Await(this Friend friend, ActionDelegate action, int msToExpire = 30000) 40 | { 41 | unit.WaitOne(Signature.FromFriend(friend.Identity), action, TimeSpan.FromMilliseconds(msToExpire)); 42 | } 43 | 44 | /// 45 | /// 监听该 的下一条消息 46 | /// 47 | /// 目标群 48 | /// 当消息抵达时的操作 49 | /// 过期时间(ms) 50 | public static void Await(this Group group, ActionDelegate action, int msToExpire = 30000) 51 | { 52 | unit.WaitOne(Signature.FromGroup(group.Identity), action, TimeSpan.FromMilliseconds(msToExpire)); 53 | } 54 | 55 | /// 56 | /// 监听该 的下一条消息 57 | /// 58 | /// 目标成员 59 | /// 当消息抵达时的操作 60 | /// 过期时间(ms) 61 | public static void Await(this Member member, ActionDelegate action, int msToExpire = 30000) 62 | { 63 | unit.WaitOne(Signature.FromMember(member.Group.Value.Identity, member.Identity), action, 64 | TimeSpan.FromMilliseconds(msToExpire)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/AuthorizationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Hyperai.Relations; 5 | using HyperaiShell.Foundation.Services; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace HyperaiShell.Foundation.ModelExtensions 9 | { 10 | public static class AuthorizationExtensions 11 | { 12 | private static readonly IAuthorizationService service; 13 | 14 | static AuthorizationExtensions() 15 | { 16 | service = Shared.Host.Services.GetRequiredService(); 17 | } 18 | 19 | /// 20 | /// 授予限次权限 21 | /// 22 | /// 宿主 23 | /// 权限名 24 | /// 次数 25 | public static void GrantLimited(this RelationModel model, string name, int count) 26 | { 27 | service.PutLimited(model, name, count); 28 | } 29 | 30 | /// 31 | /// 授予限时权限 32 | /// 33 | /// 宿主 34 | /// 权限名 35 | /// 期限 36 | public static void GrantExpiry(this RelationModel model, string name, DateTime expiration) 37 | { 38 | service.PutExpiry(model, name, expiration); 39 | } 40 | 41 | /// 42 | /// 授予宿主特定权限 43 | /// 44 | /// 宿主 45 | /// 权限名 46 | public static void Grant(this RelationModel model, string name) 47 | { 48 | service.PutNormal(model, name); 49 | } 50 | 51 | /// 52 | /// 检查宿主是否具有某权限 53 | /// 54 | /// 宿主 55 | /// 权限名 56 | /// 57 | public static bool CheckPermission(this RelationModel model, string name) 58 | { 59 | return service.CheckTicket(model, name); 60 | } 61 | 62 | /// 63 | /// 撤销权限 64 | /// 65 | /// 宿主 66 | /// 权限名 67 | public static void RevokePermission(this RelationModel model, string name) 68 | { 69 | service.RemoveTicket(model, name); 70 | } 71 | 72 | /// 73 | /// 获取全部由 Ticket 给予的权限 74 | /// 75 | /// 宿主 76 | /// 权限名单 77 | public static IEnumerable GetPermissions(this RelationModel model) 78 | { 79 | return service.GetTickets(model).Select(x => x.Name); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Hyperai.Units/Hyperai.Units/UnitMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Hyperai.Events; 6 | using Hyperai.Middlewares; 7 | using Hyperai.Relations; 8 | using Hyperai.Services; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Hyperai.Units 12 | { 13 | public class UnitMiddleware : IMiddleware 14 | { 15 | private readonly ILogger _logger; 16 | private readonly IUnitService _service; 17 | 18 | private readonly Stopwatch stopwatch = new(); 19 | 20 | public UnitMiddleware(IUnitService service, ILogger logger) 21 | { 22 | _service = service; 23 | _logger = logger; 24 | } 25 | 26 | public bool Run(IApiClient sender, GenericEventArgs args) 27 | { 28 | if (args is MessageEventArgs messageArgs) 29 | { 30 | // 该消息无法被序列化 31 | // 没必要继续下去 32 | if (messageArgs.Message.Any(x => x.GetType().GetCustomAttribute() == null)) 33 | return true; 34 | } 35 | else 36 | { 37 | // 连消息事件都不是, 就更没必要了 38 | return true; 39 | } 40 | 41 | 42 | stopwatch.Start(); 43 | var context = new MessageContext 44 | { 45 | SentAt = args.Time, 46 | Client = sender, 47 | Me = sender.RequestAsync(null).GetAwaiter().GetResult(), 48 | Group = null 49 | }; 50 | switch (args) 51 | { 52 | case GroupMessageEventArgs gm: 53 | context.Group = gm.Group; 54 | context.User = gm.User; 55 | context.Message = gm.Message; 56 | context.Type = MessageEventType.Group; 57 | break; 58 | 59 | case FriendMessageEventArgs fm: 60 | context.User = fm.User; 61 | context.Message = fm.Message; 62 | context.Type = MessageEventType.Friend; 63 | break; 64 | 65 | default: 66 | return true; 67 | } 68 | 69 | stopwatch.Stop(); 70 | var prepare = stopwatch.ElapsedMilliseconds; 71 | stopwatch.Restart(); 72 | _service.Handle(context); 73 | stopwatch.Stop(); 74 | if (stopwatch.ElapsedMilliseconds > 1000) 75 | _logger.LogWarning( 76 | "Handling for Unit Actions took {Total} milliseconds(preparing = {Prepare}): {Message}", 77 | stopwatch.ElapsedMilliseconds + prepare, prepare, context.Message); 78 | else 79 | _logger.LogDebug( 80 | "Handling for Unit Actions took {Total} milliseconds(preparing = {Prepare}): {Message}", 81 | stopwatch.ElapsedMilliseconds + prepare, prepare, context.Message); 82 | stopwatch.Reset(); 83 | return true; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Services/AttachmentService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hyperai.Relations; 3 | using HyperaiShell.Foundation.Data; 4 | using HyperaiShell.Foundation.Services; 5 | using Microsoft.Extensions.Logging; 6 | using Sentry; 7 | using Attachment = HyperaiShell.App.Models.Attachment; 8 | 9 | namespace HyperaiShell.App.Services 10 | { 11 | public class AttachmentService : IAttachmentService 12 | { 13 | private readonly IHub _hub; 14 | private readonly ILogger _logger; 15 | private readonly IRepository _repository; 16 | 17 | public AttachmentService(IRepository repository, ILogger logger, IHub hub) 18 | { 19 | _repository = repository; 20 | _logger = logger; 21 | _hub = hub; 22 | } 23 | 24 | public void Attach(T ins, RelationModel toWhom) 25 | { 26 | var transaction = _hub.StartTransaction($"{nameof(HyperaiShell)}-{nameof(AttachmentService)}", 27 | nameof(Attach) 28 | , typeof(T).Name); 29 | var typeName = typeof(T).FullName; 30 | var first = _repository.Query() 31 | .Where(x => x.Target == toWhom.Identifier && x.TypeName == typeName).FirstOrDefault(); 32 | if (first != null) 33 | { 34 | first.Object = ins; 35 | _repository.Update(first); 36 | } 37 | else 38 | { 39 | first = new Attachment 40 | { 41 | Target = toWhom.Identifier, 42 | TypeName = typeName, 43 | Object = ins 44 | }; 45 | _repository.Store(first); 46 | } 47 | 48 | transaction.Finish(); 49 | } 50 | 51 | public void Detach(RelationModel toWhom) 52 | { 53 | var transaction = _hub.StartTransaction($"{nameof(HyperaiShell)}-{nameof(AttachmentService)}", 54 | nameof(Detach) 55 | , typeof(T).Name); 56 | var typeName = typeof(T).FullName; 57 | var first = _repository.Query() 58 | .Where(x => x.Target == toWhom.Identifier && x.TypeName == typeName).FirstOrDefault(); 59 | if (first != null) _repository.Delete(first.Id); 60 | transaction.Finish(); 61 | } 62 | 63 | public T Retrieve(RelationModel fromWhom) 64 | { 65 | var transaction = _hub.StartTransaction($"{nameof(HyperaiShell)}-{nameof(AttachmentService)}", 66 | nameof(Retrieve), typeof(T).Name); 67 | var typeName = typeof(T).FullName; 68 | var ins = (T)_repository.Query() 69 | .Where(x => x.Target == fromWhom.Identifier && x.TypeName == typeName).FirstOrDefault()?.Object; 70 | transaction.Finish(); 71 | return ins; 72 | } 73 | 74 | public ForAttachmentUpdateScope For(RelationModel model, out T ins, Func generator = null) 75 | { 76 | var t = Retrieve(model) ?? (generator ?? (() => default))(); 77 | var scope = new ForAttachmentUpdateScope(this, t, model); 78 | ins = t; 79 | return scope; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Services/ApiClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Hyperai.Events; 4 | using Hyperai.Messages; 5 | using Hyperai.Receipts; 6 | using Hyperai.Relations; 7 | 8 | namespace Hyperai.Services 9 | { 10 | public static class ApiClientExtensions 11 | { 12 | public static IApiClient On(this IApiClient client, Action action) where T : GenericEventArgs 13 | { 14 | client.On(new DefaultEventHandler(client, action)); 15 | return client; 16 | } 17 | 18 | public static async Task SendFriendMessageAsync(this IApiClient client, Friend friend, 19 | MessageChain message) 20 | { 21 | var args = new FriendMessageEventArgs 22 | { 23 | Message = message, 24 | User = friend 25 | }; 26 | return (MessageReceipt)await client.SendAsync(args); 27 | } 28 | 29 | public static async Task SendGroupMessageAsync(this IApiClient client, Group group, 30 | MessageChain message) 31 | { 32 | var args = new GroupMessageEventArgs 33 | { 34 | Message = message, 35 | Group = group 36 | }; 37 | return (MessageReceipt)await client.SendAsync(args); 38 | } 39 | 40 | public static async Task RevokeMessageAsync(this IApiClient client, long messageId) 41 | { 42 | var args = new RecallEventArgs 43 | { 44 | MessageId = messageId 45 | }; 46 | await client.SendAsync(args); 47 | } 48 | 49 | public static async Task KickAsync(this IApiClient client, Group group, Member member) 50 | { 51 | var args = new GroupLeftEventArgs 52 | { 53 | Group = group, 54 | IsKicked = true, 55 | Who = member 56 | }; 57 | await client.SendAsync(args); 58 | } 59 | 60 | public static async Task QuitAsync(this IApiClient client, Group group) 61 | { 62 | var me = await client.RequestAsync(new Self()); 63 | var args = new GroupLeftEventArgs 64 | { 65 | Group = group, 66 | IsKicked = false, 67 | Who = new Member 68 | { 69 | Group = new Lazy(group), 70 | Identity = me.Identity 71 | } 72 | }; 73 | await client.SendAsync(args); 74 | } 75 | 76 | public static async Task MuteAsync(this IApiClient client, Group group, Member member, TimeSpan duration) 77 | { 78 | var args = new GroupMemberMutedEventArgs 79 | { 80 | Group = group, 81 | Whom = member, 82 | Duration = duration 83 | }; 84 | await client.SendAsync(args); 85 | } 86 | 87 | public static async Task MuteAllAsync(this IApiClient client, Group group) 88 | { 89 | var args = new GroupAllMutedEventArgs 90 | { 91 | Group = group, 92 | IsEnded = false 93 | }; 94 | await client.SendAsync(args); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.Foundation/ModelExtensions/ClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Hyperai.Messages; 3 | using Hyperai.Messages.ConcreteModels; 4 | using Hyperai.Relations; 5 | using Hyperai.Services; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace HyperaiShell.Foundation.ModelExtensions 10 | { 11 | public static class ClientExtensions 12 | { 13 | private static readonly IApiClient _client; 14 | private static readonly ILogger _logger; 15 | 16 | static ClientExtensions() 17 | { 18 | _client = Shared.Host.Services.GetRequiredService(); 19 | _logger = Shared.Host.Services.GetRequiredService() 20 | .CreateLogger(typeof(ClientExtensions).AssemblyQualifiedName); 21 | } 22 | 23 | /// 24 | /// 使用默认 发送 25 | /// 26 | /// 好友 27 | /// 消息链 28 | public static async Task SendAsync(this Friend friend, MessageChain message) 29 | { 30 | _logger.LogInformation("{Client}({Type}) < {Friend}:\n{Message}", _client.GetType().Name, nameof(Friend), 31 | friend.Identifier, 32 | message); 33 | await _client.SendFriendMessageAsync(friend, message); 34 | } 35 | 36 | /// 37 | /// 使用默认 发送 构成的 38 | /// 39 | /// 好友 40 | /// 消息串 41 | public static async Task SendPlainAsync(this Friend friend, string plain) 42 | { 43 | _logger.LogInformation("{Client}({Type}) < {Friend}:\n{Message}", _client.GetType().Name, nameof(Friend), 44 | friend.Identifier, 45 | plain); 46 | await _client.SendFriendMessageAsync(friend, MessageChain.Construct(new Plain(plain))); 47 | } 48 | 49 | /// 50 | /// 使用默认 发送 51 | /// 52 | /// 群 53 | /// 消息链 54 | public static async Task SendAsync(this Group group, MessageChain message) 55 | { 56 | _logger.LogInformation("{Client}({Type}) < {Group}:\n{Message}", _client.GetType().Name, nameof(Group), 57 | group.Identifier, 58 | message); 59 | await _client.SendGroupMessageAsync(group, message); 60 | } 61 | 62 | /// 63 | /// 使用默认 发送 构成的 64 | /// 65 | /// 群 66 | /// 消息串 67 | public static async Task SendPlainAsync(this Group group, string plain) 68 | { 69 | _logger.LogInformation("{Client}({Type}) < {Group}:\n{Message}", _client.GetType().Name, nameof(Group), 70 | group.Identifier, 71 | plain); 72 | await _client.SendGroupMessageAsync(group, MessageChain.Construct(new Plain(plain))); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Bootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Ac682.Extensions.Logging.Console; 3 | using Hangfire; 4 | using Hangfire.Storage.SQLite; 5 | using Hyperai; 6 | using Hyperai.Messages; 7 | using Hyperai.Serialization; 8 | using Hyperai.Units; 9 | using HyperaiShell.App.Data; 10 | using HyperaiShell.App.Hangfire.Logging; 11 | using HyperaiShell.App.Logging.ConsoleFormatters; 12 | using HyperaiShell.App.Middlewares; 13 | using HyperaiShell.App.Plugins; 14 | using HyperaiShell.App.Services; 15 | using HyperaiShell.Foundation.Data; 16 | using HyperaiShell.Foundation.Plugins; 17 | using LiteDB; 18 | using Microsoft.Extensions.Configuration; 19 | using Microsoft.Extensions.DependencyInjection; 20 | using Microsoft.Extensions.Logging; 21 | using Newtonsoft.Json; 22 | 23 | namespace HyperaiShell.App 24 | { 25 | public class Bootstrapper 26 | { 27 | public Bootstrapper(IConfiguration configuration) 28 | { 29 | Configuration = configuration; 30 | } 31 | 32 | public IConfiguration Configuration { get; } 33 | 34 | public void ConfigureServices(IServiceCollection services) 35 | { 36 | var dbName = "data/internal.litedb.db"; 37 | Directory.CreateDirectory(Path.GetDirectoryName(dbName)!); 38 | var database = new LiteDatabase(dbName); 39 | var repository = new LiteDbRepository(database); 40 | 41 | services.AddSingleton(Configuration); 42 | services.AddSingleton(repository); 43 | 44 | services.AddLogging(builder => 45 | { 46 | builder 47 | .AddConfiguration(Configuration) 48 | .AddDebug() 49 | .AddFile("logs/app.log") 50 | .AddConsole(c => c 51 | .SetMinimalLevel(LogLevel.Debug) 52 | .AddBuiltinFormatters() 53 | .AddFormatter() 54 | .AddFormatter() 55 | .AddFormatter() 56 | ); 57 | 58 | builder.AddSentry(); 59 | }); 60 | 61 | var settings = new JsonSerializerSettings 62 | { 63 | ReferenceLoopHandling = ReferenceLoopHandling.Serialize, 64 | PreserveReferencesHandling = PreserveReferencesHandling.Objects, 65 | TypeNameHandling = TypeNameHandling.All 66 | }; 67 | 68 | services.AddScoped(typeof(IPluginConfiguration<>), typeof(PluginConfiguration<>)) 69 | .AddScoped(typeof(IPluginRepository<>), typeof(PluginRepository<>)) 70 | .AddScoped() 71 | .AddScoped() 72 | .AddHangfire(configure => configure 73 | .UseLogProvider(new HangfireLogProvider()) 74 | .UseSQLiteStorage("data/hangfire.sqlite.db") 75 | .UseSerializerSettings(settings)) 76 | .AddHttpClient() 77 | .AddHangfireServer() 78 | .AddHyperaiServer(options => options 79 | .UseLogging() 80 | .UseBlacklist() 81 | .UseTranslator() 82 | .UseBots() 83 | .UseUnits()) 84 | .AddDistributedMemoryCache() 85 | .AddBots() 86 | .AddClients(Configuration) 87 | .AddUnits() 88 | .AddAttachments() 89 | .AddAuthorizationService() 90 | .AddBlacklist(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/HyperaiShell/README.md: -------------------------------------------------------------------------------- 1 | # HyperaiShell 2 | 3 | 开箱即用的 Hyperai Application. ProjHyperai 的一部分. 4 | 5 | 6 | 7 | [![Contributors][contributors-shield]][contributors-url] 8 | [![Forks][forks-shield]][forks-url] 9 | [![Stargazers][stars-shield]][stars-url] 10 | 11 | 12 | 13 |

14 | 15 | Logo 16 | 17 |

18 | 19 | 20 |

ProjHyperai

21 |

22 | QQ/TG 机器人开发在这入门 23 |
24 | 本项目的文档 » 25 |
26 |
27 | 加入群聊 28 | · 29 | 报告问题 30 | · 31 | 提供建议 32 |

33 | 34 | 35 | ## 部署机器人 | Deploy 36 | 37 | ### 视窗系统 | Windows 38 | 39 | 下载已经编译好的 [Release](https://github.com/theGravityLab/HyperaiShell/releases). 40 | 41 | ### 林纽克斯 | Linux 42 | 43 | 参考[手动编译](https://projhyperai.dowob.vip/guide/2.1.deploy/#%E6%89%8B%E5%8A%A8%E7%BC%96%E8%AF%91). 44 | 45 | ### 配置 | Configuration 46 | 47 | HyperaiShell 仅能处理消息, 消息接收发送需要[**适配器**](https://projhyperai.dowob.vip/guide/5.1.knowledge/#%E9%80%82%E9%85%8D%E5%99%A8-iapiclient). 48 | 49 | 50 | *为保证及时更新, 包括适配器和如何配置等内容请在文档中查看.* 51 | 52 | ### 目前支持的适配器列表 | Supported Adapters 53 | 54 | |平台|媒介|项目| 55 | |--|--|--| 56 | |OICQ|[mirai-api-http](https://github.com/project-mirai/mirai-api-http)|[Ac682.Hyperai.Clients.Mirai](https://github.com/ac682/Ac682.Hyperai.Clients.Mirai)| 57 | |OICQ|[go-cqhttp](https://github.com/Mrs4s/go-cqhttp)|[Ac682.Hyperai.Clients.CQHTTP](https://github.com/ac682/Ac682.Hyperai.Clients.CQHTTP)| 58 | |OICQ|[CQHTTP](https://github.com/richardchien/coolq-http-api)|🈚(你去PR就有了)| 59 | |Telegram|[TG-API](https://core.telegram.org/api)|🈚(你去PR就有了)| 60 | 61 | ## 插件开发 | Plugin Development 62 | 63 | 首先安装 nuget 包 64 | ```bash 65 | dotnet add package HyperaiShell.Foundation 66 | ``` 67 | 68 | 然后阅读[开发文档>>](https://projhyperai.dowob.vip/guide/5.0.about/) 69 | 70 | ## 截图 | Screenshots 71 | 72 | ![screenshot](.github/images/screenshot.png) 73 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperaiShell.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperaiShell?ref=badge_shield) 74 | 75 | ## 引用 | Reference 76 | 77 | - [Best README template](https://github.com/shaojintian/Best_README_template/blob/master/README.md) 78 | - [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet) 79 | - [Image Shields](https://shields.io) 80 | - [Choose an Open Source License](https://choosealicense.com) 81 | - [Netlify](https://www.netlify.com/) 82 | 83 | 84 | [project-path]:theGravityLab/HyperaiShell 85 | [contributors-shield]: https://img.shields.io/github/contributors/theGravityLab/HyperaiShell?style=for-the-badge 86 | [contributors-url]: https://github.com/theGravityLab/HyperaiShell/graphs/contributors 87 | [forks-shield]: https://img.shields.io/github/forks/theGravityLab/HyperaiShell?style=for-the-badge 88 | [forks-url]: https://github.com/theGravityLab/HyperaiShell/network/members 89 | [stars-shield]: https://img.shields.io/github/stars/theGravityLab/HyperaiShell?style=for-the-badge 90 | [stars-url]: https://github.com/theGravityLab/HyperaiShell/stargazers 91 | 92 | ## License 93 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperaiShell.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperaiShell?ref=badge_large) -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Build Schema", 4 | "$ref": "#/definitions/build", 5 | "definitions": { 6 | "build": { 7 | "type": "object", 8 | "properties": { 9 | "ApiKey": { 10 | "type": "string" 11 | }, 12 | "Configuration": { 13 | "type": "string", 14 | "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", 15 | "enum": [ 16 | "Debug", 17 | "Release" 18 | ] 19 | }, 20 | "Continue": { 21 | "type": "boolean", 22 | "description": "Indicates to continue a previously failed build attempt" 23 | }, 24 | "Help": { 25 | "type": "boolean", 26 | "description": "Shows the help text for this build assembly" 27 | }, 28 | "Host": { 29 | "type": "string", 30 | "description": "Host for execution. Default is 'automatic'", 31 | "enum": [ 32 | "AppVeyor", 33 | "AzurePipelines", 34 | "Bamboo", 35 | "Bitrise", 36 | "GitHubActions", 37 | "GitLab", 38 | "Jenkins", 39 | "Rider", 40 | "SpaceAutomation", 41 | "TeamCity", 42 | "Terminal", 43 | "TravisCI", 44 | "VisualStudio", 45 | "VSCode" 46 | ] 47 | }, 48 | "NoLogo": { 49 | "type": "boolean", 50 | "description": "Disables displaying the NUKE logo" 51 | }, 52 | "Partition": { 53 | "type": "string", 54 | "description": "Partition to use on CI" 55 | }, 56 | "Plan": { 57 | "type": "boolean", 58 | "description": "Shows the execution plan (HTML)" 59 | }, 60 | "Profile": { 61 | "type": "array", 62 | "description": "Defines the profiles to load", 63 | "items": { 64 | "type": "string" 65 | } 66 | }, 67 | "Root": { 68 | "type": "string", 69 | "description": "Root directory during build execution" 70 | }, 71 | "Skip": { 72 | "type": "array", 73 | "description": "List of targets to be skipped. Empty list skips all dependencies", 74 | "items": { 75 | "type": "string", 76 | "enum": [ 77 | "Clean", 78 | "Compile", 79 | "GeneratePackages", 80 | "PublishApplication", 81 | "PushPackages", 82 | "Restore", 83 | "Test" 84 | ] 85 | } 86 | }, 87 | "Solution": { 88 | "type": "string", 89 | "description": "Path to a solution file that is automatically loaded" 90 | }, 91 | "Target": { 92 | "type": "array", 93 | "description": "List of targets to be invoked. Default is '{default_target}'", 94 | "items": { 95 | "type": "string", 96 | "enum": [ 97 | "Clean", 98 | "Compile", 99 | "GeneratePackages", 100 | "PublishApplication", 101 | "PushPackages", 102 | "Restore", 103 | "Test" 104 | ] 105 | } 106 | }, 107 | "Verbosity": { 108 | "type": "string", 109 | "description": "Logging verbosity during build execution. Default is 'Normal'", 110 | "enum": [ 111 | "Minimal", 112 | "Normal", 113 | "Quiet", 114 | "Verbose" 115 | ] 116 | } 117 | } 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/Hyperai.Units/README.md: -------------------------------------------------------------------------------- 1 | # Hyperai.Units 2 | 3 | 为 Hyperai Application 提供 Units 服务, 将处理对象细化到消息. 4 | 5 | 6 | 7 | [![Contributors][contributors-shield]][contributors-url] 8 | [![Forks][forks-shield]][forks-url] 9 | [![Stargazers][stars-shield]][stars-url] 10 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.Units.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.Units?ref=badge_shield) 11 | 12 | 13 | 14 |

15 | 16 | Logo 17 | 18 |

19 | 20 | 21 |

ProjHyperai

22 |

23 | QQ/TG 机器人开发在这入门 24 |
25 | 本项目的文档 » 26 |
27 |
28 | 加入群聊 29 | · 30 | 报告问题 31 | · 32 | 提供建议 33 |

34 | 35 | 36 | ## 注册服务 | Registering Units service 37 | 38 | 安装包 39 | ```bash 40 | dotnet add package Hyperai.Units 41 | ``` 42 | 43 | 在一个已有的 `IHyperaiApplicationBuilder` 对象中添加服务 44 | ```csharp 45 | var builder = MagicalBox.Take(); 46 | // => new HyperaiApplicationBuilder() 47 | 48 | builder.Services.AddUnits(); 49 | 50 | var app = builder.Build(); 51 | 52 | var service = app.Provider.GetRequiredService(); 53 | service.SearchForUnits(); 54 | 55 | app.Run(); 56 | ``` 57 | 58 | ## 编写一个 Unit | Make a class a Unit 59 | 60 | 在任意地方创建一个类, 名字任意 61 | ```csharp 62 | public class Asshole 63 | { 64 | [Receive(MessageEventType.Group)] 65 | [Extract("!fuck {who}")] 66 | [CheckTicket("ability.to.fuck")] 67 | public async Task Fuck(Member sender, Group group, long who) 68 | { 69 | FuckRecord record = null; 70 | using(sender.For(() => new FuckRecord(0), out record)) 71 | { 72 | await group.SendAsync($"[hyper.at({sender.Identity})] fucked [hyper.at({who})] the {++record.Count}th time.").MakeMessageChain()); 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | #### 解析参数 | Parsing Arguments 79 | 80 | 如你所见 `ExtractAttribute` 可以在消息中解析出特定文本, UnitService 会把该文本转换为所需要的值类型或 `MessageChain`. 81 | 82 | #### 权限标注 | That's not my job, sir. It's yours. 83 | 84 | 你看到了 `CheckTicketAttribute`, 但是这并不是本项目提供的功能. 但你可以使用 `FilterByAttribute` 来实现它. 85 | 86 | #### 方法注入 | Method Injection 87 | 88 | 是的, 所需的参数用形参来注入. 89 | 90 | 形参中可以有 `UnitBase.Context` 中的成员(使用类型来匹配), 也可以是 `ExtractAttribute` 中大括号内的参数(使用名字来识别). 91 | 92 | ## 引用 | Reference 93 | 94 | - [Best README template](https://github.com/shaojintian/Best_README_template/blob/master/README.md) 95 | - [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet) 96 | - [Image Shields](https://shields.io) 97 | - [Choose an Open Source License](https://choosealicense.com) 98 | - [Netlify](https://www.netlify.com/) 99 | 100 | 101 | [project-path]:theGravityLab/Hyperai.Units 102 | [contributors-shield]: https://img.shields.io/github/contributors/theGravityLab/Hyperai.Units?style=for-the-badge 103 | [contributors-url]: https://github.com/theGravityLab/Hyperai.Units/graphs/contributors 104 | [forks-shield]: https://img.shields.io/github/forks/theGravityLab/Hyperai.Units?style=for-the-badge 105 | [forks-url]: https://github.com/theGravityLab/Hyperai.Units/network/members 106 | [stars-shield]: https://img.shields.io/github/stars/theGravityLab/Hyperai.Units?style=for-the-badge 107 | [stars-url]: https://github.com/theGravityLab/Hyperai.Units/stargazers 108 | 109 | ## License 110 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.Units.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FtheGravityLab%2FHyperai.Units?ref=badge_large) -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Services/BotService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Hyperai.Events; 5 | using Hyperai.Relations; 6 | using Hyperai.Services; 7 | using HyperaiShell.App.Bots; 8 | using HyperaiShell.Foundation.Bots; 9 | using HyperaiShell.Foundation.Services; 10 | using Microsoft.Extensions.Logging; 11 | using Sentry; 12 | 13 | namespace HyperaiShell.App.Services 14 | { 15 | public class BotService : IBotService 16 | { 17 | private readonly IApiClient _client; 18 | private readonly IHub _hub; 19 | private readonly ILogger _logger; 20 | private readonly IServiceProvider _provider; 21 | private BotCollection bots; 22 | 23 | public BotService(IApiClient client, IServiceProvider provider, ILogger logger, IHub hub) 24 | { 25 | _client = client; 26 | _provider = provider; 27 | _logger = logger; 28 | _hub = hub; 29 | } 30 | 31 | public IBotCollectionBuilder Builder { get; } = new BotCollectionBuilder(); 32 | 33 | public async Task PushAsync(GenericEventArgs args) 34 | { 35 | var transaction = _hub.StartTransaction($"{nameof(HyperaiShell)}-{nameof(BotService)}", nameof(PushAsync), 36 | args.GetType().Name); 37 | var self = await _client.RequestAsync(null); 38 | 39 | switch (args) 40 | { 41 | case FriendMessageEventArgs it: 42 | await DoForAllAsync(x => x.OnFriendMessage(_client, it), self); 43 | break; 44 | case GroupMessageEventArgs it: 45 | await DoForAllAsync(x => x.OnGroupMessage(_client, it), self); 46 | break; 47 | case FriendRecallEventArgs it: 48 | await DoForAllAsync(x => x.OnFriendRecall(_client, it), self); 49 | break; 50 | case GroupRecallEventArgs it: 51 | await DoForAllAsync(x => x.OnGroupRecall(_client, it), self); 52 | break; 53 | case GroupMemberMutedEventArgs it: 54 | await DoForAllAsync(x => x.OnGroupMemberMuted(_client, it), self); 55 | break; 56 | case GroupMemberUnmutedEventArgs it: 57 | await DoForAllAsync(x => x.OnGroupMemberUnmuted(_client, it), self); 58 | break; 59 | case GroupAllMutedEventArgs it: 60 | await DoForAllAsync(x => x.OnGroupAllMuted(_client, it), self); 61 | break; 62 | case GroupLeftEventArgs it: 63 | await DoForAllAsync(x => x.OnGroupLeft(_client, it), self); 64 | break; 65 | case GroupJoinedEventArgs it: 66 | await DoForAllAsync(x => x.OnGroupJoined(_client, it), self); 67 | break; 68 | case GroupMemberCardChangedEventArgs it: 69 | await DoForAllAsync(x => x.OnGroupMemberCardChanged(_client, it), self); 70 | break; 71 | case GroupMemberTitleChangedEventArgs it: 72 | await DoForAllAsync(x => x.OnGroupMemberTitleChanged(_client, it), self); 73 | break; 74 | case GroupPermissionChangedEventArgs it: 75 | await DoForAllAsync(x => x.OnGroupPermissionChanged(_client, it), self); 76 | break; 77 | case FriendRequestEventArgs it: 78 | await DoForAllAsync(x => x.OnFriendRequest(_client, it), self); 79 | break; 80 | case GroupRequestEventArgs it: 81 | await DoForAllAsync(x => x.OnGroupRequest(_client, it), self); 82 | break; 83 | } 84 | 85 | await DoForAllAsync(x => x.OnEverything(_client, args), self); 86 | transaction.Finish(); 87 | } 88 | 89 | private async Task DoForAllAsync(Action action, Self me) 90 | { 91 | bots ??= Builder.Build(_provider); 92 | 93 | foreach (var bot in bots) 94 | { 95 | bot.Me = me; 96 | var task = Task.Run(() => action(bot), CancellationToken.None); 97 | await task; 98 | if (task.IsFaulted) 99 | _logger.LogError(task.Exception, "Bot({BotType}) action({ActionName}) exited unsuccessfully", 100 | bot.GetType().Name, action.Method.Name); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Hyperai/Hyperai.Abstractions/Messages/ConcreteModels/Face.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hyperai.Messages.ConcreteModels 4 | { 5 | [Serializable] 6 | public class Face : MessageElement 7 | { 8 | public Face(int faceId) 9 | { 10 | FaceId = faceId; 11 | } 12 | 13 | public int FaceId { get; set; } 14 | 15 | public override int GetHashCode() 16 | { 17 | return FaceId; 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return $""; 23 | } 24 | } 25 | 26 | public enum FaceType 27 | { 28 | UNKNOWN = 0xFF, 29 | JINGYA = 0, 30 | PIEZUI = 1, 31 | SE = 2, 32 | FADAI = 3, 33 | DEYI = 4, 34 | LIULEI = 5, 35 | HAIXIU = 6, 36 | BIZUI = 7, 37 | SHUI = 8, 38 | DAKU = 9, 39 | GANGA = 10, 40 | FANU = 11, 41 | TIAOPI = 12, 42 | CIYA = 13, 43 | WEIXIAO = 14, 44 | NANGUO = 15, 45 | KU = 16, 46 | ZHUAKUANG = 18, 47 | TU = 19, 48 | TOUXIAO = 20, 49 | KEAI = 21, 50 | BAIYAN = 22, 51 | AOMAN = 23, 52 | JI_E = 24, 53 | KUN = 25, 54 | JINGKONG = 26, 55 | LIUHAN = 27, 56 | HANXIAO = 28, 57 | DABING = 29, 58 | FENDOU = 30, 59 | ZHOUMA = 31, 60 | YIWEN = 32, 61 | YUN = 34, 62 | ZHEMO = 35, 63 | SHUAI = 36, 64 | KULOU = 37, 65 | QIAODA = 38, 66 | ZAIJIAN = 39, 67 | FADOU = 41, 68 | AIQING = 42, 69 | TIAOTIAO = 43, 70 | ZHUTOU = 46, 71 | YONGBAO = 49, 72 | DAN_GAO = 53, 73 | SHANDIAN = 54, 74 | ZHADAN = 55, 75 | DAO = 56, 76 | ZUQIU = 57, 77 | BIANBIAN = 59, 78 | KAFEI = 60, 79 | FAN = 61, 80 | MEIGUI = 63, 81 | DIAOXIE = 64, 82 | AIXIN = 66, 83 | XINSUI = 67, 84 | LIWU = 69, 85 | TAIYANG = 74, 86 | YUELIANG = 75, 87 | QIANG = 76, 88 | RUO = 77, 89 | WOSHOU = 78, 90 | SHENGLI = 79, 91 | FEIWEN = 85, 92 | NAOHUO = 86, 93 | XIGUA = 89, 94 | LENGHAN = 96, 95 | CAHAN = 97, 96 | KOUBI = 98, 97 | GUZHANG = 99, 98 | QIUDALE = 100, 99 | HUAIXIAO = 101, 100 | ZUOHENGHENG = 102, 101 | YOUHENGHENG = 103, 102 | HAQIAN = 104, 103 | BISHI = 105, 104 | WEIQU = 106, 105 | KUAIKULE = 107, 106 | YINXIAN = 108, 107 | QINQIN = 109, 108 | XIA = 110, 109 | KELIAN = 111, 110 | CAIDAO = 112, 111 | PIJIU = 113, 112 | LANQIU = 114, 113 | PINGPANG = 115, 114 | SHIAI = 116, 115 | PIAOCHONG = 117, 116 | BAOQUAN = 118, 117 | GOUYIN = 119, 118 | QUANTOU = 120, 119 | CHAJIN = 121, 120 | AINI = 122, 121 | BU = 123, 122 | HAO = 124, 123 | ZHUANQUAN = 125, 124 | KETOU = 126, 125 | HUITOU = 127, 126 | TIAOSHENG = 128, 127 | HUISHOU = 129, 128 | JIDONG = 130, 129 | JIEWU = 131, 130 | XIANWEN = 132, 131 | ZUOTAIJI = 133, 132 | YOUTAIJI = 134, 133 | SHUANGXI = 136, 134 | BIANPAO = 137, 135 | DENGLONG = 138, 136 | FACAI = 139, 137 | K_GE = 140, 138 | GOUWU = 141, 139 | YOUJIAN = 142, 140 | SHUAI_QI = 143, 141 | HECAI = 144, 142 | QIDAO = 145, 143 | BAOJIN = 146, 144 | BANGBANGTANG = 147, 145 | HE_NAI = 148, 146 | XIAMIAN = 149, 147 | XIANGJIAO = 150, 148 | FEIJI = 151, 149 | KAICHE = 152, 150 | GAOTIEZUOCHETOU = 153, 151 | CHEXIANG = 154, 152 | GAOTIEYOUCHETOU = 155, 153 | DUOYUN = 156, 154 | XIAYU = 157, 155 | CHAOPIAO = 158, 156 | XIONGMAO = 159, 157 | DENGPAO = 160, 158 | FENGCHE = 161, 159 | NAOZHONG = 162, 160 | DASAN = 163, 161 | CAIQIU = 164, 162 | ZUANJIE = 165, 163 | SHAFA = 166, 164 | ZHIJIN = 167, 165 | YAO = 168, 166 | SHOUQIANG = 169, 167 | QINGWA = 170, 168 | HEXIE = 184, 169 | YANGTUO = 185, 170 | YOULING = 187, 171 | DAN = 188, 172 | JUHUA = 190, 173 | HONGBAO = 192, 174 | DAXIAO = 193, 175 | BUKAIXIN = 194, 176 | LENGMO = 197, 177 | E = 198, 178 | HAOBANG = 199, 179 | BAITUO = 200, 180 | DIANZAN = 201, 181 | WULIAO = 202, 182 | TUOLIAN = 203, 183 | CHI = 204, 184 | SONGHUA = 205, 185 | HAIPA = 206, 186 | HUACHI = 207, 187 | XIAOYANGER = 208, 188 | BIAOLEI = 210, 189 | WOBUKAN = 211, 190 | BOBO = 214, 191 | HULIAN = 215, 192 | PAITOU = 216, 193 | CHEYICHE = 217, 194 | TIANYITIAN = 218, 195 | CENGYICENG = 219, 196 | ZHUAIZHATIAN = 220, 197 | DINGGUAGUA = 221, 198 | BAOBAO = 222, 199 | BAOJI = 223, 200 | KAIQIANG = 224, 201 | LIAOYILIAO = 225, 202 | PAIZHUO = 226, 203 | PAISHOU = 227, 204 | GONGXI = 228, 205 | GANBEI = 229, 206 | CHAOFENG = 230, 207 | HENG = 231, 208 | FOXI = 232, 209 | QIAOYIQIOA = 233, 210 | JINGDAI = 234, 211 | CHANDOU = 235, 212 | KENTOU = 236, 213 | TOUKAN = 237, 214 | SHANLIAN = 238, 215 | YUANLIANG = 239, 216 | PENLIAN = 24 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Packages/PackageManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.Loader; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using NuGet.Common; 10 | using NuGet.Frameworks; 11 | using NuGet.Packaging; 12 | using NuGet.Packaging.Core; 13 | using NuGet.Protocol; 14 | using NuGet.Protocol.Core.Types; 15 | 16 | namespace HyperaiShell.App.Packages 17 | { 18 | public class PackageManager 19 | { 20 | private readonly SourceCacheContext cacheContext = new(); 21 | 22 | 23 | private readonly List loadedIdentities = new(); 24 | 25 | private readonly SourceRepository repository = 26 | Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); 27 | 28 | public Action> PluginPackageLoaded { get; set; } 29 | 30 | 31 | public async Task BeginBatchAsync(IEnumerable files) 32 | { 33 | var queue = new Queue(); 34 | foreach (var file in files) queue.Enqueue(file); 35 | 36 | var tCount = queue.Count; 37 | var nCount = 0; 38 | while (queue.Count > 0) 39 | { 40 | var now = queue.Dequeue(); 41 | var (reader, assemblies) = 42 | nCount < tCount 43 | ? await LoadPluginPackageAsync(now) 44 | : await LoadDependencyPackageAsync(now); 45 | 46 | if (reader == null) continue; 47 | 48 | var depGroup = (await reader.GetPackageDependenciesAsync(CancellationToken.None)) 49 | .OrderByDescending(x => x.TargetFramework.Version).FirstOrDefault(); 50 | 51 | foreach (var dependent in depGroup?.Packages ?? Enumerable.Empty()) 52 | { 53 | if (loadedIdentities.Contains(dependent.Id)) continue; 54 | var located = await LocatePackageAsync(dependent.Id, dependent.VersionRange.MinVersion.Version); 55 | if (string.IsNullOrWhiteSpace(located)) continue; 56 | 57 | queue.Enqueue(located); 58 | } 59 | 60 | nCount++; 61 | } 62 | } 63 | 64 | public async Task<(PackageArchiveReader, IEnumerable)> LoadPluginPackageAsync(string file) 65 | { 66 | await using var stream = File.OpenRead(file); 67 | var (reader, assemblies) = await LoadPackageAsync(stream); 68 | if (reader != null) OnPluginPackageLoaded(file, reader, assemblies); 69 | 70 | return (reader, assemblies); 71 | } 72 | 73 | public async Task<(PackageArchiveReader, IEnumerable)> LoadDependencyPackageAsync(string file) 74 | { 75 | await using var stream = File.OpenRead(file); 76 | var (reader, assemblies) = await LoadPackageAsync(stream); 77 | 78 | return (reader, assemblies); 79 | } 80 | 81 | public async Task<(PackageArchiveReader, IEnumerable)> LoadPackageAsync(Stream stream) 82 | { 83 | var reader = new PackageArchiveReader(stream); 84 | var identity = await reader.GetIdentityAsync(CancellationToken.None); 85 | if (CheckIfNoNeed(identity.Id)) return (null, null); 86 | 87 | var assemblies = new List(); 88 | var libGroup = (await reader.GetLibItemsAsync(CancellationToken.None)) 89 | .OrderByDescending(x => x.TargetFramework, new NuGetFrameworkSorter()).FirstOrDefault(); 90 | foreach (var item in libGroup?.Items ?? Enumerable.Empty()) 91 | { 92 | if (!item.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase)) continue; 93 | if (item.Count(x => x == '/' || x == '\\') > 2) continue; // skip net50/*culture*/*.resource.dll 94 | var libStream = reader.GetStream(item); 95 | var buffer = new MemoryStream(); 96 | await libStream.CopyToAsync(buffer); 97 | buffer.Position = 0; 98 | var bytes = new byte[buffer.Length]; 99 | buffer.Read(bytes, 0, bytes.Length); 100 | var assembly = Assembly.Load(bytes); 101 | assemblies.Add(assembly); 102 | buffer.Close(); 103 | libStream.Close(); 104 | } 105 | 106 | loadedIdentities.Add(identity.Id); 107 | return (reader, assemblies); 108 | } 109 | 110 | public Assembly FindAssembly(string assemblyName) 111 | { 112 | foreach (var assembly in AssemblyLoadContext.All.SelectMany(x => x.Assemblies)) 113 | if (assembly.GetName().Name == new AssemblyName(assemblyName).Name) 114 | return assembly; 115 | 116 | return null; 117 | } 118 | 119 | protected void OnPluginPackageLoaded(string file, PackageArchiveReader reader, IEnumerable assemblies) 120 | { 121 | PluginPackageLoaded?.Invoke(file, reader, assemblies); 122 | } 123 | 124 | 125 | private bool CheckIfNoNeed(string identity) 126 | { 127 | return loadedIdentities.Contains(identity); 128 | } 129 | 130 | private async Task LocatePackageAsync(string identity, Version version) 131 | { 132 | // look up in cacahe 133 | if (CheckIfNoNeed(identity)) return null; 134 | var packagesDir = Path.Combine(Environment.CurrentDirectory, "cache", "packages"); 135 | Directory.CreateDirectory(packagesDir); 136 | var file = Path.Combine("cache", "packages", $"{identity}.{version}.nupkg"); 137 | if (!File.Exists(file)) 138 | { 139 | var resource = await repository.GetResourceAsync(CancellationToken.None); 140 | var found = 141 | (await resource.GetAllVersionsAsync(identity, cacheContext, NullLogger.Instance, 142 | CancellationToken.None)).FirstOrDefault(x => x.Version == version); 143 | if (version != null) 144 | { 145 | var local = Path.Combine("cache", "packages", 146 | $"{identity}.{found.Version}.nupkg"); 147 | var localStream = File.OpenWrite(local); 148 | await resource.CopyNupkgToStreamAsync(identity, found, localStream, 149 | cacheContext, NullLogger.Instance, CancellationToken.None); 150 | await localStream.FlushAsync(); 151 | localStream.Close(); 152 | file = local; 153 | } 154 | } 155 | 156 | return file; 157 | } 158 | 159 | #region Singleton 160 | 161 | public static PackageManager Instance { get; } = new(); 162 | 163 | private PackageManager() 164 | { 165 | } 166 | 167 | #endregion 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/HyperaiShell/HyperaiShell.App/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Hyperai.Services; 9 | using Hyperai.Units; 10 | using HyperaiShell.App.Data; 11 | using HyperaiShell.App.Packages; 12 | using HyperaiShell.App.Plugins; 13 | using HyperaiShell.Foundation; 14 | using HyperaiShell.Foundation.Data; 15 | using HyperaiShell.Foundation.Plugins; 16 | using HyperaiShell.Foundation.Services; 17 | using LiteDB; 18 | using Microsoft.Extensions.Configuration; 19 | using Microsoft.Extensions.DependencyInjection; 20 | using Microsoft.Extensions.Hosting; 21 | using Microsoft.Extensions.Logging; 22 | using NuGet.Common; 23 | using NuGet.Frameworks; 24 | using NuGet.Packaging; 25 | using ILogger = Microsoft.Extensions.Logging.ILogger; 26 | 27 | namespace HyperaiShell.App 28 | { 29 | public class Program 30 | { 31 | private static ILogger _logger; 32 | 33 | public static async Task Main() 34 | { 35 | AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; 36 | AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; 37 | // manager instance init 38 | PackageManager.Instance.PluginPackageLoaded = PluginPackageLoaded; 39 | 40 | //env init 41 | var cfgBuilder = new ConfigurationBuilder().AddTomlFile("appsettings.toml", false); 42 | var config = cfgBuilder.Build(); 43 | 44 | var startup = new Bootstrapper(config); 45 | var hostBuilder = new HostBuilder() 46 | .ConfigureServices(startup.ConfigureServices) 47 | .UseConsoleLifetime(); 48 | // search packages and load 49 | var nupkgs = new Queue(); 50 | foreach (var nupkg in Directory.GetFiles("plugins", "*.nupkg")) nupkgs.Enqueue(nupkg); 51 | 52 | await PackageManager.Instance.BeginBatchAsync(nupkgs); 53 | 54 | PluginManager.Instance.ActivateAll(plugin => hostBuilder.ConfigureServices(plugin.ConfigureServices)); 55 | 56 | Shared.Host = hostBuilder.Build(); 57 | 58 | _logger = Shared.Host.Services.GetRequiredService>(); 59 | 60 | Welcome(); 61 | 62 | var botService = Shared.Host.Services.GetRequiredService(); 63 | var unitService = Shared.Host.Services.GetRequiredService(); 64 | unitService.SearchForUnits(); 65 | 66 | PluginManager.Instance.ActivateAll(plugin => 67 | { 68 | plugin.ConfigureBots(botService.Builder, config); 69 | plugin.OnStarted(Shared.Host.Services, config); 70 | _logger.LogInformation("Plugin {PluginIdentity}/{Version} activated", plugin.Context.Meta.Identity, 71 | plugin.GetType().Assembly.GetName().Version); 72 | }); 73 | 74 | var task = Shared.Host.RunAsync(); 75 | // TEST HERE 76 | 77 | await task; 78 | } 79 | 80 | private static Assembly? CurrentDomainOnAssemblyResolve(object? sender, ResolveEventArgs args) 81 | { 82 | return PackageManager.Instance.FindAssembly(args.Name); 83 | } 84 | 85 | private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) 86 | { 87 | var exception = (Exception)e.ExceptionObject; 88 | if (e.IsTerminating) 89 | { 90 | _logger.LogCritical(exception, "Terminating for uncaught exception"); 91 | Environment.ExitCode = -1; 92 | } 93 | else 94 | { 95 | _logger.LogError(exception, "Exception caught"); 96 | } 97 | } 98 | 99 | private static void Welcome() 100 | { 101 | _logger.LogInformation(@" 102 | ____ _ _ _ _ 103 | | _ \ _ __ ___ (_) | | |_ _ _ __ ___ _ __ __ _(_) 104 | | |_) | '__/ _ \| | |_| | | | | '_ \ / _ \ '__/ _` | | 105 | | __/| | | (_) | | _ | |_| | |_) | __/ | | (_| | | 106 | |_| |_| \___// |_| |_|\__, | .__/ \___|_| \__,_|_| 107 | |__/ |___/|_| "); 108 | _logger.LogInformation( 109 | "Powered by ProjHyperai\nHyperaiShell v{HyperaiShellV} (Plugin based on v{PluginBaseV})\nHyperai v{HyperaiV}\nHyperai.Units v{HyperaiUnitsV}", 110 | typeof(Program).Assembly.GetName().Version, 111 | typeof(PluginBase).Assembly.GetName().Version, 112 | typeof(IApiClient).Assembly.GetName().Version, 113 | typeof(UnitService).Assembly.GetName().Version); 114 | } 115 | 116 | private static async void PluginPackageLoaded(string fileName, PackageArchiveReader reader, 117 | IEnumerable assemblies) 118 | { 119 | var plugins = assemblies.SelectMany(x => 120 | x.GetExportedTypes().Where(y => !y.IsAbstract && y.IsSubclassOf(typeof(PluginBase)))); 121 | var plugin = plugins.FirstOrDefault(); // 其他的都无视掉 122 | 123 | if (plugin != null) 124 | { 125 | var context = new PluginContext(); 126 | var identity = await reader.GetIdentityAsync(CancellationToken.None); 127 | var meta = new PluginMeta(identity.Id, fileName, 128 | Path.Combine("plugins", identity.Id)); 129 | context.Meta = meta; 130 | if (!Directory.Exists(meta.SpaceDirectory)) 131 | { 132 | Directory.CreateDirectory(meta.SpaceDirectory); 133 | 134 | var content = (await reader.GetContentItemsAsync(CancellationToken.None)) 135 | .OrderByDescending(x => x.TargetFramework, new NuGetFrameworkSorter()).FirstOrDefault(); 136 | 137 | if (content != null) 138 | foreach (var item in content.Items) 139 | { 140 | var path = Path.Combine(meta.SpaceDirectory, 141 | item.Substring(item.IndexOf('/') + 1)); 142 | reader.ExtractFile(item, path, NullLogger.Instance); 143 | } 144 | } 145 | 146 | var configFile = Path.Combine(meta.SpaceDirectory, "config.toml"); 147 | if (File.Exists(configFile)) 148 | context.Configuration = 149 | new Lazy(() => new ConfigurationBuilder().AddTomlFile(configFile).Build()); 150 | else context.Configuration = new Lazy(() => new ConfigurationBuilder().Build()); 151 | 152 | var dataFile = Path.Combine(meta.SpaceDirectory, "data.lite.db"); 153 | context.Repository = new Lazy(() => new LiteDbRepository(new LiteDatabase(dataFile))); 154 | PluginManager.Instance.RegisterPlugin(plugin, context); 155 | } 156 | } 157 | } 158 | } 159 | --------------------------------------------------------------------------------