├── .editorconfig ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── ProjHyperai.code-workspace ├── README.md ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── .editorconfig ├── Build.cs ├── Configuration.cs ├── Directory.Build.props ├── Directory.Build.targets ├── _build.csproj └── _build.csproj.DotSettings └── src ├── Hyperai.Units ├── Hyperai.Units.Abstractions.Tests │ ├── Attributes │ │ └── ExtractAttributeTests.cs │ └── Hyperai.Units.Abstractions.Tests.csproj ├── Hyperai.Units.Abstractions │ ├── ActionDelegate.cs │ ├── ActionEntry.cs │ ├── Attributes │ │ ├── ExtractAttribute.cs │ │ ├── FilterByAttribute.cs │ │ └── ReceiveAttribute.cs │ ├── Hyperai.Units.Abstractions.csproj │ ├── IFilter.cs │ ├── IUnitService.cs │ ├── MessageContext.cs │ ├── MessageContextExtensions.cs │ ├── UnitBase.cs │ └── UnitFactory.cs ├── Hyperai.Units.Tests │ └── Hyperai.Units.Tests.csproj ├── Hyperai.Units │ ├── Extensions.cs │ ├── Hyperai.Units.csproj │ ├── UnitMiddleware.cs │ └── UnitService.cs └── README.md ├── Hyperai ├── Hyperai.Abstractions.Tests │ ├── Hyperai.Abstractions.Tests.csproj │ ├── Messages │ │ ├── MessageChainBuilderTests.cs │ │ ├── MessageChainTests.cs │ │ └── PlainTests.cs │ └── Relations │ │ └── SignatureTests.cs ├── Hyperai.Abstractions │ ├── Events │ │ ├── DefaultEventHandler.cs │ │ ├── FriendMessageEventArgs.cs │ │ ├── FriendRecallEventArgs.cs │ │ ├── FriendRequestEventArgs.cs │ │ ├── FriendResponseEventArgs.cs │ │ ├── GenericEventArgs.cs │ │ ├── GroupAllMutedEventArgs.cs │ │ ├── GroupJoinedEventArgs.cs │ │ ├── GroupLeftEventArgs.cs │ │ ├── GroupMemberCardChangedEventArgs.cs │ │ ├── GroupMemberMutedEventArgs.cs │ │ ├── GroupMemberTitleChangedEventArgs.cs │ │ ├── GroupMemberUnmutedEventArgs.cs │ │ ├── GroupMessageEventArgs.cs │ │ ├── GroupNameChangedEventArgs.cs │ │ ├── GroupPermissionChangedEventArgs.cs │ │ ├── GroupRecallEventArgs.cs │ │ ├── GroupRequestEventArgs.cs │ │ ├── GroupResponseEventArgs.cs │ │ ├── IEventHandler.cs │ │ ├── MessageEventArgs.cs │ │ ├── MessageEventType.cs │ │ └── RecallEventArgs.cs │ ├── Hyperai.Abstractions.csproj │ ├── Messages │ │ ├── ConcreteModels │ │ │ ├── AppContent.cs │ │ │ ├── At.cs │ │ │ ├── AtAll.cs │ │ │ ├── ContentBase.cs │ │ │ ├── Face.cs │ │ │ ├── FileSources │ │ │ │ ├── IFileSource.cs │ │ │ │ ├── StreamSource.cs │ │ │ │ └── UrlSource.cs │ │ │ ├── Flash.cs │ │ │ ├── Image.cs │ │ │ ├── ImageBase.cs │ │ │ ├── ImageBaseExtensions.cs │ │ │ ├── JsonContent.cs │ │ │ ├── Music.cs │ │ │ ├── Node.cs │ │ │ ├── Plain.cs │ │ │ ├── Poke.cs │ │ │ ├── Quote.cs │ │ │ ├── Source.cs │ │ │ ├── StreamedFileBase.cs │ │ │ ├── Unknown.cs │ │ │ ├── Video.cs │ │ │ ├── Voice.cs │ │ │ └── XmlContent.cs │ │ ├── IMessageChainFormatter.cs │ │ ├── IMessageChainParser.cs │ │ ├── MessageChain.cs │ │ ├── MessageChainBuilder.cs │ │ ├── MessageChainBuilderExtensions.cs │ │ ├── MessageChainExtensions.cs │ │ └── MessageElement.cs │ ├── Middlewares │ │ └── IMiddleware.cs │ ├── Receipts │ │ ├── GenericReceipt.cs │ │ └── MessageReceipt.cs │ ├── Relations │ │ ├── Friend.cs │ │ ├── Group.cs │ │ ├── Member.cs │ │ ├── RelationModel.cs │ │ ├── Self.cs │ │ ├── Signature.cs │ │ └── User.cs │ └── Services │ │ ├── ApiClientConnectionState.cs │ │ ├── ApiClientExtensions.cs │ │ └── IApiClient.cs ├── Hyperai.Core.Tests │ ├── Hyperai.Core.Tests.csproj │ └── Serialization │ │ ├── HyperCodeFormatterTests.cs │ │ └── HyperCodeParserTests.cs ├── Hyperai.Core │ ├── Hyperai.Core.csproj │ ├── HyperaiServer.cs │ ├── HyperaiServerOptions.cs │ ├── HyperaiServerOptionsBuilder.cs │ ├── HyperaiServerOptionsBuilderExtensions.cs │ ├── Serialization │ │ ├── HyperCodeFormatter.cs │ │ ├── HyperCodeParser.cs │ │ └── MessageElementFactory.cs │ └── ServiceCollectionExtensions.cs └── README.md ├── HyperaiShell ├── HyperaiShell.App │ ├── Bootstrapper.cs │ ├── Bots │ │ ├── BotBuilder.cs │ │ └── BotCollectionBuilder.cs │ ├── Data │ │ ├── AssemblyNameTypeNameBinder.cs │ │ ├── LiteDbQueryable.cs │ │ ├── LiteDbRepository.cs │ │ └── SearchingTypeNameBinder.cs │ ├── Hangfire │ │ └── Logging │ │ │ ├── HangfireLogProvider.cs │ │ │ └── HangfireLogger.cs │ ├── Helpers │ │ └── PathHelper.cs │ ├── HyperaiShell.App.csproj │ ├── Logging │ │ └── ConsoleFormatters │ │ │ ├── EventArgsFormatter.cs │ │ │ ├── MessageElementFormatter.cs │ │ │ └── RelationFormatter.cs │ ├── Middlewares │ │ ├── BlockMiddleware.cs │ │ ├── BotMiddleware.cs │ │ ├── LoggingMiddleware.cs │ │ ├── MiddlewareExtensions.cs │ │ └── TranslatorMiddleware.cs │ ├── Models │ │ ├── Attachment.cs │ │ ├── BlockedUser.cs │ │ ├── TicketBox.cs │ │ └── TicketBoxExtensions.cs │ ├── Packages │ │ └── PackageManager.cs │ ├── Plugins │ │ ├── PluginConfiguration.cs │ │ ├── PluginContext.cs │ │ ├── PluginManager.cs │ │ └── PluginRepository.cs │ ├── Program.cs │ ├── Services │ │ ├── AttachmentService.cs │ │ ├── AuthorizationService.cs │ │ ├── BlockService.cs │ │ ├── BotService.cs │ │ └── ServiceCollectionExtensions.cs │ └── appsettings.toml ├── HyperaiShell.Foundation │ ├── Authorization │ │ ├── Attributes │ │ │ └── RequiredTicketAttribute.cs │ │ ├── ExpiryTicket.cs │ │ ├── LimitedUseTicket.cs │ │ ├── NormalTicket.cs │ │ └── TicketBase.cs │ ├── Bots │ │ ├── BotBase.cs │ │ ├── BotCollection.cs │ │ ├── IBotBuilder.cs │ │ └── IBotCollectionBuilder.cs │ ├── Data │ │ └── IRepository.cs │ ├── HyperaiShell.Foundation.csproj │ ├── ModelExtensions │ │ ├── AttachmentExtensions.cs │ │ ├── AuthorizationExtensions.cs │ │ ├── BlacklistExtensions.cs │ │ ├── ClientExtensions.cs │ │ ├── MessageChainExtensions.cs │ │ └── RelationExtensions.cs │ ├── Plugins │ │ ├── IPluginConfiguration.cs │ │ ├── IPluginContext.cs │ │ ├── IPluginProperty.cs │ │ ├── IPluginRepository.cs │ │ ├── PluginBase.cs │ │ └── PluginMeta.cs │ ├── Services │ │ ├── AuthorizationServiceExtensions.cs │ │ ├── ForAttachmentUpdateScope.cs │ │ ├── IAttachmentService.cs │ │ ├── IAuthorizationService.cs │ │ ├── IBlockService.cs │ │ └── IBotService.cs │ └── Shared.cs └── README.md └── ProjHyperai.sln /.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 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "src/ProjHyperai.sln" 4 | } -------------------------------------------------------------------------------- /ProjHyperai.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj" 17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "Current" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function ExecSafe([scriptblock] $cmd) { 32 | & $cmd 33 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 34 | } 35 | 36 | # If dotnet CLI is installed globally and it matches requested version, use for execution 37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 40 | } 41 | else { 42 | # Download install script 43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 47 | 48 | # If global.json exists, load expected version 49 | if (Test-Path $DotNetGlobalFile) { 50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 52 | $DotNetVersion = $DotNetGlobal.sdk.version 53 | } 54 | } 55 | 56 | # Install by channel or version 57 | $DotNetDirectory = "$TempDirectory\dotnet-win" 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 70 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="Current" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | export DOTNET_MULTILEVEL_LOOKUP=0 22 | 23 | ########################################################################### 24 | # EXECUTION 25 | ########################################################################### 26 | 27 | function FirstJsonValue { 28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 29 | } 30 | 31 | # If dotnet CLI is installed globally and it matches requested version, use for execution 32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 33 | export DOTNET_EXE="$(command -v dotnet)" 34 | else 35 | # Download install script 36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 37 | mkdir -p "$TEMP_DIRECTORY" 38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 39 | chmod +x "$DOTNET_INSTALL_FILE" 40 | 41 | # If global.json exists, load expected version 42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 44 | if [[ "$DOTNET_VERSION" == "" ]]; then 45 | unset DOTNET_VERSION 46 | fi 47 | fi 48 | 49 | # Install by channel or version 50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 51 | if [[ -z ${DOTNET_VERSION+x} ]]; then 52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 53 | else 54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 55 | fi 56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 57 | fi 58 | 59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" 60 | 61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 63 | -------------------------------------------------------------------------------- /build/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_style_qualification_for_field = false:warning 3 | dotnet_style_qualification_for_property = false:warning 4 | dotnet_style_qualification_for_method = false:warning 5 | dotnet_style_qualification_for_event = false:warning 6 | dotnet_style_require_accessibility_modifiers = never:warning 7 | 8 | csharp_style_expression_bodied_methods = true:silent 9 | csharp_style_expression_bodied_properties = true:warning 10 | csharp_style_expression_bodied_indexers = true:warning 11 | csharp_style_expression_bodied_accessors = true:warning 12 | -------------------------------------------------------------------------------- /build/Build.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using Nuke.Common; 4 | using Nuke.Common.CI; 5 | using Nuke.Common.Execution; 6 | using Nuke.Common.Git; 7 | using Nuke.Common.IO; 8 | using Nuke.Common.ProjectModel; 9 | using Nuke.Common.Tooling; 10 | using Nuke.Common.Tools.DotNet; 11 | using Nuke.Common.Tools.GitVersion; 12 | using static Nuke.Common.IO.FileSystemTasks; 13 | using static Nuke.Common.Tools.DotNet.DotNetTasks; 14 | 15 | [CheckBuildProjectConfigurations] 16 | [ShutdownDotNetAfterServerBuild] 17 | class Build : NukeBuild 18 | { 19 | [Parameter] readonly string ApiKey; 20 | 21 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] 22 | readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; 23 | 24 | [GitRepository] readonly GitRepository GitRepository; 25 | [GitVersion] readonly GitVersion GitVersion; 26 | 27 | [Solution] readonly Solution Solution; 28 | 29 | AbsolutePath SourceDirectory => RootDirectory / "src"; 30 | AbsolutePath TestsDirectory => RootDirectory / "tests"; 31 | AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; 32 | 33 | Target Clean => _ => _ 34 | .Before(Restore) 35 | .Executes(() => 36 | { 37 | EnsureCleanDirectory(ArtifactsDirectory); 38 | }); 39 | 40 | Target Restore => _ => _ 41 | .Executes(() => 42 | { 43 | DotNetRestore(s => s 44 | .SetProjectFile(Solution)); 45 | }); 46 | 47 | Target Compile => _ => _ 48 | .DependsOn(Restore) 49 | .Executes(() => 50 | { 51 | DotNetBuild(s => s 52 | .SetProjectFile(Solution) 53 | .SetConfiguration(Configuration) 54 | .EnableNoRestore()); 55 | }); 56 | 57 | Target Test => _ => _ 58 | .DependsOn(Compile) 59 | .Executes(() => 60 | { 61 | DotNetTest(c => c 62 | .SetProcessWorkingDirectory(Solution.Directory)); 63 | }); 64 | 65 | Target PublishApplication => _ => _ 66 | .DependsOn(Test) 67 | .DependsOn(Clean) 68 | .Executes(() => 69 | { 70 | DotNetPublish(c => c 71 | .SetProcessWorkingDirectory(Solution.GetProject("HyperaiShell.App")!.Directory) 72 | .SetOutput(ArtifactsDirectory) 73 | .SetRuntime("linux-x64") 74 | .EnablePublishSingleFile()); 75 | }); 76 | 77 | Target GeneratePackages => _ => _ 78 | .DependsOn(Test) 79 | .DependsOn(Clean) 80 | .Executes(() => 81 | { 82 | foreach (var project in Solution.AllProjects.Where(x => x.Name != nameof(Build))) 83 | DotNetPack(c => c 84 | .SetProcessWorkingDirectory(project.Directory) 85 | .SetOutputDirectory(ArtifactsDirectory)); 86 | }); 87 | 88 | Target PushPackages => _ => _ 89 | .DependsOn(GeneratePackages) 90 | .Requires(() => ApiKey) 91 | .Executes(() => 92 | { 93 | foreach (var file in Directory.GetFiles(ArtifactsDirectory, "*.nupkg")) 94 | DotNetNuGetPush(c => c 95 | .SetSource("https://api.nuget.org/v3/index.json") 96 | .SetApiKey(ApiKey) 97 | .EnableSkipDuplicate() 98 | .SetTargetPath(file)); 99 | }); 100 | 101 | /// Support plugins are available for: 102 | /// - JetBrains ReSharper https://nuke.build/resharper 103 | /// - JetBrains Rider https://nuke.build/rider 104 | /// - Microsoft VisualStudio https://nuke.build/visualstudio 105 | /// - Microsoft VSCode https://nuke.build/vscode 106 | public static int Main() => Execute(x => x.Compile); 107 | } 108 | -------------------------------------------------------------------------------- /build/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Nuke.Common.Tooling; 3 | 4 | [TypeConverter(typeof(TypeConverter))] 5 | public class Configuration : Enumeration 6 | { 7 | public static Configuration Debug = new() { Value = nameof(Debug) }; 8 | public static Configuration Release = new() { Value = nameof(Release) }; 9 | 10 | public static implicit operator string(Configuration configuration) => configuration.Value; 11 | } 12 | -------------------------------------------------------------------------------- /build/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | CS0649;CS0169 8 | .. 9 | .. 10 | 1 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /build/_build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | Implicit 7 | Implicit 8 | ExpressionBody 9 | 0 10 | NEXT_LINE 11 | True 12 | False 13 | 120 14 | IF_OWNER_IS_SINGLE_LINE 15 | WRAP_IF_LONG 16 | False 17 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 19 | True 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | True 27 | True 28 | -------------------------------------------------------------------------------- /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.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.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/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.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/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/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.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.Units/Hyperai.Units.Abstractions/IFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Units 2 | { 3 | public interface IFilter 4 | { 5 | bool Check(MessageContext context); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /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.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.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.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.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.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/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.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/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/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/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/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/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/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/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.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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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.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.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/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/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/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/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/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/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/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/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.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.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/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/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/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/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/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/Messages/IMessageChainParser.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Messages 2 | { 3 | public interface IMessageChainParser 4 | { 5 | MessageChain Parse(string text); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /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.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/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/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/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/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/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/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/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/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/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/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/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/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.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/Hyperai/Hyperai.Abstractions/Services/ApiClientConnectionState.cs: -------------------------------------------------------------------------------- 1 | namespace Hyperai.Services 2 | { 3 | public enum ApiClientConnectionState 4 | { 5 | Connected, 6 | Disconnected 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /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/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/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/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.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/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/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/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.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.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/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/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/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/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/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/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/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/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/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/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.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/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/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/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/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/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/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/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/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/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/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/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/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/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.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.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/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/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/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/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/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/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/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/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/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/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/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.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/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/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/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/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/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/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/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/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/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/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.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.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/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/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.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.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/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.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/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/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/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 | -------------------------------------------------------------------------------- /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.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/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/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/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/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/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/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/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) --------------------------------------------------------------------------------