main 178a7708f859 cached
507 files
3.9 MB
1.0M tokens
3673 symbols
1 requests
Download .txt
Showing preview only (4,186K chars total). Download the full file or copy to clipboard to get everything.
Repository: ZHANGTIANYAO1/TS3AudioBot-NetEaseCloudmusic-plugin
Branch: main
Commit: 178a7708f859
Files: 507
Total size: 3.9 MB

Directory structure:
gitextract_1muog4nl/

├── ClassLibrary4.csproj
├── ClassLibrary4.sln
├── LICENSE
├── README.md
├── TS3AudioBot/
│   ├── Algorithm/
│   │   ├── IFilterAlgorithm.cs
│   │   ├── LruCache.cs
│   │   └── TimedCache.cs
│   ├── Audio/
│   │   ├── AudioValues.cs
│   │   ├── CustomTargetPipe.cs
│   │   ├── FfmpegProducer.cs
│   │   ├── IPlayerSource.cs
│   │   ├── IVoiceTarget.cs
│   │   ├── PlayInfo.cs
│   │   ├── PlayInfoEventArgs.cs
│   │   ├── PlayManager.cs
│   │   ├── Player.cs
│   │   ├── SongEndEventArgs.cs
│   │   ├── SongInfoChanged.cs
│   │   ├── StallCheckPipe.cs
│   │   └── StreamAudioPlayerSource.cs
│   ├── Bot.cs
│   ├── BotManager.cs
│   ├── CallerInfo.cs
│   ├── ClientCall.cs
│   ├── CommandSystem/
│   │   ├── Ast/
│   │   │   ├── AstCommand.cs
│   │   │   ├── AstError.cs
│   │   │   ├── AstNode.cs
│   │   │   ├── AstType.cs
│   │   │   ├── AstValue.cs
│   │   │   └── StringType.cs
│   │   ├── BotCommand.cs
│   │   ├── CommandAttribute.cs
│   │   ├── CommandException.cs
│   │   ├── CommandManager.cs
│   │   ├── CommandParser.cs
│   │   ├── CommandResults/
│   │   │   ├── IAudioResourceResult.cs
│   │   │   ├── IWrappedResult.cs
│   │   │   ├── PickObjectCommand.cs
│   │   │   └── TailString.cs
│   │   ├── CommandSystemExtensions.cs
│   │   ├── CommandSystemTypes.cs
│   │   ├── Commands/
│   │   │   ├── AliasCommand.cs
│   │   │   ├── AppliedCommand.cs
│   │   │   ├── CommandGroup.cs
│   │   │   ├── FunctionCommand.cs
│   │   │   ├── ICommand.cs
│   │   │   ├── LazyCommand.cs
│   │   │   ├── OverloadedFunctionCommand.cs
│   │   │   ├── ResultCommand.cs
│   │   │   └── RootCommand.cs
│   │   ├── ExecutionInformation.cs
│   │   ├── ICommandBag.cs
│   │   ├── StaticList.cs
│   │   └── Text/
│   │       ├── AppliedTextMod.cs
│   │       ├── Color.cs
│   │       ├── LongTextBehaviour.cs
│   │       ├── LongTextTransform.cs
│   │       ├── TextMod.cs
│   │       ├── TextModBuilder.cs
│   │       ├── TextModFlag.cs
│   │       └── TextModHelper.cs
│   ├── Config/
│   │   ├── Config.cs
│   │   ├── ConfigArray.cs
│   │   ├── ConfigDynamicTable.cs
│   │   ├── ConfigEnumerable.cs
│   │   ├── ConfigHelper.cs
│   │   ├── ConfigPart.cs
│   │   ├── ConfigStructs.cs
│   │   ├── ConfigTable.cs
│   │   ├── ConfigUpgrade2.cs
│   │   └── ConfigValue.cs
│   ├── Core.cs
│   ├── DbStore.cs
│   ├── Dependency/
│   │   ├── BasicInjector.cs
│   │   ├── ChainedInjector.cs
│   │   ├── DependencyBuilder.cs
│   │   ├── IInjector.cs
│   │   ├── InjectorExtensions.cs
│   │   ├── Module.cs
│   │   └── NullInjector.cs
│   ├── Environment/
│   │   ├── Stats.cs
│   │   ├── SystemData.cs
│   │   └── SystemMonitor.cs
│   ├── Error.cs
│   ├── Helper/
│   │   ├── AttributeStrings.cs
│   │   ├── Const.cs
│   │   ├── Diagnose/
│   │   │   ├── SelfDiagnoseLevel.cs
│   │   │   └── SelfDiagnoseMessage.cs
│   │   ├── IJsonConfig.cs
│   │   ├── ImageUtil.cs
│   │   ├── Interactive.cs
│   │   ├── LimitStream.cs
│   │   ├── TextUtil.cs
│   │   ├── TomlTools.cs
│   │   ├── Util.cs
│   │   └── WebWrapper.cs
│   ├── History/
│   │   ├── AudioLogEntry.cs
│   │   ├── HistoryManager.cs
│   │   ├── HistorySaveData.cs
│   │   ├── IHistoryFormatter.cs
│   │   ├── SearchQuery.cs
│   │   └── SmartHistoryFormatter.cs
│   ├── InvokerData.cs
│   ├── Limits.cs
│   ├── Localization/
│   │   ├── DynamicResourceManager.cs
│   │   ├── LocalStr.cs
│   │   ├── LocalizationManager.cs
│   │   ├── strings.Designer.cs
│   │   └── strings.resx
│   ├── MainCommands.cs
│   ├── Playlists/
│   │   ├── LoopMode.cs
│   │   ├── Parser/
│   │   │   └── JspfContent.cs
│   │   ├── Playlist.cs
│   │   ├── PlaylistApiExtensions.cs
│   │   ├── PlaylistIO.cs
│   │   ├── PlaylistItem.cs
│   │   ├── PlaylistManager.cs
│   │   └── Shuffle/
│   │       ├── IShuffleAlgorithm.cs
│   │       ├── LinearFeedbackShiftRegister.cs
│   │       ├── ListedShuffle.cs
│   │       └── NormalOrder.cs
│   ├── Plugins/
│   │   ├── ITabPlugin.cs
│   │   ├── Plugin.cs
│   │   ├── PluginCommandBag.cs
│   │   ├── PluginExtensions.cs
│   │   ├── PluginManager.cs
│   │   ├── PluginObjects.cs
│   │   ├── PluginResponse.cs
│   │   └── PluginStatus.cs
│   ├── Properties/
│   │   └── PublishProfiles/
│   │       ├── FolderProfile.pubxml
│   │       └── FolderProfile.pubxml.user
│   ├── Properties.cs
│   ├── ResourceFactories/
│   │   ├── AudioResource.cs
│   │   ├── AudioTags/
│   │   │   ├── AudioTagReader.cs
│   │   │   ├── BinaryReaderBigEndianExtensions.cs
│   │   │   └── M3uReader.cs
│   │   ├── BandcampResolver.cs
│   │   ├── IPlaylistResolver.cs
│   │   ├── IResolver.cs
│   │   ├── IResourceResolver.cs
│   │   ├── ISearchResolver.cs
│   │   ├── IThumbnailResolver.cs
│   │   ├── MatchCertainty.cs
│   │   ├── MediaResolver.cs
│   │   ├── PlayResource.cs
│   │   ├── ResolveContext.cs
│   │   ├── ResourceResolver.cs
│   │   ├── SongInfo.cs
│   │   ├── SoundcloudResolver.cs
│   │   ├── TwitchResolver.cs
│   │   ├── Youtube/
│   │   │   ├── Json.cs
│   │   │   ├── LoaderPriority.cs
│   │   │   ├── VideoCodec.cs
│   │   │   ├── VideoData.cs
│   │   │   └── YoutubeResolver.cs
│   │   └── YoutubeDlHelper.cs
│   ├── Resources/
│   │   ├── DefaultRights.toml
│   │   └── NLog.config
│   ├── Rights/
│   │   ├── CreateFileSettings.cs
│   │   ├── ExecuteContext.cs
│   │   ├── Matchers/
│   │   │   ├── MatchApiCallerIp.cs
│   │   │   ├── MatchBot.cs
│   │   │   ├── MatchChannelGroupId.cs
│   │   │   ├── MatchClientGroupId.cs
│   │   │   ├── MatchClientUid.cs
│   │   │   ├── MatchHost.cs
│   │   │   ├── MatchIsApi.cs
│   │   │   ├── MatchPermission.cs
│   │   │   ├── MatchToken.cs
│   │   │   ├── MatchVisibility.cs
│   │   │   ├── Matcher.cs
│   │   │   └── PermCompare.cs
│   │   ├── ParseContext.cs
│   │   ├── RightsDecl.cs
│   │   ├── RightsGroup.cs
│   │   ├── RightsManager.cs
│   │   └── RightsRule.cs
│   ├── Sessions/
│   │   ├── AnonymousSession.cs
│   │   ├── ApiToken.cs
│   │   ├── SessionManager.cs
│   │   ├── TokenManager.cs
│   │   └── UserSession.cs
│   ├── Setup.cs
│   ├── TS3AudioBot.csproj
│   ├── TS3AudioBot.csproj.user
│   ├── Ts3Client.cs
│   ├── Upgrader.cs
│   ├── Web/
│   │   ├── Api/
│   │   │   ├── ApiCall.cs
│   │   │   ├── DataStream.cs
│   │   │   ├── JsonArray.cs
│   │   │   ├── JsonEmpty.cs
│   │   │   ├── JsonError.cs
│   │   │   ├── JsonObject.cs
│   │   │   ├── JsonValue.cs
│   │   │   ├── OpenApiGenerator.cs
│   │   │   ├── TimeSpanConverter.cs
│   │   │   └── WebApi.cs
│   │   ├── Model/
│   │   │   ├── CurrentSongInfo.cs
│   │   │   ├── PlaylistInfo.cs
│   │   │   ├── PlaylistItemGetData.cs
│   │   │   └── QueueInfo.cs
│   │   └── WebServer.cs
│   ├── build.csx
│   └── obj/
│       ├── Debug/
│       │   ├── TS3AudioBot.1.0.0.nuspec
│       │   ├── net7.0/
│       │   │   └── TS3AudioBot.csproj.AssemblyReference.cache
│       │   ├── net7.0-windows/
│       │   │   ├── .NETCoreApp,Version=v7.0.AssemblyAttributes.cs
│       │   │   ├── TS3AudioBot.AssemblyInfo.cs
│       │   │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │   │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TS3AudioBot.Localization.strings.resources
│       │   │   ├── TS3AudioBot.assets.cache
│       │   │   ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │   │   ├── TS3AudioBot.csproj.CopyComplete
│       │   │   ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │   │   ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │   │   ├── TS3AudioBot.csproj.GenerateResource.cache
│       │   │   ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │   │   ├── TS3AudioBot.dll.config
│       │   │   ├── TS3AudioBot.genruntimeconfig.cache
│       │   │   └── TS3AudioBot.pdb
│       │   └── netcoreapp3.1/
│       │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       ├── TS3AudioBot.AssemblyInfo.cs
│       │       ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TS3AudioBot.Localization.strings.resources
│       │       ├── TS3AudioBot.assets.cache
│       │       ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       ├── TS3AudioBot.csproj.CopyComplete
│       │       ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       ├── TS3AudioBot.dll.config
│       │       ├── TS3AudioBot.genruntimeconfig.cache
│       │       └── TS3AudioBot.pdb
│       ├── Release/
│       │   ├── net7.0-windows/
│       │   │   ├── .NETCoreApp,Version=v7.0.AssemblyAttributes.cs
│       │   │   ├── TS3AudioBot.AssemblyInfo.cs
│       │   │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │   │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TS3AudioBot.assets.cache
│       │   │   └── TS3AudioBot.csproj.AssemblyReference.cache
│       │   └── netcoreapp3.1/
│       │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       ├── PublishOutputs.25bc18e9a6.txt
│       │       ├── TS3AudioBot.AssemblyInfo.cs
│       │       ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TS3AudioBot.Localization.strings.resources
│       │       ├── TS3AudioBot.assets.cache
│       │       ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       ├── TS3AudioBot.csproj.CopyComplete
│       │       ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       ├── TS3AudioBot.dll.config
│       │       ├── TS3AudioBot.genruntimeconfig.cache
│       │       ├── TS3AudioBot.pdb
│       │       ├── linux-x64/
│       │       │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       │   ├── PublishOutputs.c54adf16a2.txt
│       │       │   ├── TS3AudioBot.AssemblyInfo.cs
│       │       │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       │   ├── TS3AudioBot.Localization.strings.resources
│       │       │   ├── TS3AudioBot.assets.cache
│       │       │   ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       │   ├── TS3AudioBot.csproj.CopyComplete
│       │       │   ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       │   ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       │   ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       │   ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       │   ├── TS3AudioBot.deps.json
│       │       │   ├── TS3AudioBot.dll.config
│       │       │   ├── TS3AudioBot.genruntimeconfig.cache
│       │       │   ├── TS3AudioBot.pdb
│       │       │   └── apphost
│       │       └── win-x64/
│       │           ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │           ├── PublishOutputs.cf05aea114.txt
│       │           ├── TS3AudioBot.AssemblyInfo.cs
│       │           ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │           ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │           ├── TS3AudioBot.Localization.strings.resources
│       │           ├── TS3AudioBot.assets.cache
│       │           ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │           ├── TS3AudioBot.csproj.CopyComplete
│       │           ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │           ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │           ├── TS3AudioBot.csproj.GenerateResource.cache
│       │           ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │           ├── TS3AudioBot.deps.json
│       │           ├── TS3AudioBot.dll.config
│       │           ├── TS3AudioBot.genruntimeconfig.cache
│       │           └── TS3AudioBot.pdb
│       ├── TS3AudioBot.csproj.nuget.dgspec.json
│       ├── TS3AudioBot.csproj.nuget.g.props
│       ├── TS3AudioBot.csproj.nuget.g.targets
│       ├── project.assets.json
│       ├── project.nuget.cache
│       └── publish/
│           ├── linux-x64/
│           │   ├── TS3AudioBot.csproj.nuget.dgspec.json
│           │   ├── TS3AudioBot.csproj.nuget.g.props
│           │   ├── TS3AudioBot.csproj.nuget.g.targets
│           │   ├── project.assets.json
│           │   └── project.nuget.cache
│           └── win-x64/
│               ├── TS3AudioBot.csproj.nuget.dgspec.json
│               ├── TS3AudioBot.csproj.nuget.g.props
│               ├── TS3AudioBot.csproj.nuget.g.targets
│               ├── project.assets.json
│               └── project.nuget.cache
├── TSLib/
│   ├── Audio/
│   │   ├── AudioInterfaces.cs
│   │   ├── AudioMeta.cs
│   │   ├── AudioPacketReader.cs
│   │   ├── AudioPipeExtensions.cs
│   │   ├── AudioTools.cs
│   │   ├── CheckActivePipe.cs
│   │   ├── ClientMixdown.cs
│   │   ├── DecoderPipe.cs
│   │   ├── EncoderPipe.cs
│   │   ├── Opus/
│   │   │   ├── LICENSE
│   │   │   ├── NativeMethods.cs
│   │   │   ├── OPUS_LICENSE
│   │   │   ├── OpusDecoder.cs
│   │   │   ├── OpusEncoder.cs
│   │   │   └── README
│   │   ├── PassiveMergePipe.cs
│   │   ├── PassiveSplitterPipe.cs
│   │   ├── PreciseAudioTimer.cs
│   │   ├── PreciseTimedPipe.cs
│   │   ├── StaticMetaPipe.cs
│   │   ├── StreamAudioProducer.cs
│   │   └── VolumePipe.cs
│   ├── Commands/
│   │   ├── CommandMultiParameter.cs
│   │   ├── CommandOption.cs
│   │   ├── CommandParameter.cs
│   │   ├── ICommandPart.cs
│   │   ├── TsCommand.cs
│   │   ├── TsCommand.gen.cs
│   │   ├── TsCommand.gen.tt
│   │   ├── TsConst.cs
│   │   └── TsString.cs
│   ├── ConnectionData.cs
│   ├── DisconnectEventArgs.cs
│   ├── EventDispatcher.cs
│   ├── Full/
│   │   ├── Book/
│   │   │   ├── Book.cs
│   │   │   └── SpecialTypes.cs
│   │   ├── GenerationWindow.cs
│   │   ├── IdentityData.cs
│   │   ├── License.cs
│   │   ├── NetworkStats.cs
│   │   ├── Packet.cs
│   │   ├── PacketHandler.cs
│   │   ├── PacketType.cs
│   │   ├── QuickerLz.cs
│   │   ├── RingQueue.cs
│   │   ├── TsCrypt.cs
│   │   ├── TsFullClient.cs
│   │   ├── TsFullClient.gen.cs
│   │   └── TsFullClient.gen.tt
│   ├── Generated/
│   │   ├── Book.cs
│   │   ├── Book.tt
│   │   ├── BookParser.ttinclude
│   │   ├── ErrorParser.ttinclude
│   │   ├── M2B.cs
│   │   ├── M2B.tt
│   │   ├── M2BParser.ttinclude
│   │   ├── MessageParser.ttinclude
│   │   ├── Messages.cs
│   │   ├── Messages.tt
│   │   ├── NotificationUtil.ttinclude
│   │   ├── TsErrorCode.cs
│   │   ├── TsErrorCode.tt
│   │   ├── TsPermission.cs
│   │   ├── TsPermission.tt
│   │   ├── TsVersion.gen.cs
│   │   ├── TsVersion.gen.tt
│   │   └── Util.ttinclude
│   ├── Helper/
│   │   ├── AsyncEventHandler.cs
│   │   ├── CommandErrorExtensions.cs
│   │   ├── DebugUtil.cs
│   │   ├── LogId.cs
│   │   ├── MissingEnumCaseException.cs
│   │   ├── NativeLibraryLoader.cs
│   │   ├── R.cs
│   │   ├── SpanExtensions.cs
│   │   ├── SpanSplitter.cs
│   │   └── Tools.cs
│   ├── LazyNotification.cs
│   ├── MessageProcessor.cs
│   ├── Messages/
│   │   ├── BaseTypes.cs
│   │   ├── Deserializer.cs
│   │   ├── MessageAdditions.cs
│   │   ├── PermissionTransform.cs
│   │   └── ResponseDictionary.cs
│   ├── Properties.cs
│   ├── Query/
│   │   ├── TsQueryClient.cs
│   │   ├── TsQueryClient.gen.cs
│   │   └── TsQueryClient.gen.tt
│   ├── Scheduler/
│   │   ├── DedicatedTaskScheduler.cs
│   │   ├── DispatcherHelper.cs
│   │   └── TickWorker.cs
│   ├── TSLib.csproj
│   ├── TsBaseFunctions.FileTransfer.cs
│   ├── TsBaseFunctions.cs
│   ├── TsBaseFunctions.gen.cs
│   ├── TsBaseFunctions.gen.tt
│   ├── TsDnsResolver.cs
│   ├── TsEnums.cs
│   ├── TsPermissionHelper.cs
│   ├── TsVersion.cs
│   ├── Types.cs
│   ├── Types.gen.cs
│   ├── Types.gen.tt
│   ├── WaitBlock.cs
│   ├── bin/
│   │   ├── Debug/
│   │   │   ├── netcoreapp3.1/
│   │   │   │   ├── TSLib.deps.json
│   │   │   │   └── TSLib.pdb
│   │   │   ├── netstandard2.0/
│   │   │   │   ├── TSLib.deps.json
│   │   │   │   └── TSLib.pdb
│   │   │   └── netstandard2.1/
│   │   │       ├── TSLib.deps.json
│   │   │       └── TSLib.pdb
│   │   └── Release/
│   │       ├── netcoreapp3.1/
│   │       │   ├── TSLib.deps.json
│   │       │   └── TSLib.pdb
│   │       ├── netstandard2.0/
│   │       │   ├── TSLib.deps.json
│   │       │   └── TSLib.pdb
│   │       └── netstandard2.1/
│   │           ├── TSLib.deps.json
│   │           └── TSLib.pdb
│   ├── dnc2_compat/
│   │   ├── Extensions.cs
│   │   ├── Range.cs
│   │   └── info.txt
│   └── obj/
│       ├── Debug/
│       │   ├── netcoreapp3.1/
│       │   │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   ├── netstandard2.0/
│       │   │   ├── .NETStandard,Version=v2.0.AssemblyAttributes.cs
│       │   │   ├── NuGet/
│       │   │   │   ├── 2C8E6E8C03FF0327F78E3CB90559803756F36314/
│       │   │   │   │   └── Nullable/
│       │   │   │   │       └── 1.2.1/
│       │   │   │   │           └── Nullable/
│       │   │   │   │               └── NullableAttributes.cs
│       │   │   │   └── 7BA94E4E53727142735FA3B08F79617CD03664FD/
│       │   │   │       └── Nullable/
│       │   │   │           └── 1.2.1/
│       │   │   │               └── Nullable/
│       │   │   │                   └── NullableAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   └── netstandard2.1/
│       │       ├── .NETStandard,Version=v2.1.AssemblyAttributes.cs
│       │       ├── TSLib.AssemblyInfo.cs
│       │       ├── TSLib.AssemblyInfoInputs.cache
│       │       ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TSLib.assets.cache
│       │       ├── TSLib.csproj.AssemblyReference.cache
│       │       ├── TSLib.csproj.CoreCompileInputs.cache
│       │       ├── TSLib.csproj.FileListAbsolute.txt
│       │       └── TSLib.pdb
│       ├── Release/
│       │   ├── netcoreapp3.1/
│       │   │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   ├── netstandard2.0/
│       │   │   ├── .NETStandard,Version=v2.0.AssemblyAttributes.cs
│       │   │   ├── NuGet/
│       │   │   │   ├── 2C8E6E8C03FF0327F78E3CB90559803756F36314/
│       │   │   │   │   └── Nullable/
│       │   │   │   │       └── 1.2.1/
│       │   │   │   │           └── Nullable/
│       │   │   │   │               └── NullableAttributes.cs
│       │   │   │   └── 7BA94E4E53727142735FA3B08F79617CD03664FD/
│       │   │   │       └── Nullable/
│       │   │   │           └── 1.2.1/
│       │   │   │               └── Nullable/
│       │   │   │                   └── NullableAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   └── netstandard2.1/
│       │       ├── .NETStandard,Version=v2.1.AssemblyAttributes.cs
│       │       ├── TSLib.AssemblyInfo.cs
│       │       ├── TSLib.AssemblyInfoInputs.cache
│       │       ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TSLib.assets.cache
│       │       ├── TSLib.csproj.AssemblyReference.cache
│       │       ├── TSLib.csproj.CoreCompileInputs.cache
│       │       ├── TSLib.csproj.FileListAbsolute.txt
│       │       └── TSLib.pdb
│       ├── TSLib.csproj.nuget.dgspec.json
│       ├── TSLib.csproj.nuget.g.props
│       ├── TSLib.csproj.nuget.g.targets
│       ├── project.assets.json
│       └── project.nuget.cache
├── YunBot.cs
└── obj/
    ├── ClassLibrary4.csproj.nuget.dgspec.json
    ├── ClassLibrary4.csproj.nuget.g.props
    ├── ClassLibrary4.csproj.nuget.g.targets
    ├── Debug/
    │   ├── netcoreapp3.0/
    │   │   ├── ClassLibrary4.assets.cache
    │   │   └── ClassLibrary4.csproj.FileListAbsolute.txt
    │   └── netcoreapp3.1/
    │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
    │       ├── ClassLibrary4.AssemblyInfo.cs
    │       ├── ClassLibrary4.AssemblyInfoInputs.cache
    │       ├── ClassLibrary4.GeneratedMSBuildEditorConfig.editorconfig
    │       ├── ClassLibrary4.assets.cache
    │       ├── ClassLibrary4.csproj.AssemblyReference.cache
    │       ├── ClassLibrary4.csproj.CopyComplete
    │       ├── ClassLibrary4.csproj.CoreCompileInputs.cache
    │       ├── ClassLibrary4.csproj.FileListAbsolute.txt
    │       └── YunBot.pdb
    ├── project.assets.json
    └── project.nuget.cache

================================================
FILE CONTENTS
================================================

================================================
FILE: ClassLibrary4.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AssemblyName>YunBot</AssemblyName>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="G:\TS3DEV\TS3AudioBot-master\TS3AudioBot\TS3AudioBot.csproj" />
    <ProjectReference Include="G:\TS3DEV\TS3AudioBot-master\TSLib\TSLib.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: ClassLibrary4.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.33927.289
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary4", "ClassLibrary4.csproj", "{D3D9037A-AD7B-4C9E-9FC0-CA330D8776BD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TSLib", "G:\TS3DEV\TS3AudioBot-master\TSLib\TSLib.csproj", "{66AF7F2B-43B7-41E0-982A-FCD7DA062BAA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TS3AudioBot", "G:\TS3DEV\TS3AudioBot-master\TS3AudioBot\TS3AudioBot.csproj", "{FFC15959-0725-4B2B-BF06-BFE8403C60A6}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{D3D9037A-AD7B-4C9E-9FC0-CA330D8776BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{D3D9037A-AD7B-4C9E-9FC0-CA330D8776BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{D3D9037A-AD7B-4C9E-9FC0-CA330D8776BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{D3D9037A-AD7B-4C9E-9FC0-CA330D8776BD}.Release|Any CPU.Build.0 = Release|Any CPU
		{66AF7F2B-43B7-41E0-982A-FCD7DA062BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{66AF7F2B-43B7-41E0-982A-FCD7DA062BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{66AF7F2B-43B7-41E0-982A-FCD7DA062BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{66AF7F2B-43B7-41E0-982A-FCD7DA062BAA}.Release|Any CPU.Build.0 = Release|Any CPU
		{FFC15959-0725-4B2B-BF06-BFE8403C60A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{FFC15959-0725-4B2B-BF06-BFE8403C60A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{FFC15959-0725-4B2B-BF06-BFE8403C60A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{FFC15959-0725-4B2B-BF06-BFE8403C60A6}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {86AB0697-D2EC-474E-8C8F-67F25BEDB10E}
	EndGlobalSection
EndGlobal


================================================
FILE: LICENSE
================================================
Open Software License ("OSL") v 3.0

This Open Software License (the "License") applies to any original work of
authorship (the "Original Work") whose owner (the "Licensor") has placed the
following licensing notice adjacent to the copyright notice for the Original
Work:

Licensed under the Open Software License version 3.0

1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, for the duration of the copyright, to do
the following:

  a) to reproduce the Original Work in copies, either alone or as part of a
  collective work;

  b) to translate, adapt, alter, transform, modify, or arrange the Original
  Work, thereby creating derivative works ("Derivative Works") based upon the
  Original Work;

  c) to distribute or communicate copies of the Original Work and Derivative
  Works to the public, with the proviso that copies of Original Work or
  Derivative Works that You distribute or communicate shall be licensed under
  this Open Software License;

  d) to perform the Original Work publicly; and

  e) to display the Original Work publicly.

2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or controlled
by the Licensor that are embodied in the Original Work as furnished by the
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
have made, and import the Original Work and Derivative Works.

3) Grant of Source Code License. The term "Source Code" means the preferred
form of the Original Work for making modifications to it and all available
documentation describing how to modify the Original Work. Licensor agrees to
provide a machine-readable copy of the Source Code of the Original Work along
with each copy of the Original Work that Licensor distributes. Licensor
reserves the right to satisfy this obligation by placing a machine-readable
copy of the Source Code in an information repository reasonably calculated to
permit inexpensive and convenient access by You for as long as Licensor
continues to distribute the Original Work.

4) Exclusions From License Grant. Neither the names of Licensor, nor the names
of any contributors to the Original Work, nor any of their trademarks or
service marks, may be used to endorse or promote products derived from this
Original Work without express prior permission of the Licensor. Except as
expressly stated herein, nothing in this License grants any license to
Licensor's trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.

5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone
other than You, whether those works are distributed or communicated to those
persons or made available as an application intended for use over a network.
As an express condition for the grants of license hereunder, You must treat
any External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).

6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and any
descriptive text identified therein as an "Attribution Notice." You must cause
the Source Code for any Derivative Works that You create to carry a prominent
Attribution Notice reasonably calculated to inform recipients that You have
modified the Original Work.

7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted herein
by Licensor are owned by the Licensor or are sublicensed to You under the
terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.

8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.

9) Acceptance and Termination. If, at any time, You expressly assented to this
License, that assent indicates your clear and irrevocable acceptance of this
License and all of its terms and conditions. If You distribute or communicate
copies of the Original Work or a Derivative Work, You must make a reasonable
effort under the circumstances to obtain the express assent of recipients to
the terms of this License. This License conditions your rights to undertake
the activities listed in Section 1, including your right to create Derivative
Works based upon the Original Work, and doing so without honoring these terms
and conditions is prohibited by copyright law and international treaty.
Nothing in this License is intended to affect copyright exceptions and
limitations (including "fair use" or "fair dealing"). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).

10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original Work
infringes a patent. This termination provision shall not apply for an action
alleging patent infringement by combinations of the Original Work with other
software or hardware.

11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.

12) Attorneys' Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.

13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.

14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.

15) Right to Use. You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.

16) Modification of This License. This License is Copyright © 2005 Lawrence
Rosen. Permission is granted to copy, distribute, or communicate this License
without modification. Nothing in this License permits You to modify this
License as applied to the Original Work or to Derivative Works. However, You
may modify the text of this License and copy, distribute or communicate your
modified version (the "Modified License") and apply it to other original works
of authorship subject to the following conditions: (i) You may not indicate in
any way that your Modified License is the "Open Software License" or "OSL" and
you may not use those names in the name of your Modified License; (ii) You
must replace the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your own
that is not confusingly similar to the notice in this License; and (iii) You
may not claim that your original works are open source software unless your
Modified License has been approved by Open Source Initiative (OSI) and You
comply with its license review and certification process.


================================================
FILE: README.md
================================================
# ⚠️ 本项目已停止更新和维护

**请使用最新的项目:[teamspeak-music-bot](https://github.com/ZHANGTIANYAO1/teamspeak-music-bot)**

本仓库将不再进行任何更新或维护,建议所有用户迁移到新项目。

---

# TS3AudioBot-NetEaseCloudmusic-plugin  
>此插件基于Splamy/TS3AudioBot项目 https://github.com/Splamy/TS3AudioBot   
>以及网易云音乐 API开发 [https://gitlab.com/Binaryify/neteasecloudmusicapi  ](https://www.npmjs.com/package/NeteaseCloudMusicApi)
此插件安装方法同样见TS3AudioBot项目wiki  
**2.0版本之后可以不需要本地部署网易云API了,但是强烈建议自行部署防止隐私泄露  **  
**最好给音乐机器人超管权限保证能正常更新头像和描述**

这是一个用C#给TS3AudioBot编写网易云插件,让你的TS可以有一个音乐机器人。如果觉得好的话,还请给个星星支持一下

## 关于API和机器人部署
目前网易云可能有什么改动,导致如果使用部署在海外的API将无法播放部分VIP歌曲,已经在ISSUE中添加新的国内公开API

推荐将API和机器人都部署在国内的服务器上

## 关于 DEV 版本
此版本基于 Splamy/TS3AudioBot 项目和 NeteaseCloudMusicApi 开发。DEV 版本由 @577fkj 大佬重构和增强,感谢他的贡献!

由于原开发环境的丢失,无法继续之前兼容 stable 版本的音乐机器人插件的开发。此 DEV 版本是在 @577fkj 的重构和增强版本基础上进一步开发的。

此版本新增了多项功能,包括查看播放列表、验证码登录、无人自动暂停、清除歌单、获取歌单最大长度限制、让机器人前往当前频道、播放专辑功能、私人 FM 模式等。

### 链接
- DEV 版本 GitHub 仓库:https://github.com/ZHANGTIANYAO1/TS3AudioBot-NetEaseCloudmusic-plugin/tree/DEV
- pre-release(测试版)下载链接:https://github.com/ZHANGTIANYAO1/TS3AudioBot-NetEaseCloudmusic-plugin/releases/tag/3.0.0

### 注意事项
- 此版本windows系统需要使用新的机器人程序,已经打包在pre-release中
- 虽然添加了第三方网易云API,但是为了大家的安全考虑,强烈建议自行部署API
- 需要使用最新版本的网易云API:https://gitlab.com/Binaryify/neteasecloudmusicapi
- 此版本在开发中,可能会有bug,遇到bug请提交issue,如果有想要添加的功能也可以提交issue
- 将机器人整合包的音质默认设置为了最高bitrate
- 权限文件修改为所有人有全部权限
- 使用版本过新的linux可能会有困难,比如使用Ubuntu(24,22)版本的需要自己去安装老的lib库,例子:(libicu70_70.1-2_amd64.deb libssl1.1_1.1.1f-1ubuntu2_amd64.deb)
- 推荐使用Ubuntu20

## 关于设置文件YunSettings.ini
`playMode=`是播放模式   
`WangYiYunAPI_Address`是网易云API地址,目前默认的是一个大佬的远程API,如果加载速度过慢或者无法访问,请自行部署API并修改API地址。(为了保护你的隐私强烈建议你自行部署API)   
`cookies1=`是保存在你本地的身份验证,通过二维码登录获取。(不需要修改)   

## 目前的指令:
正在播放的歌单的图片和名称可以点机器人看它的头像和描述  
vip音乐想要先登陆才能播放完整版本:(输入指令后扫描机器人头像二维码登陆)  
`!yun login`  

双击机器人,目前有以下指令(把[xxx]替换成对应信息,**包括中括号**)  
1.立即播放网易云音乐  
`!yun play [音乐名称]`  
  
2.添加音乐到下一首  
`!yun add [音乐名称]`  
  
3.播放网易云音乐歌单(如果提示Error: Nothing to play...重新输入指令解决)  
`!yun gedan [歌单名称]`  
  
4.播放网易云音乐歌单id  
`!yun gedanid [歌单名称]`  
  
5.立即播放网易云音乐id  
`!yun playid [歌单id]`  
  
6.添加指定音乐id到下一首  
`!yun add [音乐id]`  
  
7.播放列表中的下一首    
`!yun next`  

8.修改播放模式    
`!yun mode [模式选择数字0-3]`  
`0 = 顺序播放`
`1 = 顺序循环`
`2 = 随机播放`
`3 = 随机循环`

需要注意的是如果歌单歌曲过多需要时间加载,期间一定一定不要输入其他指令  

### TS频道描述(复制代码到频道描述)
```
[COLOR=#ff5500][B]正在播放的歌单的图片和名称可以点机器人看它的头像和描述[/B][/COLOR]
[COLOR=#aa00ff]机器人现在可以通过歌单播放vip音乐,如果遇到其他问题可以联系Github[/COLOR]

[COLOR=#0055ff]双击机器人,目前有以下指令([I]把[xxx]替换成对应信息,包括中括号[/I])[/COLOR]
1.立即播放网易云音乐
[COLOR=#00aa00]!yun play [音乐名称][/COLOR]
2.添加音乐到下一首
[COLOR=#00aa00]!yun add [音乐名称][/COLOR]
3.播放网易云音乐歌单
[COLOR=#00aa00]!yun gedan [歌单名称][/COLOR]
4.播放网易云音乐歌单id
[COLOR=#00aa00]!yun gedanid [歌单名称][/COLOR]
5.立即播放网易云音乐id
[COLOR=#00aa00]!yun playid [歌单id][/COLOR]
6.添加指定音乐id到下一首
[COLOR=#00aa00]!yun add [音乐id][/COLOR]
7.播放列表中的下一首
[COLOR=#00aa00]!yun next[/COLOR]
8.播放模式选择【0=顺序播放 1=顺序循环 2=随机 3=随即循环】
[COLOR=#00aa00]!yun mode[/COLOR]
9.登陆账户
[COLOR=#00aa00]!yun login[/COLOR]
[COLOR=#aaaaff]如果想要播放会员音乐需要先登陆会员账户,输入上述命令后扫描机器人头像的二维码登陆(只需要一账户登陆一次即可)[/COLOR]
需要注意的是如果歌单歌曲过多需要时间加载(重写后应该只需要几秒),期间[B]一定一定不要[/B]输入其他指令

以下例子加粗的就是音乐或者歌单id
[URL]https://music.163.com/#/my/m/music/playlist?id=[B]2139305008[I][/I][/B][/URL]

```

### 已知问题  
使用官方构建的ts3aduiobot会报错,因为官方的编译环境有问题。  
解决方法:1.自行构建 2.使用我打包的版本  

重复卸载加载插件会出现问题,如果需要重新加载请先重启Bot

Linux Docker无法正常显示description
解决方法:docker版本启用了web管理界面,在里面的Show song in bot description冲突了。关掉就可以正常显示了

#### 写在最后的话
本人完全不会C#,纯粹是自己花两天自学的,所以请各位大佬轻点喷我。  
还有很多地方需要完善以及很多功能可以添加,但是本人无法做出任何承诺  
同时欢迎各位来修改和完善这个插件  
最后的最后请不要轻易信任使用他人提供的公开服务,以免发生安全问题,泄露自己的账号和密码。如果决定使用默认的远程API请自行承担任何可能的后续风险。


================================================
FILE: TS3AudioBot/Algorithm/IFilterAlgorithm.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Linq;

namespace TS3AudioBot.Algorithm
{
	public interface IFilter
	{
		IEnumerable<KeyValuePair<string, T>> Filter<T>(IEnumerable<KeyValuePair<string, T>> list, string filter);
	}

	public static class Filter
	{
		public static IFilter DefaultFilter { get; } = Ic3Filter.Instance;

		public static IFilter? GetFilterByName(string filter)
		{
			return filter switch
			{
				"exact" => ExactFilter.Instance,
				"substring" => SubstringFilter.Instance,
				"ic3" => Ic3Filter.Instance,
				"hamming" => HammingFilter.Instance,
				_ => null,
			};
		}

		public static IFilter GetFilterByNameOrDefault(string filter) => GetFilterByName(filter) ?? DefaultFilter;
	}

	/// <summary>Interleaved continuous character chain.</summary>
	internal sealed class Ic3Filter : IFilter
	{
		private Ic3Filter() { }

		public static IFilter Instance { get; } = new Ic3Filter();

		IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<KeyValuePair<string, T>> list, string filter)
		{
			// Convert result to list because it can be enumerated multiple times
			var possibilities = list.Select(t => (Name: t.Key, t.Value, Index: 0)).ToList();
			// Filter matching commands
			foreach (var c in filter.ToLowerInvariant())
			{
				var newPossibilities = (from p in possibilities
										let pos = p.Name.ToLowerInvariant().IndexOf(c, p.Index)
										where pos != -1
										select (p.Name, p.Value, Index: pos + 1)).ToList();
				if (newPossibilities.Count > 0)
					possibilities = newPossibilities;
			}
			// Take command with lowest index
			int minIndex = possibilities.Min(t => t.Index);
			var cmds = possibilities.Where(t => t.Index == minIndex).ToArray();
			// Take the smallest command
			int minLength = cmds.Min(c => c.Name.Length);

			return cmds.Where(c => c.Name.Length == minLength).Select(fi => new KeyValuePair<string, T>(fi.Name, fi.Value));
		}
	}

	internal sealed class ExactFilter : IFilter
	{
		private ExactFilter() { }

		public static IFilter Instance { get; } = new ExactFilter();

		IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<KeyValuePair<string, T>> list, string filter)
		{
			return list.Where(x => x.Key == filter);
		}
	}

	internal sealed class HammingFilter : IFilter
	{
		private HammingFilter() { }

		public static IFilter Instance { get; } = new HammingFilter();

		IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<KeyValuePair<string, T>> list, string filter)
		{
			throw new System.NotImplementedException();
		}
	}

	internal sealed class SubstringFilter : IFilter
	{
		private SubstringFilter() { }

		public static IFilter Instance { get; } = new SubstringFilter();

		IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<KeyValuePair<string, T>> list, string filter)
		{
			var result = list.Where(x => x.Key.StartsWith(filter));
			using var enu = result.GetEnumerator();
			if (!enu.MoveNext())
				yield break;
			yield return enu.Current;
			if (enu.Current.Key == filter)
				yield break;
			while (enu.MoveNext())
				yield return enu.Current;
		}
	}
}


================================================
FILE: TS3AudioBot/Algorithm/LruCache.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace TS3AudioBot.Algorithm
{
	public class LruCache<TK, TV> where TK : notnull
	{
		private readonly int maxCapacity;
		private readonly Dictionary<TK, LinkedListNode<(TK key, TV value)>> cacheDict = new Dictionary<TK, LinkedListNode<(TK, TV)>>();
		private readonly LinkedList<(TK key, TV value)> lruList = new LinkedList<(TK, TV)>();

		public LruCache(int capacity)
		{
			maxCapacity = capacity;
		}

		public bool TryGetValue(TK key, [MaybeNullWhen(false)] out TV value)
		{
			if (cacheDict.TryGetValue(key, out var node))
			{
				Renew(node);
				value = node.Value.value;
				return true;
			}
			value = default!;
			return false;
		}

		public void Set(TK key, TV value)
		{
			if (cacheDict.TryGetValue(key, out var node))
			{
				Renew(node);
				node.Value = (node.Value.key, value);
				return;
			}

			if (cacheDict.Count >= maxCapacity)
				RemoveOldest();

			node = lruList.AddLast((key, value));
			cacheDict.Add(key, node);
		}

		public bool Remove(TK key) => cacheDict.Remove(key);

		private void Renew(LinkedListNode<(TK, TV)> node)
		{
			lruList.Remove(node);
			lruList.AddLast(node);
		}

		private void RemoveOldest()
		{
			var node = lruList.First;
			if (node is null)
				return;
			lruList.RemoveFirst();
			cacheDict.Remove(node.Value.key);
		}

		public void Clear()
		{
			cacheDict.Clear();
			lruList.Clear();
		}
	}
}


================================================
FILE: TS3AudioBot/Algorithm/TimedCache.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using TSLib.Helper;

namespace TS3AudioBot.Algorithm
{
	public class TimedCache<TK, TV> where TK : notnull
	{
		public TimeSpan Timeout { get; }
		private readonly ConcurrentDictionary<TK, TimedData> cachedData;

		public TimedCache() : this(TimeSpan.FromSeconds(3)) { }

		public TimedCache(TimeSpan timeout)
		{
			Timeout = timeout;
			cachedData = new ConcurrentDictionary<TK, TimedData>();
		}

		public bool TryGetValue(TK key, [MaybeNullWhen(false)] out TV value)
		{
			if (!cachedData.TryGetValue(key, out var data)
				|| Tools.Now - Timeout > data.Timestamp)
			{
				CleanCache();
				value = default!;
				return false;
			}
			value = data.Data;
			return true;
		}

		public void Set(TK key, TV value)
		{
			cachedData[key] = new TimedData { Data = value, Timestamp = Tools.Now };
		}

		public void Clear()
		{
			cachedData.Clear();
		}

		private void CleanCache()
		{
			var now = Tools.Now - Timeout;
			foreach (var item in cachedData.Where(kvp => now > kvp.Value.Timestamp).ToList())
			{
				cachedData.TryRemove(item.Key, out _);
			}
		}

		private struct TimedData
		{
			public TV Data;
			public DateTime Timestamp;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/AudioValues.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using TSLib.Helper;

namespace TS3AudioBot.Audio
{
	public static class AudioValues
	{
		public const float MinVolume = 0;
		public const float MaxVolume = 100;

		// Reference explanation for the logarithmic scale
		// https://www.dr-lex.be/info-stuff/volumecontrols.html#table1
		// Adjusted values for 40dB

		private const float fact_a = 1e-2f;
		private const float fact_b = 4.61512f;

		public static float HumanVolumeToFactor(float value)
		{
			if (value < MinVolume) return 0;
			if (value > MaxVolume) return 1;

			// Map input values from [MinVolume, MaxVolume] to [0, 1]
			value = (value - MinVolume) / (MaxVolume - MinVolume);

			// Scale the value logarithmically
			return Tools.Clamp((float)(fact_a * Math.Exp(fact_b * value)) - fact_a, 0, 1);
		}

		public static float FactorToHumanVolume(float value)
		{
			if (value < 0) return MinVolume;
			if (value > 1) return MaxVolume;

			// Undo logarithmical scale
			value = Tools.Clamp((float)(Math.Log((value + fact_a) / fact_a) / fact_b), 0, 1);

			// Map input values from [0, 1] to [MinVolume, MaxVolume]
			return (value * (MaxVolume - MinVolume)) + MinVolume;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/CustomTargetPipe.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using TSLib;
using TSLib.Audio;
using TSLib.Full;
using TSLib.Helper;

namespace TS3AudioBot.Audio
{
	internal class CustomTargetPipe : IVoiceTarget, IAudioPassiveConsumer
	{
		public TargetSendMode SendMode { get; set; } = TargetSendMode.Voice;
		public ulong GroupWhisperTargetId { get; private set; }
		public GroupWhisperType GroupWhisperType { get; private set; }
		public GroupWhisperTarget GroupWhisperTarget { get; private set; }
		public bool Alone { get; set; }

		public IReadOnlyCollection<ClientId> WhisperClients
		{
			get { lock (subscriptionLockObj) { return clientSubscriptionsSetup.ToArray(); } }
		}
		public IReadOnlyCollection<ChannelId> WhisperChannel
		{
			get { lock (subscriptionLockObj) { return channelSubscriptionsSetup.Keys.ToArray(); } }
		}

		public bool Active
		{
			get
			{
				switch (SendMode)
				{
				case TargetSendMode.None:
					return false;
				case TargetSendMode.Voice:
					return !Alone;
				case TargetSendMode.Whisper:
					UpdatedSubscriptionCache();
					return channelSubscriptionsCache.Length > 0 || clientSubscriptionsCache.Length > 0;
				default:
					return true;
				}
			}
		}

		private readonly Dictionary<ChannelId, bool> channelSubscriptionsSetup = new Dictionary<ChannelId, bool>();
		private readonly HashSet<ClientId> clientSubscriptionsSetup = new HashSet<ClientId>();
		private ChannelId[] channelSubscriptionsCache = Array.Empty<ChannelId>();
		private ClientId[] clientSubscriptionsCache = Array.Empty<ClientId>();
		private bool subscriptionSetupChanged;
		private readonly object subscriptionLockObj = new object();

		private readonly TsFullClient client;

		public CustomTargetPipe(TsFullClient client)
		{
			this.client = client;
			subscriptionSetupChanged = true;
		}

		public void Write(Span<byte> data, Meta? meta)
		{
			UpdatedSubscriptionCache();

			var codec = meta?.Codec ?? Codec.OpusMusic; // XXX a bit hacky
			switch (SendMode)
			{
			case TargetSendMode.None:
				break;
			case TargetSendMode.Voice:
				client.SendAudio(data, codec);
				break;
			case TargetSendMode.Whisper:
				client.SendAudioWhisper(data, codec, channelSubscriptionsCache, clientSubscriptionsCache);
				break;
			case TargetSendMode.WhisperGroup:
				client.SendAudioGroupWhisper(data, codec, GroupWhisperType, GroupWhisperTarget, GroupWhisperTargetId);
				break;
			default:
				throw Tools.UnhandledDefault(SendMode);
			}
		}

		#region ITargetManager

		public void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget target, ulong targetId = 0)
		{
			GroupWhisperType = type;
			GroupWhisperTarget = target;
			GroupWhisperTargetId = targetId;
		}

		public void WhisperChannelSubscribe(bool temp, params ChannelId[] channels)
		{
			lock (subscriptionLockObj)
			{
				foreach (var channel in channels)
				{
					if (channelSubscriptionsSetup.TryGetValue(channel, out var subscriptionTemp))
					{
						channelSubscriptionsSetup[channel] = !subscriptionTemp || !temp;
					}
					else
					{
						channelSubscriptionsSetup[channel] = !temp;
						subscriptionSetupChanged = true;
					}
				}
			}
		}

		public void WhisperChannelUnsubscribe(bool temp, params ChannelId[] channels)
		{
			lock (subscriptionLockObj)
			{
				foreach (var channel in channels)
				{
					if (!temp)
					{
						subscriptionSetupChanged |= channelSubscriptionsSetup.Remove(channel);
					}
					else
					{
						if (channelSubscriptionsSetup.TryGetValue(channel, out bool subscriptionTemp) && subscriptionTemp)
						{
							channelSubscriptionsSetup.Remove(channel);
							subscriptionSetupChanged = true;
						}
					}
				}
			}
		}

		public void WhisperClientSubscribe(params ClientId[] userId)
		{
			lock (subscriptionLockObj)
			{
				clientSubscriptionsSetup.UnionWith(userId);
				subscriptionSetupChanged = true;
			}
		}

		public void WhisperClientUnsubscribe(params ClientId[] userId)
		{
			lock (subscriptionLockObj)
			{
				clientSubscriptionsSetup.ExceptWith(userId);
				subscriptionSetupChanged = true;
			}
		}

		public void ClearTemporary()
		{
			lock (subscriptionLockObj)
			{
				var removeList = channelSubscriptionsSetup
					.Where(kvp => kvp.Value)
					.Select(kvp => kvp.Key)
					.ToArray();
				foreach (var chan in removeList)
				{
					channelSubscriptionsSetup.Remove(chan);
					subscriptionSetupChanged = true;
				}
			}
		}

		private void UpdatedSubscriptionCache()
		{
			if (!subscriptionSetupChanged)
				return;
			lock (subscriptionLockObj)
			{
				if (!subscriptionSetupChanged)
					return;
				channelSubscriptionsCache = channelSubscriptionsSetup.Keys.ToArray();
				clientSubscriptionsCache = clientSubscriptionsSetup.ToArray();
				subscriptionSetupChanged = false;
			}
		}

		#endregion
	}
}


================================================
FILE: TS3AudioBot/Audio/FfmpegProducer.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using TS3AudioBot.Config;
using TS3AudioBot.Helper;
using TSLib.Audio;
using TSLib.Helper;
using TSLib.Scheduler;

namespace TS3AudioBot.Audio
{
	public class FfmpegProducer : IPlayerSource, ISampleInfo, IDisposable
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
		private readonly Id id;
		private static readonly Regex FindDurationMatch = new Regex(@"^\s*Duration: (\d+):(\d\d):(\d\d).(\d\d)", Util.DefaultRegexConfig);
		private static readonly Regex IcyMetadataMacher = new Regex("((\\w+)='(.*?)';\\s*)+", Util.DefaultRegexConfig);
		private const string PreLinkConf = "-hide_banner -nostats -threads 1 -i \"";
		private const string PostLinkConf = "\" -ac 2 -ar 48000 -f s16le -acodec pcm_s16le pipe:1";
		private const string LinkConfIcy = "-hide_banner -nostats -threads 1 -i pipe:0 -ac 2 -ar 48000 -f s16le -acodec pcm_s16le pipe:1";
		private static readonly TimeSpan retryOnDropBeforeEnd = TimeSpan.FromSeconds(10);

		private readonly ConfToolsFfmpeg config;

		public event EventHandler? OnSongEnd;
		public event EventHandler<SongInfoChanged>? OnSongUpdated;

		private readonly DedicatedTaskScheduler scheduler;
		private FfmpegInstance? ffmpegInstance;

		public int SampleRate { get; } = 48000;
		public int Channels { get; } = 2;
		public int BitsPerSample { get; } = 16;

		public FfmpegProducer(ConfToolsFfmpeg config, DedicatedTaskScheduler scheduler, Id id)
		{
			this.config = config;
			this.scheduler = scheduler;
			this.id = id;
		}

		public Task AudioStart(string url, TimeSpan? startOff = null)
		{
			StartFfmpegProcess(url, startOff ?? TimeSpan.Zero);
			return Task.CompletedTask;
		}

		public async Task AudioStartIcy(string url) => await StartFfmpegProcessIcy(url);

		public void AudioStop()
		{
			StopFfmpegProcess();
		}

		public TimeSpan? Length => GetCurrentSongLength();

		public TimeSpan? Position => ffmpegInstance?.AudioTimer.SongPosition;

		public Task Seek(TimeSpan position) { SetPosition(position); return Task.CompletedTask; }

		public int Read(byte[] buffer, int offset, int length, out Meta? meta)
		{
			meta = default;
			int read;

			var instance = ffmpegInstance;

			if (instance is null)
				return 0;

			try
			{
				read = instance.FfmpegProcess.StandardOutput.BaseStream.Read(buffer, 0, length);
			}
			catch (Exception ex)
			{
				read = 0;
				Log.Debug(ex, "Can't read ffmpeg");
			}

			if (read == 0)
			{
				AssertNotMainScheduler();

				var (ret, triggerEndSafe) = instance.IsIcyStream
					? OnReadEmptyIcy(instance)
					: OnReadEmpty(instance);
				if (ret)
					return 0;

				if (instance.FfmpegProcess.HasExitedSafe())
				{
					Log.Trace("Ffmpeg has exited");
					AudioStop();
					triggerEndSafe = true;
				}

				if (triggerEndSafe)
				{
					OnSongEnd?.Invoke(this, EventArgs.Empty);
					return 0;
				}
			}

			instance.HasTriedToReconnect = false;
			instance.AudioTimer.PushBytes(read);
			return read;
		}

		private (bool ret, bool trigger) OnReadEmpty(FfmpegInstance instance)
		{
			if (instance.FfmpegProcess.HasExitedSafe() && !instance.HasTriedToReconnect)
			{
				var expectedStopLength = GetCurrentSongLength();
				Log.Trace("Expected song length {0}", expectedStopLength);
				if (expectedStopLength != TimeSpan.Zero)
				{
					var actualStopPosition = instance.AudioTimer.SongPosition;
					Log.Trace("Actual song position {0}", actualStopPosition);
					if (actualStopPosition + retryOnDropBeforeEnd < expectedStopLength)
					{
						Log.Debug("Connection to song lost, retrying at {0}", actualStopPosition);
						instance.HasTriedToReconnect = true;
						var newInstance = SetPosition(actualStopPosition);
						if (newInstance.Ok)
						{
							newInstance.Value.HasTriedToReconnect = true;
							return (true, false);
						}
						else
						{
							Log.Debug("Retry failed {0}", newInstance.Error);
							return (false, true);
						}
					}
				}
			}
			return (false, false);
		}

		private (bool ret, bool trigger) OnReadEmptyIcy(FfmpegInstance instance)
		{
			AssertNotMainScheduler();

			if (instance.FfmpegProcess.HasExitedSafe() && !instance.HasTriedToReconnect)
			{
				Log.Debug("Connection to stream lost, retrying...");
				instance.HasTriedToReconnect = true;
				var newInstance = StartFfmpegProcessIcy(instance.ReconnectUrl).Result;
				if (newInstance.Ok)
				{
					newInstance.Value.HasTriedToReconnect = true;
					return (true, false);
				}
				else
				{
					Log.Debug("Retry failed {0}", newInstance.Error);
					return (false, true);
				}
			}
			return (false, false);
		}

		private R<FfmpegInstance, string> SetPosition(TimeSpan value)
		{
			if (value < TimeSpan.Zero)
				throw new ArgumentOutOfRangeException(nameof(value));
			var instance = ffmpegInstance;
			if (instance is null)
				return "No instance running";
			if (instance.IsIcyStream)
				return "Cannot seek icy stream";
			var lastLink = instance.ReconnectUrl;
			if (lastLink is null)
				return "No current url active";
			return StartFfmpegProcess(lastLink, value);
		}

		private R<FfmpegInstance, string> StartFfmpegProcess(string url, TimeSpan? offsetOpt)
		{
			StopFfmpegProcess();
			Log.Trace("Start request {0}", url);

			string arguments;
			var offset = offsetOpt ?? TimeSpan.Zero;
			if (offset > TimeSpan.Zero)
			{
				var seek = string.Format(CultureInfo.InvariantCulture, @"-ss {0:hh\:mm\:ss\.fff}", offset);
				arguments = string.Concat(seek, " ", PreLinkConf, url, PostLinkConf, " ", seek);
			}
			else
			{
				arguments = string.Concat(PreLinkConf, url, PostLinkConf);
			}

			var newInstance = new FfmpegInstance(
				url,
				new PreciseAudioTimer(this)
				{
					SongPositionOffset = offset,
				});

			return StartFfmpegProcessInternal(newInstance, arguments);
		}

		private async Task<R<FfmpegInstance, string>> StartFfmpegProcessIcy(string url)
		{
			StopFfmpegProcess();
			Log.Trace("Start icy-stream request {0}", url);

			try
			{
				var response = await WebWrapper
					.Request(url)
					.WithHeader("Icy-MetaData", "1")
					.UnsafeResponse();

				if (!int.TryParse(response.Headers.GetSingle("icy-metaint"), out var metaint))
				{
					response.Dispose();
					return "Invalid icy stream tags";
				}

				var stream = await response.Content.ReadAsStreamAsync();
				var newInstance = new FfmpegInstance(
					url,
					new PreciseAudioTimer(this),
					stream,
					metaint)
				{
					OnMetaUpdated = e => OnSongUpdated?.Invoke(this, e)
				};

				new Thread(() => newInstance.ReadStreamLoop(id))
				{
					Name = $"IcyStreamReader[{id}]",
				}.Start();

				return StartFfmpegProcessInternal(newInstance, LinkConfIcy);
			}
			catch (Exception ex)
			{
				var error = $"Unable to create icy-stream ({ex.Message})";
				Log.Warn(ex, error);
				return error;
			}
		}

		private R<FfmpegInstance, string> StartFfmpegProcessInternal(FfmpegInstance instance, string arguments)
		{
			try
			{
				instance.FfmpegProcess.StartInfo = new ProcessStartInfo
				{
					FileName = config.Path.Value,
					Arguments = arguments,
					RedirectStandardOutput = true,
					RedirectStandardInput = true,
					RedirectStandardError = true,
					UseShellExecute = false,
					CreateNoWindow = true,
				};
				instance.FfmpegProcess.EnableRaisingEvents = true;

				Log.Debug("Starting ffmpeg with {0}", arguments);
				instance.FfmpegProcess.ErrorDataReceived += instance.FfmpegProcess_ErrorDataReceived;
				instance.FfmpegProcess.Start();
				instance.FfmpegProcess.BeginErrorReadLine();

				instance.AudioTimer.Start();

				var oldInstance = Interlocked.Exchange(ref ffmpegInstance, instance);
				oldInstance?.Close();

				return instance;
			}
			catch (Exception ex)
			{
				var error = ex is Win32Exception
					? $"Ffmpeg could not be found ({ex.Message})"
					: $"Unable to create stream ({ex.Message})";
				Log.Error(ex, error);
				instance.Close();
				StopFfmpegProcess();
				return error;
			}
		}

		private void StopFfmpegProcess()
		{
			var oldInstance = Interlocked.Exchange(ref ffmpegInstance, null);
			if (oldInstance != null)
			{
				oldInstance.OnMetaUpdated = null;
				oldInstance.Close();
			}
		}

		private TimeSpan? GetCurrentSongLength() => ffmpegInstance?.ParsedSongLength;

		private void AssertNotMainScheduler()
		{
			if (TaskScheduler.Current == scheduler)
				throw new Exception("Cannot read on own scheduler. Throwing to prevent deadlock");
		}

		public void Dispose()
		{
			StopFfmpegProcess();
		}

		private class FfmpegInstance
		{
			public Process FfmpegProcess { get; }
			public bool HasTriedToReconnect { get; set; }
			public string ReconnectUrl { get; }
			public bool IsIcyStream => IcyStream != null;

			public PreciseAudioTimer AudioTimer { get; }
			public TimeSpan? ParsedSongLength { get; set; } = null;

			public Stream? IcyStream { get; }
			public int IcyMetaInt { get; }
			public bool Closed { get; set; }

			public Action<SongInfoChanged>? OnMetaUpdated;

			public FfmpegInstance(string url, PreciseAudioTimer timer) : this(url, timer, null!, 0) { }
			public FfmpegInstance(string url, PreciseAudioTimer timer, Stream icyStream, int icyMetaInt)
			{
				FfmpegProcess = new Process();
				ReconnectUrl = url;
				AudioTimer = timer;
				IcyStream = icyStream;
				IcyMetaInt = icyMetaInt;

				HasTriedToReconnect = false;
			}

			public void Close()
			{
				Closed = true;

				try
				{
					if (!FfmpegProcess.HasExitedSafe())
						FfmpegProcess.Kill();
				}
				catch { }
				try { FfmpegProcess.CancelErrorRead(); } catch { }
				try { FfmpegProcess.StandardInput.Dispose(); } catch { }
				try { FfmpegProcess.StandardOutput.Dispose(); } catch { }
				try { FfmpegProcess.Dispose(); } catch { }

				IcyStream?.Dispose();
			}

			public void FfmpegProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
			{
				if (e.Data is null)
					return;

				if (sender != FfmpegProcess)
					throw new InvalidOperationException("Wrong process associated to event");

				if (ParsedSongLength is null)
				{
					var match = FindDurationMatch.Match(e.Data);
					if (!match.Success)
						return;

					int hours = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
					int minutes = int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
					int seconds = int.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture);
					int millisec = int.Parse(match.Groups[4].Value, CultureInfo.InvariantCulture) * 10;
					ParsedSongLength = new TimeSpan(0, hours, minutes, seconds, millisec);
				}

				//if (!HasIcyTag && e.Data.AsSpan().TrimStart().StartsWith("icy-".AsSpan()))
				//{
				//	HasIcyTag = true;
				//}
			}

			public void ReadStreamLoop(Id id)
			{
				if (IcyStream is null)
					throw new InvalidOperationException("Instance is not an icy stream");

				Tools.SetLogId(id.ToString());
				const int IcyMaxMeta = 255 * 16;
				const int ReadBufferSize = 4096;

				int errorCount = 0;
				var buffer = new byte[Math.Max(ReadBufferSize, IcyMaxMeta)];
				int readCount = 0;

				while (!Closed)
				{
					try
					{
						while (readCount < IcyMetaInt)
						{
							int read = IcyStream.Read(buffer, 0, Math.Min(ReadBufferSize, IcyMetaInt - readCount));
							if (read == 0)
							{
								Close();
								return;
							}
							readCount += read;
							FfmpegProcess.StandardInput.BaseStream.Write(buffer, 0, read);
							errorCount = 0;
						}
						readCount = 0;

						var metaByte = IcyStream.ReadByte();
						if (metaByte < 0)
						{
							Close();
							return;
						}

						if (metaByte > 0)
						{
							metaByte *= 16;
							while (readCount < metaByte)
							{
								int read = IcyStream.Read(buffer, 0, metaByte - readCount);
								if (read == 0)
								{
									Close();
									return;
								}
								readCount += read;
							}
							readCount = 0;

							var metaString = Tools.Utf8Encoder.GetString(buffer, 0, metaByte).TrimEnd('\0');
							Log.Debug("Meta: {0}", metaString);
							OnMetaUpdated?.Invoke(ParseIcyMeta(metaString));
						}
					}
					catch (Exception ex)
					{
						errorCount++;
						if (errorCount >= 50)
						{
							Log.Error(ex, "Failed too many times trying to access ffmpeg. Closing stream.");
							Close();
							return;
						}

						if (ex is InvalidOperationException)
						{
							Log.Debug(ex, "Waiting for ffmpeg");
							Thread.Sleep(100);
						}
						else
						{
							Log.Debug(ex, "Stream read/write error");
						}
					}
				}
			}

			private static SongInfoChanged ParseIcyMeta(string metaString)
			{
				var songInfo = new SongInfoChanged();
				var match = IcyMetadataMacher.Match(metaString);
				if (match.Success)
				{
					for (int i = 0; i < match.Groups[1].Captures.Count; i++)
					{
						switch (match.Groups[2].Captures[i].Value.ToUpperInvariant())
						{
						case "STREAMTITLE":
							songInfo.Title = match.Groups[3].Captures[i].Value;
							break;
						}
					}
				}
				return songInfo;
			}
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/IPlayerSource.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Threading.Tasks;
using TSLib.Audio;

namespace TS3AudioBot.Audio
{
	public interface IPlayerSource : IAudioPassiveProducer
	{
		event EventHandler OnSongEnd;
		event EventHandler<SongInfoChanged> OnSongUpdated;

		TimeSpan? Length { get; }
		TimeSpan? Position { get; }

		Task Seek(TimeSpan position);
	}
}


================================================
FILE: TS3AudioBot/Audio/IVoiceTarget.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using TSLib;
using TSLib.Audio;

namespace TS3AudioBot.Audio
{
	/// <summary>Used to specify playing mode and active targets to send to.</summary>
	public interface IVoiceTarget
	{
		TargetSendMode SendMode { get; set; }
		ulong GroupWhisperTargetId { get; }
		GroupWhisperType GroupWhisperType { get; }
		GroupWhisperTarget GroupWhisperTarget { get; }
		void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget target, ulong targetId);

		IReadOnlyCollection<ClientId> WhisperClients { get; }
		IReadOnlyCollection<ChannelId> WhisperChannel { get; }

		/// <summary>Adds a channel to the audio streaming list.</summary>
		/// <param name="temp">When set to true this channel will be cleared with
		/// the next <see cref="ClearTemporary"/> call (unless overwritten with false).</param>
		/// <param name="channel">The id of the channel.</param>
		void WhisperChannelSubscribe(bool temp, params ChannelId[] channel);
		/// <summary>Removes a channel from the audio streaming list.</summary>
		/// <param name="temp">When set to true this channel will be cleared with
		/// the next <see cref="ClearTemporary"/> call (unless overwritten with false).</param>
		/// <param name="channel">The id of the channel.</param>
		void WhisperChannelUnsubscribe(bool temp, params ChannelId[] channel);
		void ClearTemporary();
		void WhisperClientSubscribe(params ClientId[] userId);
		void WhisperClientUnsubscribe(params ClientId[] userId);
	}
}


================================================
FILE: TS3AudioBot/Audio/PlayInfo.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Diagnostics.CodeAnalysis;
using TSLib;

namespace TS3AudioBot.Audio
{
	public sealed class PlayInfo
	{
		/// <summary>Defaults to: invoker.Uid - Can be set if the owner of a song differs from the invoker.</summary>
		public Uid? ResourceOwnerUid { get; set; }
		/// <summary>Starts the song at the specified time if set.</summary>
		public TimeSpan? StartOffset { get; set; }

		public PlayInfo(TimeSpan? startOffset = null)
		{
			StartOffset = startOffset;
		}

		public PlayInfo Merge(PlayInfo other) => Merge(this, other);

		[return: NotNullIfNotNull("self")]
		[return: NotNullIfNotNull("other")]
		public static PlayInfo? Merge(PlayInfo? self, PlayInfo? other)
		{
			if (other is null)
				return self;
			if (self is null)
				return other;
			self.ResourceOwnerUid ??= other.ResourceOwnerUid;
			self.StartOffset ??= other.StartOffset;
			return self;
		}

		public static PlayInfo MergeDefault(PlayInfo? self, PlayInfo? other)
			=> Merge(self, other) ?? new PlayInfo();
	}

	public interface IMetaContainer
	{
		public PlayInfo? PlayInfo { get; set; }
	}

	public static class MetaContainerExtensions
	{
		public static T MergeMeta<T>(this T container, PlayInfo? other) where T : IMetaContainer
		{
			container.PlayInfo = PlayInfo.Merge(container.PlayInfo, other);
			return container;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/PlayInfoEventArgs.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using TS3AudioBot.ResourceFactories;

namespace TS3AudioBot.Audio
{
	public sealed class PlayInfoEventArgs : EventArgs
	{
		public InvokerData Invoker { get; }
		public PlayResource PlayResource { get; }
		public AudioResource ResourceData => PlayResource.AudioResource;
		public PlayInfo? PlayInfo => PlayResource.PlayInfo;
		public string? SourceLink { get; }

		public PlayInfoEventArgs(InvokerData invoker, PlayResource playResource, string? sourceLink)
		{
			Invoker = invoker;
			PlayResource = playResource;
			SourceLink = sourceLink;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/PlayManager.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Config;
using TS3AudioBot.Environment;
using TS3AudioBot.Helper;
using TS3AudioBot.Localization;
using TS3AudioBot.Playlists;
using TS3AudioBot.ResourceFactories;
using TSLib.Helper;

namespace TS3AudioBot.Audio
{
	/// <summary>Provides a convenient inferface for enqueing, playing and registering song events.</summary>
	public class PlayManager
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

		private readonly ConfBot confBot;
		private readonly Player playerConnection;
		private readonly PlaylistManager playlistManager;
		private readonly ResolveContext resourceResolver;
		private readonly Stats stats;

		public PlayInfoEventArgs? CurrentPlayData { get; private set; }
		public bool IsPlaying => CurrentPlayData != null;

		public event AsyncEventHandler<PlayInfoEventArgs>? OnResourceUpdated;
		public event AsyncEventHandler<PlayInfoEventArgs>? BeforeResourceStarted;
		public event AsyncEventHandler<PlayInfoEventArgs>? AfterResourceStarted;
		public event AsyncEventHandler<SongEndEventArgs>? ResourceStopped;
		public event AsyncEventHandler? PlaybackStopped;

		public PlayManager(ConfBot config, Player playerConnection, PlaylistManager playlistManager, ResolveContext resourceResolver, Stats stats)
		{
			confBot = config;
			this.playerConnection = playerConnection;
			this.playlistManager = playlistManager;
			this.resourceResolver = resourceResolver;
			this.stats = stats;
		}

		public Task Enqueue(InvokerData invoker, AudioResource ar, PlayInfo? meta = null) => Enqueue(invoker, new PlaylistItem(ar, meta));
		public async Task Enqueue(InvokerData invoker, string message, string? audioType = null, PlayInfo? meta = null)
		{
			PlayResource playResource;
			try { playResource = await resourceResolver.Load(message, audioType); }
			catch
			{
				stats.TrackSongLoad(audioType, false, true);
				throw;
			}
			await Enqueue(invoker, PlaylistItem.From(playResource).MergeMeta(meta));
		}
		public Task Enqueue(InvokerData invoker, IEnumerable<PlaylistItem> items)
		{
			var startOff = playlistManager.CurrentList.Items.Count;
			playlistManager.Queue(items.Select(x => UpdateItem(invoker, x)));
			return PostEnqueue(invoker, startOff);
		}
		public Task Enqueue(InvokerData invoker, PlaylistItem item)
		{
			var startOff = playlistManager.CurrentList.Items.Count;
			playlistManager.Queue(UpdateItem(invoker, item));
			return PostEnqueue(invoker, startOff);
		}

		private static PlaylistItem UpdateItem(InvokerData invoker, PlaylistItem item)
		{
			item.PlayInfo ??= new PlayInfo();
			item.PlayInfo.ResourceOwnerUid = invoker.ClientUid;
			return item;
		}

		private async Task PostEnqueue(InvokerData invoker, int startIndex)
		{
			if (IsPlaying)
				return;
			playlistManager.Index = startIndex;
			await StartCurrent(invoker);
		}

		/// <summary>Tries to play the passed <see cref="AudioResource"/></summary>
		/// <param name="invoker">The invoker of this resource. Used for responses and association.</param>
		/// <param name="ar">The resource to load and play.</param>
		/// <param name="meta">Allows overriding certain settings for the resource. Can be null.</param>
		/// <returns>Ok if successful, or an error message otherwise.</returns>
		public async Task Play(InvokerData invoker, AudioResource ar, PlayInfo? meta = null)
		{
			if (ar is null)
				throw new ArgumentNullException(nameof(ar));

			PlayResource playResource;
			try { playResource = await resourceResolver.Load(ar); }
			catch
			{
				stats.TrackSongLoad(ar.AudioType, false, true);
				throw;
			}
			await Play(invoker, playResource.MergeMeta(meta));
		}

		/// <summary>Tries to play the passed link.</summary>
		/// <param name="invoker">The invoker of this resource. Used for responses and association.</param>
		/// <param name="link">The link to resolve, load and play.</param>
		/// <param name="audioType">The associated resource type string to a factory.</param>
		/// <param name="meta">Allows overriding certain settings for the resource. Can be null.</param>
		/// <returns>Ok if successful, or an error message otherwise.</returns>
		public async Task Play(InvokerData invoker, string link, string? audioType = null, PlayInfo? meta = null)
		{
			PlayResource playResource;
			try { playResource = await resourceResolver.Load(link, audioType); }
			catch
			{
				stats.TrackSongLoad(audioType, false, true);
				throw;
			}
			await Play(invoker, playResource.MergeMeta(meta));
		}

		public Task Play(InvokerData invoker, IEnumerable<PlaylistItem> items, int index = 0)
		{
			playlistManager.Clear();
			playlistManager.Queue(items.Select(x => UpdateItem(invoker, x)));
			playlistManager.Index = index;
			return StartCurrent(invoker);
		}

		public Task Play(InvokerData invoker, PlaylistItem item)
		{
			if (item is null)
				throw new ArgumentNullException(nameof(item));

			if (item.AudioResource is null)
				throw new Exception("Invalid playlist item");
			playlistManager.Clear();
			playlistManager.Queue(item);
			playlistManager.Index = 0;
			return StartResource(invoker, item);
		}

		public Task Play(InvokerData invoker) => StartCurrent(invoker);

		/// <summary>Plays the passed <see cref="PlayResource"/></summary>
		/// <param name="invoker">The invoker of this resource. Used for responses and association.</param>
		/// <param name="play">The associated resource type string to a factory.</param>
		/// <param name="meta">Allows overriding certain settings for the resource.</param>
		/// <returns>Ok if successful, or an error message otherwise.</returns>
		public Task Play(InvokerData invoker, PlayResource play)
		{
			playlistManager.Clear();
			playlistManager.Queue(PlaylistItem.From(play));
			playlistManager.Index = 0;
			stats.TrackSongLoad(play.AudioResource.AudioType, true, true);
			return StartResource(invoker, play);
		}

		private async Task StartResource(InvokerData invoker, PlaylistItem item)
		{
			PlayResource playResource;
			try { playResource = await resourceResolver.Load(item.AudioResource); }
			catch
			{
				stats.TrackSongLoad(item.AudioResource.AudioType, false, false);
				throw;
			}
			stats.TrackSongLoad(item.AudioResource.AudioType, true, false);
			await StartResource(invoker, playResource.MergeMeta(item.PlayInfo));
		}

		private async Task StartResource(InvokerData invoker, PlayResource play)
		{
			var sourceLink = resourceResolver.RestoreLink(play.AudioResource);
			var playInfo = new PlayInfoEventArgs(invoker, play, sourceLink);
			await BeforeResourceStarted.InvokeAsync(this, playInfo);

			if (string.IsNullOrWhiteSpace(play.PlayUri))
			{
				Log.Error("Internal resource error: link is empty (resource:{0})", play);
				throw Error.LocalStr(strings.error_playmgr_internal_error);
			}

			Log.Debug("AudioResource start: {0}", play);
			try { await playerConnection.Play(play); }
			catch (AudioBotException ex)
			{
				Log.Error("Error return from player: {0}", ex.Message);
				throw Error.Exception(ex).LocalStr(strings.error_playmgr_internal_error);
			}

			playerConnection.Volume = Tools.Clamp(playerConnection.Volume, confBot.Audio.Volume.Min, confBot.Audio.Volume.Max);
			CurrentPlayData = playInfo; // TODO meta as readonly
			await AfterResourceStarted.InvokeAsync(this, playInfo);
		}

		private async Task StartCurrent(InvokerData invoker, bool manually = true)
		{
			var pli = playlistManager.Current;
			if (pli is null)
				throw Error.LocalStr(strings.error_playlist_is_empty);
			try
			{
				await StartResource(invoker, pli);
			}
			catch (AudioBotException ex)
			{
				Log.Warn("Skipping: {0} because {1}", pli, ex.Message);
				await Next(invoker, manually);
			}
		}

		public async Task Next(InvokerData invoker, bool manually = true)
		{
			PlaylistItem? pli = null;
			for (int i = 0; i < 10; i++)
			{
				pli = playlistManager.Next(manually);
				if (pli is null) break;
				try
				{
					await StartResource(invoker, pli);
					return;
				}
				catch (AudioBotException ex) { Log.Warn("Skipping: {0} because {1}", pli, ex.Message); }
			}
			if (pli is null)
				throw Error.LocalStr(strings.info_playmgr_no_next_song);
			else
				throw Error.LocalStr(string.Format(strings.error_playmgr_many_songs_failed, "!next"));
		}

		public async Task Previous(InvokerData invoker, bool manually = true)
		{
			PlaylistItem? pli = null;
			for (int i = 0; i < 10; i++)
			{
				pli = playlistManager.Previous(manually);
				if (pli is null) break;
				try
				{
					await StartResource(invoker, pli);
					return;
				}
				catch (AudioBotException ex) { Log.Warn("Skipping: {0} because {1}", pli, ex.Message); }
			}
			if (pli is null)
				throw Error.LocalStr(strings.info_playmgr_no_previous_song);
			else
				throw Error.LocalStr(string.Format(strings.error_playmgr_many_songs_failed, "!previous"));
		}

		public async Task SongStoppedEvent(object? sender, EventArgs e) => await StopInternal(true);

		public Task Stop() => StopInternal(false);

		private async Task StopInternal(bool songEndedByCallback)
		{
			await ResourceStopped.InvokeAsync(this, new SongEndEventArgs(songEndedByCallback));

			if (songEndedByCallback)
			{
				try
				{
					await Next(CurrentPlayData?.Invoker ?? InvokerData.Anonymous, false);
					return;
				}
				catch (AudioBotException ex) { Log.Info("Song queue ended: {0}", ex.Message); }
			}
			else
			{
				playerConnection.Stop();
			}

			CurrentPlayData = null;
			PlaybackStopped?.Invoke(this, EventArgs.Empty);
		}

		public async Task Update(SongInfoChanged newInfo)
		{
			var data = CurrentPlayData;
			if (data is null)
				return;
			if (newInfo.Title != null)
				data.ResourceData.ResourceTitle = newInfo.Title;
			// further properties...
			try
			{
				await OnResourceUpdated.InvokeAsync(this, data);
			}
			catch (AudioBotException ex)
			{
				Log.Warn(ex, "Error in OnResourceUpdated event.");
			}
		}

		public static PlayInfo? ParseAttributes(string[] attrs)
		{
			if (attrs is null || attrs.Length == 0)
				return null;

			var meta = new PlayInfo();
			foreach (var attr in attrs)
			{
				if (attr.StartsWith("@"))
				{
					meta.StartOffset = TextUtil.ParseTime(attr.Substring(1));
				}
			}
			return meta;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/Player.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Threading.Tasks;
using TS3AudioBot.Config;
using TS3AudioBot.Helper;
using TS3AudioBot.ResourceFactories;
using TSLib;
using TSLib.Audio;
using TSLib.Helper;
using TSLib.Scheduler;

namespace TS3AudioBot.Audio
{
	public class Player : IDisposable
	{
		private const Codec SendCodec = Codec.OpusMusic;
		private readonly DedicatedTaskScheduler scheduler;

		public IPlayerSource? CurrentPlayerSource { get; private set; }
		public StallCheckPipe StallCheckPipe { get; }
		public VolumePipe VolumePipe { get; }
		public FfmpegProducer FfmpegProducer { get; }
		public PreciseTimedPipe TimePipe { get; }
		public PassiveMergePipe MergePipe { get; }
		public EncoderPipe EncoderPipe { get; }
		public IAudioPassiveConsumer? PlayerSink { get; private set; }

		public Player(ConfRoot confRoot, ConfBot config, DedicatedTaskScheduler scheduler, Id id)
		{
			this.scheduler = scheduler;

			FfmpegProducer = new FfmpegProducer(confRoot.Tools.Ffmpeg, scheduler, id);
			StallCheckPipe = new StallCheckPipe();
			VolumePipe = new VolumePipe();
			Volume = config.Audio.Volume.Default;
			EncoderPipe = new EncoderPipe(SendCodec) { Bitrate = ScaleBitrate(config.Audio.Bitrate) };
			TimePipe = new PreciseTimedPipe(EncoderPipe, id) { ReadBufferSize = EncoderPipe.PacketSize };
			MergePipe = new PassiveMergePipe();

			config.Audio.Bitrate.Changed += (s, e) => EncoderPipe.Bitrate = ScaleBitrate(e.NewValue);

			MergePipe.Into(TimePipe).Chain<CheckActivePipe>().Chain(StallCheckPipe).Chain(VolumePipe).Chain(EncoderPipe);
		}

		public void SetTarget(IAudioPassiveConsumer target)
		{
			PlayerSink = target;
			EncoderPipe.Chain(target);
		}

		private static int ScaleBitrate(int value) => Tools.Clamp(value, 1, 255) * 1000;

		public event AsyncEventHandler? OnSongEnd;
		public event AsyncEventHandler<SongInfoChanged>? OnSongUpdated;

		private void TriggerSongEnd(object? o, EventArgs e) => scheduler.InvokeAsync(() => OnSongEnd.InvokeAsync(this, EventArgs.Empty));
		private void TriggerSongUpdated(object? o, SongInfoChanged e) => scheduler.InvokeAsync(() => OnSongUpdated.InvokeAsync(this, e));

		public async Task Play(PlayResource res)
		{
			if (res is MediaPlayResource mres && mres.IsIcyStream)
				await FfmpegProducer.AudioStartIcy(res.PlayUri);
			else
				await FfmpegProducer.AudioStart(res.PlayUri, res.PlayInfo?.StartOffset);
			Play(FfmpegProducer);
		}

		public void Play(IPlayerSource source)
		{
			var oldSource = CurrentPlayerSource;
			if (oldSource != source)
			{
				// Clean up old
				CleanSource(oldSource);
				// Set events
				source.OnSongEnd += TriggerSongEnd;
				source.OnSongUpdated += TriggerSongUpdated;
				// Update pipes
				MergePipe.Add(source);
				CurrentPlayerSource = source;
			}
			// Start Ticker
			TimePipe.AudioTimer.Reset();
			TimePipe.Paused = false;
		}

		private void CleanSource(IPlayerSource? source)
		{
			if (source is null)
				return;
			source.OnSongEnd -= TriggerSongEnd;
			source.OnSongUpdated -= TriggerSongUpdated;
			MergePipe.Remove(source);
			source.Dispose();
		}

		public void Stop()
		{
			CurrentPlayerSource?.Dispose();
			if (MergePipe.Count <= 1)
				TimePipe.Paused = true;
		}

		public void StopAll()
		{
			Stop();
			TimePipe.Paused = true;
			MergePipe.Dispose();
		}

		public TimeSpan? Length => CurrentPlayerSource?.Length;

		public TimeSpan? Position => CurrentPlayerSource?.Position;

		public Task Seek(TimeSpan position) => CurrentPlayerSource?.Seek(position) ?? Task.CompletedTask;

		public float Volume
		{
			get => AudioValues.FactorToHumanVolume(VolumePipe.Volume);
			set => VolumePipe.Volume = AudioValues.HumanVolumeToFactor(value);
		}

		public bool Paused
		{
			get => TimePipe.Paused;
			set => TimePipe.Paused = value;
		}

		// Extras

		public void SetStall() => StallCheckPipe.SetStall();

		[Obsolete(AttributeStrings.UnderDevelopment)]
		public void MixInStreamOnce(IPlayerSource producer)
		{
			producer.OnSongEnd += (s, e) =>
			{
				MergePipe.Remove(producer);
				producer.Dispose();
			};
			MergePipe.Add(producer);
			TimePipe.Paused = false;
		}

		public void Dispose()
		{
			StopAll();
			CleanSource(CurrentPlayerSource);
			TimePipe.Dispose();
			FfmpegProducer.Dispose();
			EncoderPipe.Dispose();
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/SongEndEventArgs.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;

namespace TS3AudioBot.Audio
{
	public class SongEndEventArgs : EventArgs
	{
		public bool SongEndedByCallback { get; }
		public SongEndEventArgs(bool songEndedByCallback) { SongEndedByCallback = songEndedByCallback; }
	}
}


================================================
FILE: TS3AudioBot/Audio/SongInfoChanged.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;

namespace TS3AudioBot.Audio
{
	public class SongInfoChanged : EventArgs
	{
		public string? Title { get; set; }
	}
}


================================================
FILE: TS3AudioBot/Audio/StallCheckPipe.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using TSLib.Audio;

namespace TS3AudioBot.Audio
{
	public class StallCheckPipe : IAudioPipe
	{
		private const uint StallCountInterval = 10;
		private const uint StallNoErrorCountMax = 5;

		public bool Active => OutStream?.Active ?? false;
		public IAudioPassiveConsumer? OutStream { get; set; }

		private bool isStall;
		private uint stallCount;
		private uint stallNoErrorCount;

		public StallCheckPipe()
		{
			isStall = false;
			stallCount = 0;
		}

		public void Write(Span<byte> data, Meta? meta)
		{
			if (OutStream is null) return;

			if (isStall)
			{
				// TODO maybe do time-cooldown instead of call-count-cooldown
				if (++stallCount % StallCountInterval == 0)
				{
					stallNoErrorCount++;
					if (stallNoErrorCount > StallNoErrorCountMax)
					{
						stallCount = 0;
						isStall = false;
					}
				}
				else
				{
					return;
				}
			}

			OutStream?.Write(data, meta);
		}

		public void SetStall()
		{
			stallNoErrorCount = 0;
			isStall = true;
		}
	}
}


================================================
FILE: TS3AudioBot/Audio/StreamAudioPlayerSource.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Threading.Tasks;
using TSLib.Audio;

namespace TS3AudioBot.Audio
{
	public class StreamAudioPlayerSource : IPlayerSource, IAudioActiveConsumer
	{
		private bool hasFired = false;

		public IAudioPassiveProducer? InStream { get; set; }
		public TimeSpan? Length => null;
		public TimeSpan? Position => null;

		public event EventHandler? OnSongEnd;
		event EventHandler<SongInfoChanged> IPlayerSource.OnSongUpdated { add { } remove { } }

		public StreamAudioPlayerSource() { }

		public StreamAudioPlayerSource(IAudioPassiveProducer stream) : this()
		{
			InStream = stream;
		}

		public int Read(byte[] buffer, int offset, int length, out Meta? meta)
		{
			var stream = InStream;
			if (stream is null)
			{
				meta = default;
				return 0;
			}

			var read = stream.Read(buffer, offset, length, out meta);
			if (read == 0 && !hasFired)
			{
				hasFired = true;
				OnSongEnd?.Invoke(this, EventArgs.Empty);
				return 0;
			}
			return read;
		}

		public void Reset() => hasFired = false;

		public void Dispose() => InStream?.Dispose();

		public Task Seek(TimeSpan position) { throw new NotSupportedException(); }
	}
}


================================================
FILE: TS3AudioBot/Bot.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Algorithm;
using TS3AudioBot.Audio;
using TS3AudioBot.CommandSystem;
using TS3AudioBot.CommandSystem.Text;
using TS3AudioBot.Config;
using TS3AudioBot.Dependency;
using TS3AudioBot.Environment;
using TS3AudioBot.Helper;
using TS3AudioBot.History;
using TS3AudioBot.Localization;
using TS3AudioBot.Playlists;
using TS3AudioBot.Plugins;
using TS3AudioBot.ResourceFactories;
using TS3AudioBot.Sessions;
using TSLib;
using TSLib.Full;
using TSLib.Helper;
using TSLib.Messages;
using TSLib.Scheduler;

namespace TS3AudioBot
{
	/// <summary>Core class managing all bots and utility modules.</summary>
	public sealed class Bot
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

		private readonly ConfBot config;
		private readonly TickWorker idleTickWorker;

		private bool isClosed;
		internal BotInjector Injector { get; }
		internal DedicatedTaskScheduler Scheduler { get; }

		public Id Id { get; }
		/// <summary>This is the template name. Can be null.</summary>
		public string? Name => config.Name;
		public bool QuizMode { get; set; }

		private readonly ResolveContext resourceResolver;
		private readonly Ts3Client ts3client;
		private readonly TsFullClient ts3FullClient;
		private readonly SessionManager sessionManager;
		private readonly PlayManager playManager;
		private readonly IVoiceTarget targetManager;
		private readonly Player player;
		private readonly Stats stats;
		private readonly LocalizationManager localization;

		public Bot(Id id, ConfBot config, BotInjector injector)
		{
			this.Id = id;
			this.config = config;
			this.Injector = injector;

			// Registering config changes
			config.Language.Changed += async (s, e) =>
			{
				var langResult = await localization.LoadLanguage(e.NewValue, true);
				if (!langResult.Ok)
					Log.Error("Failed to load language file ({0})", langResult.Error);
			};
			config.Events.IdleDelay.Changed += (s, e) => EnableIdleTickWorker();
			config.Events.OnIdle.Changed += (s, e) => EnableIdleTickWorker();

			var builder = new DependencyBuilder(Injector);
			Injector.HideParentModule<CommandManager>();
			Injector.HideParentModule<DedicatedTaskScheduler>();
			Injector.AddModule(this);
			Injector.AddModule(config);
			Injector.AddModule(Injector);
			Injector.AddModule(config.Playlists);
			Injector.AddModule(config.History);
			Injector.AddModule(Id);
			builder.RequestModule<PlaylistIO>();
			builder.RequestModule<PlaylistManager>();
			builder.RequestModule<DedicatedTaskScheduler>();
			builder.RequestModule<TsFullClient>();
			builder.RequestModule<TsBaseFunctions, TsFullClient>();
			builder.RequestModule<Ts3Client>();
			builder.RequestModule<Player>();
			builder.RequestModule<CustomTargetPipe>();
			builder.RequestModule<IVoiceTarget, CustomTargetPipe>();
			builder.RequestModule<SessionManager>();
			builder.RequestModule<ResolveContext>();
			builder.RequestModule<CommandManager>();
			builder.RequestModule<LocalizationManager>();
			if (config.History.Enabled)
			{
				builder.RequestModule<HistoryManager>();
			}
			builder.RequestModule<PlayManager>();

			if (!builder.Build())
			{
				Log.Error("Missing bot module dependency");
				throw new Exception("Could not load all bot modules");
			}
			Injector.ClearHiddenParentModules();

			resourceResolver = Injector.GetModuleOrThrow<ResolveContext>();
			ts3FullClient = Injector.GetModuleOrThrow<TsFullClient>();
			ts3client = Injector.GetModuleOrThrow<Ts3Client>();
			player = Injector.GetModuleOrThrow<Player>();
			Scheduler = Injector.GetModuleOrThrow<DedicatedTaskScheduler>();
			var customTarget = Injector.GetModuleOrThrow<CustomTargetPipe>();
			player.SetTarget(customTarget);
			Injector.AddModule(ts3FullClient.Book);
			playManager = Injector.GetModuleOrThrow<PlayManager>();
			targetManager = Injector.GetModuleOrThrow<IVoiceTarget>();
			sessionManager = Injector.GetModuleOrThrow<SessionManager>();
			stats = Injector.GetModuleOrThrow<Stats>();
			var commandManager = Injector.GetModuleOrThrow<CommandManager>();
			localization = Injector.GetModuleOrThrow<LocalizationManager>();

			idleTickWorker = Scheduler.Invoke(() => Scheduler.CreateTimer(OnIdle, TimeSpan.MaxValue, false)).Result;

			player.OnSongEnd += playManager.SongStoppedEvent;
			player.OnSongUpdated += (s, e) => playManager.Update(e);
			// Update idle status events
			playManager.BeforeResourceStarted += (s, e) => { DisableIdleTickWorker(); return Task.CompletedTask; };
			playManager.PlaybackStopped += (s, e) => { EnableIdleTickWorker(); return Task.CompletedTask; };
			// Used for custom scripts, like voice_mode, onsongstart
			playManager.BeforeResourceStarted += BeforeResourceStarted;
			playManager.AfterResourceStarted += AfterResourceStarted;
			// Update the own status text to the current song title
			playManager.AfterResourceStarted += (s, e) => UpdateBotStatus();
			playManager.PlaybackStopped += (s, e) => UpdateBotStatus();
			playManager.OnResourceUpdated += (s, e) => UpdateBotStatus();
			// Log our resource in the history
			if (Injector.TryGet<HistoryManager>(out var historyManager))
				playManager.AfterResourceStarted += (s, e) =>
				{
					if (e.PlayInfo != null)
						historyManager.LogAudioResource(new HistorySaveData(e.PlayResource.AudioResource, e.PlayInfo.ResourceOwnerUid));
					return Task.CompletedTask;
				};
			// Update our thumbnail
			playManager.AfterResourceStarted += (s, e) => GenerateStatusImage(true, e);
			playManager.PlaybackStopped += (s, e) => GenerateStatusImage(false, null);
			// Stats
			playManager.AfterResourceStarted += (s, e) => { stats.TrackSongStart(Id, e.ResourceData.AudioType); return Task.CompletedTask; };
			playManager.ResourceStopped += (s, e) => { stats.TrackSongStop(Id); return Task.CompletedTask; };
			// Register callback for all messages happening
			ts3client.OnMessageReceived += OnMessageReceived;
			// Register callback to remove open private sessions, when user disconnects
			ts3FullClient.OnEachClientLeftView += OnClientLeftView;
			ts3client.OnBotConnected += OnBotConnected;
			ts3client.OnBotDisconnected += OnBotDisconnected;
			ts3client.OnBotStoppedReconnecting += OnBotStoppedReconnecting;
			// Alone mode
			ts3client.OnAloneChanged += OnAloneChanged;
			ts3client.OnAloneChanged += (s, e) => { customTarget.Alone = e.Alone; return Task.CompletedTask; };
			// Whisper stall
			ts3client.OnWhisperNoTarget += (s, e) => player.SetStall();

			commandManager.RegisterCollection(MainCommands.Bag);
			// TODO remove after plugin rework
			var pluginManager = Injector.GetModuleOrThrow<PluginManager>();
			foreach (var plugin in pluginManager.Plugins)
				if (plugin.Type == PluginType.CorePlugin || plugin.Type == PluginType.Commands)
					commandManager.RegisterCollection(plugin.CorePlugin.Bag);
			// Restore all alias from the config
			foreach (var alias in config.Commands.Alias.GetAllItems())
				commandManager.RegisterAlias(alias.Key, alias.Value).UnwrapToLog(Log);
		}

		public Task<E<string>> Run()
		{
			Scheduler.VerifyOwnThread();

			Log.Info("Bot \"{0}\" connecting to \"{1}\"", config.Name, config.Connect.Address);
			return Task.FromResult(ts3client.Connect());
		}

		public async Task Stop()
		{
			Scheduler.VerifyOwnThread();

			Injector.GetModule<BotManager>()?.RemoveBot(this);

			if (!isClosed) isClosed = true;
			else return;

			Log.Info("Bot ({0}) disconnecting.", Id);

			DisableIdleTickWorker();

			Injector.GetModule<PluginManager>()?.StopPlugins(this);
			Injector.GetModule<PlayManager>()?.Stop();
			Injector.GetModule<Player>()?.Dispose();
			var tsClient = Injector.GetModule<Ts3Client>();
			if (tsClient != null)
				await tsClient.Disconnect();
			Injector.GetModule<DedicatedTaskScheduler>()?.Dispose();
			config.ClearEvents();
		}

		private async Task OnBotConnected(object? sender, EventArgs e)
		{
			EnableIdleTickWorker();

			var badges = config.Connect.Badges.Value;
			if (!string.IsNullOrEmpty(badges))
				ts3client?.ChangeBadges(badges);

			var onStart = config.Events.OnConnect.Value;
			if (!string.IsNullOrEmpty(onStart))
			{
				var info = CreateExecInfo();
				await CallScript(info, onStart, false, true);
			}
		}

		private async Task OnBotDisconnected(object? sender, DisconnectEventArgs e)
		{
			DisableIdleTickWorker();

			var onStop = config.Events.OnDisconnect.Value;
			if (!string.IsNullOrEmpty(onStop))
			{
				var info = CreateExecInfo();
				await CallScript(info, onStop, false, true);
			}
		}

		private Task OnBotStoppedReconnecting(object? sender, EventArgs e)
		{
			return Stop();
		}

		private async Task OnMessageReceived(object? sender, TextMessage textMessage)
		{
			if (textMessage?.Message == null)
			{
				Log.Warn("Invalid TextMessage: {@textMessage}", textMessage);
				return;
			}
			Log.Debug("TextMessage: {@textMessage}", textMessage);

			if (!localization.LanguageLoaded)
			{
				var langResult = await localization.LoadLanguage(config.Language, false);
				if (!langResult.Ok)
					Log.Error("Failed to load language file ({0})", langResult.Error);
			}
			localization.ApplyLanguage();

			textMessage.Message = textMessage.Message.TrimStart(' ');
			if (!textMessage.Message.StartsWith("!", StringComparison.Ordinal))
				return;

			Log.Info("User {0} requested: {1}", textMessage.InvokerName, textMessage.Message);

			ts3client.InvalidateClientBuffer();

			ChannelId? channelId = null;
			ClientDbId? databaseId = null;
			ChannelGroupId? channelGroup = null;
			ServerGroupId[]? serverGroups = null;

			if (ts3FullClient.Book.Clients.TryGetValue(textMessage.InvokerId, out var bookClient))
			{
				channelId = bookClient.Channel;
				databaseId = bookClient.DatabaseId;
				serverGroups = bookClient.ServerGroups.ToArray();
				channelGroup = bookClient.ChannelGroup;
			}
			else if ((await ts3FullClient.ClientInfo(textMessage.InvokerId)).Get(out var infoClient, out var infoClientError))
			{
				channelId = infoClient.ChannelId;
				databaseId = infoClient.DatabaseId;
				serverGroups = infoClient.ServerGroups;
				channelGroup = infoClient.ChannelGroup;
			}
			else
			{
				try
				{
					var cachedClient = await ts3client.GetCachedClientById(textMessage.InvokerId);
					channelId = cachedClient.ChannelId;
					databaseId = cachedClient.DatabaseId;
					channelGroup = cachedClient.ChannelGroup;
				}
				catch (AudioBotException cachedClientError)
				{
					Log.Warn(
						"The bot is missing teamspeak permissions to view the communicating client. " +
						"Some commands or permission checks might not work " +
						"(clientlist:{0}, clientinfo:{1}).",
						cachedClientError.Message, infoClientError.ErrorFormat());
				}
			}

			var invoker = new ClientCall(textMessage.InvokerUid ?? Uid.Anonymous, textMessage.Message,
				clientId: textMessage.InvokerId,
				visibiliy: textMessage.Target,
				nickName: textMessage.InvokerName,
				channelId: channelId,
				databaseId: databaseId,
				serverGroups: serverGroups,
				channelGroup: channelGroup);

			var session = sessionManager.GetOrCreateSession(textMessage.InvokerId);
			var info = CreateExecInfo(invoker, session);

			// check if the user has an open request
			if (session.ResponseProcessor != null)
			{
				await TryCatchCommand(info, answer: true, async () =>
				{
					var msg = await session.ResponseProcessor(textMessage.Message);
					if (!string.IsNullOrEmpty(msg))
						await info.Write(msg).CatchToLog(Log);
				});
				session.ClearResponse();
				return;
			}

			await CallScript(info, textMessage.Message, answer: true, false);
		}

		private void OnClientLeftView(object? sender, ClientLeftView eventArgs)
		{
			targetManager.WhisperClientUnsubscribe(eventArgs.ClientId);
			sessionManager.RemoveSession(eventArgs.ClientId);
		}

		private async Task BeforeResourceStarted(object? sender, PlayInfoEventArgs e)
		{
			const string DefaultVoiceScript = "!whisper off";
			const string DefaultWhisperScript = "!xecute (!whisper subscription) (!unsubscribe temporary) (!subscribe channeltemp (!getmy channel))";

			var mode = config.Audio.SendMode.Value;
			string script;
			if (mode.StartsWith("!", StringComparison.Ordinal))
				script = mode;
			else if (mode.Equals("voice", StringComparison.OrdinalIgnoreCase))
				script = DefaultVoiceScript;
			else if (mode.Equals("whisper", StringComparison.OrdinalIgnoreCase))
				script = DefaultWhisperScript;
			else
			{
				Log.Error("Invalid voice mode");
				return;
			}

			var info = CreateExecInfo(e.Invoker);
			await CallScript(info, script, false, true);
		}

		private async Task AfterResourceStarted(object? sender, PlayInfoEventArgs e)
		{
			var onSongStart = config.Events.OnSongStart.Value;
			if (!string.IsNullOrEmpty(onSongStart))
			{
				var info = CreateExecInfo();
				await CallScript(info, onSongStart, false, true);
			}
		}

		#region Status: Description, Avatar

		public Task UpdateBotStatus()
		{
			return Scheduler.InvokeAsync(UpdateBotStatusInternal);
		}

		private async Task UpdateBotStatusInternal()
		{
			Scheduler.VerifyOwnThread();

			if (isClosed)
				return;

			if (!config.SetStatusDescription)
				return;

			string? setString;
			if (playManager.IsPlaying)
			{
				setString = QuizMode
					? strings.info_botstatus_quiztime
					: playManager.CurrentPlayData?.ResourceData?.ResourceTitle;
			}
			else
			{
				setString = strings.info_botstatus_sleeping;
			}
		}

		public Task GenerateStatusImage(bool playing, PlayInfoEventArgs? startEvent)
		{
			return Scheduler.InvokeAsync(() => GenerateStatusImageInternal(playing, startEvent));
		}

		private async Task GenerateStatusImageInternal(bool playing, PlayInfoEventArgs? startEvent)
		{
			Scheduler.VerifyOwnThread();

			if (!config.GenerateStatusAvatar || isClosed)
				return;

			static Stream? GetRandomFile(string? basePath, string prefix)
			{
				try
				{
					if (string.IsNullOrEmpty(basePath))
						return null;
					var avatarPath = new DirectoryInfo(Path.Combine(basePath, BotPaths.Avatars));
					if (!avatarPath.Exists)
						return null;
					var avatars = avatarPath.EnumerateFiles(prefix).ToArray();
					if (avatars.Length == 0)
						return null;
					var pickedAvatar = Tools.PickRandom(avatars);
					return pickedAvatar.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
				}
				catch (Exception ex)
				{
					Log.Warn(ex, "Failed to load local avatar");
					return null;
				}
			}

			Stream? setStream = null;
			if (playing)
			{
				if (startEvent != null && !QuizMode)
				{
					try
					{
						await resourceResolver.GetThumbnail(startEvent.PlayResource,
							async thumbStream => setStream = (await ImageUtil.ResizeImageSave(thumbStream)).Stream);
					}
					catch (AudioBotException ex) { Log.Debug(ex, "Failed to fetch thumbnail image"); }
				}
				setStream ??= GetRandomFile(config.LocalConfigDir, "play*");
			}
			else
			{
				setStream ??= GetRandomFile(config.LocalConfigDir, "sleep*");
				setStream ??= Util.GetEmbeddedFile("TS3AudioBot.Media.SleepingKitty.png");
				var result = await ts3FullClient.UploadAvatar(setStream);
				result.UnwrapToLog(Log);
			}

			if (setStream != null)
			{
			}
			else
			{
				var result = await ts3FullClient.DeleteAvatar();
				result.UnwrapToLog(Log);
			}
		}

		#endregion

		#region Script Execution

		private async Task CallScript(ExecutionInformation info, string command, bool answer, bool skipRights)
		{
			Log.Debug("Calling script (skipRights:{0}, answer:{1}): {2}", skipRights, answer, command);
			stats.TrackCommandCall(answer);

			info.AddModule(new CallerInfo(false)
			{
				SkipRightsChecks = skipRights,
				CommandComplexityMax = config.Commands.CommandComplexity,
				IsColor = config.Commands.Color,
			});

			await TryCatchCommand(info, answer, async () =>
			{
				// parse and execute the command
				var res = await CommandManager.Execute(info, command);

				if (!answer)
					return;

				// Write result to user
				var s = res.AsString();
				if (!string.IsNullOrEmpty(s))
					await info.Write(s).CatchToLog(Log);
			});
		}

		private ExecutionInformation CreateExecInfo(InvokerData? invoker = null, UserSession? session = null)
		{
			var info = new ExecutionInformation(Injector);
			if (invoker is ClientCall ci)
				info.AddModule(ci);
			info.AddModule(invoker ?? InvokerData.Anonymous);
			info.AddModule(session ?? new AnonymousSession());
			info.AddModule(Filter.GetFilterByNameOrDefault(config.Commands.Matcher));
			return info;
		}

		private async Task TryCatchCommand(ExecutionInformation info, bool answer, Func<Task> action)
		{
			try
			{
				await action.Invoke();
			}
			catch (AudioBotException ex)
			{
				NLog.LogLevel commandErrorLevel = answer ? NLog.LogLevel.Debug : NLog.LogLevel.Warn;
				Log.Log(commandErrorLevel, ex, "Command Error ({0})", ex.Message);
				if (answer)
				{
					await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_error.Mod().Color(Color.Red).Bold(), ex.Message))
						.CatchToLog(Log);
				}
			}
			catch (Exception ex)
			{
				Log.Error(ex, "Unexpected command error: {0}", ex.Message);
				if (answer)
				{
					await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_unexpected_error.Mod().Color(Color.Red).Bold(), ex.Message))
						.CatchToLog(Log);
				}
			}
		}

		#endregion

		#region Event: Idle

		private async void OnIdle()
		{
			// DisableIdleTickWorker(); // fire once only ??

			var onIdle = config.Events.OnIdle.Value;
			if (!string.IsNullOrEmpty(onIdle))
			{
				var info = CreateExecInfo();
				await CallScript(info, onIdle, false, true);
			}
		}

		private void EnableIdleTickWorker()
		{
			var idleTime = config.Events.IdleDelay.Value;
			if (idleTime <= TimeSpan.Zero || string.IsNullOrEmpty(config.Events.OnIdle.Value))
			{
				DisableIdleTickWorker();
				return;
			}
			idleTickWorker.Interval = idleTime;
			idleTickWorker.Enable();
		}

		private void DisableIdleTickWorker() => idleTickWorker.Disable();

		#endregion

		#region Event: Alone/Party

		private async Task OnAloneChanged(object? sender, AloneChanged e)
		{
			Scheduler.VerifyOwnThread();

			string script;
			TimeSpan delay;
			if (e.Alone)
			{
				script = config.Events.OnAlone.Value;
				delay = config.Events.AloneDelay.Value;
			}
			else
			{
				script = config.Events.OnParty.Value;
				delay = config.Events.PartyDelay.Value;
			}
			if (string.IsNullOrEmpty(script))
				return;

			if (delay > TimeSpan.Zero) // TODO: Async (Add cancellation token for better consistency)
				await Task.Delay(delay);

			var info = CreateExecInfo();
			await CallScript(info, script, false, true);
		}

		#endregion

		public BotInfo GetInfo() => new BotInfo
		{
			Id = Id,
			Name = config.Name,
			Server = ts3FullClient.ConnectionData?.Address,
			Status = ts3FullClient.Connected ? BotStatus.Connected : BotStatus.Connecting,
		};
	}

	public class BotInfo
	{
		public int? Id { get; set; }
		public string? Name { get; set; }
		public string? Server { get; set; }
		public BotStatus Status { get; set; }

		public override string ToString() => $"Id: {Id} Name: {Name} Server: {Server} Status: {Status}"; // LOC: TODO
	}

	public enum BotStatus
	{
		Offline,
		Connecting,
		Connected,
	}
}


================================================
FILE: TS3AudioBot/BotManager.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Config;
using TS3AudioBot.Dependency;
using TS3AudioBot.Helper;
using TSLib;
using TSLib.Helper;

namespace TS3AudioBot
{
	public class BotManager
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

		private List<Bot?>? activeBots = new List<Bot?>();
		private readonly object lockObj = new object();

		private readonly ConfRoot confRoot;
		private readonly CoreInjector coreInjector;

		public BotManager(ConfRoot confRoot, CoreInjector coreInjector)
		{
			this.confRoot = confRoot;
			this.coreInjector = coreInjector;
		}

		public async Task RunBots(bool interactive)
		{
			var botConfigList = confRoot.GetAllBots();
			if (botConfigList is null)
				return;

			if (botConfigList.Length == 0)
			{
				if (!interactive)
				{
					Log.Warn("No bots are configured in the load list.");
					return;
				}

				Console.WriteLine("It seems like there are no bots configured.");
				Console.WriteLine("Fill out this quick setup to get started.");

				var newBot = CreateNewBot();
				newBot.Run.Value = true;

				var address = await Interactive.LoopActionAsync("Please enter the ip, domain or nickname (with port; default: 9987) where to connect to:", async addr =>
				{
					if (await TsDnsResolver.TryResolve(addr) != null)
						return true;
					Console.WriteLine("The address seems invalid or could not be resolved, continue anyway? [y/N]");
					return Interactive.UserAgree(defaultTo: false);
				});
				if (address is null)
					return;
				newBot.Connect.Address.Value = address;
				Console.WriteLine("Please enter the server password (or leave empty for none):");
				newBot.Connect.ServerPassword.Password.Value = Console.ReadLine();

				if (!newBot.SaveNew(ConfigHelper.DefaultBotName))
				{
					Log.Error("Could not save new bot. Ensure that the bot has access to the directory.");
					return;
				}

				if (!confRoot.Save())
					Log.Error("Could not save root config. The bot won't start by default.");

				var runResult = await RunBot(newBot);
				if (!runResult.Ok)
					Log.Error("Could not run bot ({0})", runResult.Error);
				return;
			}

			var launchBotTasks = new List<Task<R<BotInfo, string>>>(botConfigList.Length);
			foreach (var instance in botConfigList)
			{
				if (!instance.Run)
					continue;
				launchBotTasks.Add(RunBot(instance).ContinueWith(async t =>
				{
					var result = await t;
					if (!result.Ok)
					{
						Log.Error("Could not instantiate bot: {0}", result.Error);
					}
					return result;
				}).Unwrap());
			}
			await Task.WhenAll(launchBotTasks);
		}

		public ConfBot CreateNewBot() => confRoot.CreateBot();

		public Task<R<BotInfo, string>> CreateAndRunNewBot()
		{
			var botConf = CreateNewBot();
			return RunBot(botConf);
		}

		public async Task<R<BotInfo, string>> RunBotTemplate(string name)
		{
			var config = confRoot.GetBotConfig(name);
			if (!config.Ok)
				return config.Error.Message;
			return await RunBot(config.Value);
		}

		public async Task<R<BotInfo, string>> RunBot(ConfBot config)
		{
			var (bot, info) = InstantiateNewBot(config);
			if (info != null)
				return info;

			if (bot is null)
				return "Failed to create new Bot";

			return await bot.Scheduler.InvokeAsync<R<BotInfo, string>>(async () =>
			{
				var initializeResult = await bot.Run();
				if (!initializeResult.Ok)
				{
					await StopBot(bot);
					return $"Bot failed to initialize ({initializeResult.Error})";
				}
				return bot.GetInfo();
			});
		}

		private (Bot? bot, BotInfo? info) InstantiateNewBot(ConfBot config)
		{
			lock (lockObj)
			{
				if (!string.IsNullOrEmpty(config.Name))
				{
					var maybeBot = GetBotSave(config.Name);
					if (maybeBot != null)
						return (null, maybeBot.GetInfo());
				}

				var id = GetFreeId();
				if (id == null)
					return (null, null); // "BotManager is shutting down"

				var botInjector = new BotInjector(coreInjector);
				botInjector.AddModule(botInjector);
				botInjector.AddModule(new Id(id.Value));
				botInjector.AddModule(config);
				if (!botInjector.TryCreate<Bot>(out var bot))
					return (null, null); // "Failed to create new Bot"
				InsertBot(bot);
				return (bot, null);
			}
		}

		// !! This method must be called with a lock on lockObj
		private void InsertBot(Bot bot)
		{
			if (activeBots is null)
				return;
			activeBots[bot.Id] = bot;
		}

		// !! This method must be called with a lock on lockObj
		// !! The id must be either used withing the same lock or considered invalid.
		private int? GetFreeId()
		{
			if (activeBots is null)
				return null;

			for (int i = 0; i < activeBots.Count; i++)
			{
				if (activeBots[i] is null)
				{
					return i;
				}
			}

			// All slots are full, get a new slot
			activeBots.Add(null);
			return activeBots.Count - 1;
		}

		// !! This method must be called with a lock on lockObj
		private Bot? GetBotSave(int id)
		{
			if (activeBots is null || id < 0 || id >= activeBots.Count)
				return null;
			return activeBots[id];
		}

		// !! This method must be called with a lock on lockObj
		private Bot? GetBotSave(string name)
		{
			if (name is null)
				throw new ArgumentNullException(nameof(name));
			if (activeBots is null)
				return null;
			return activeBots.Find(x => x?.Name == name);
		}

		public Bot? GetBotLock(int id)
		{
			Bot? bot;
			lock (lockObj)
			{
				bot = GetBotSave(id);
				if (bot is null)
					return null;
				if (bot.Id != id)
					throw new Exception("Got not matching bot id");
			}
			return bot;
		}

		public Bot? GetBotLock(string name)
		{
			Bot? bot;
			lock (lockObj)
			{
				bot = GetBotSave(name);
				if (bot is null)
					return null;
				if (bot.Name != name)
					throw new Exception("Got not matching bot name");
			}
			return bot;
		}

		internal void IterateAll(Action<Bot> body)
		{
			lock (lockObj)
			{
				if (activeBots is null)
					return;
				foreach (var bot in activeBots)
				{
					if (bot is null) continue;
					body(bot);
				}
			}
		}

		public async Task StopBot(Bot bot)
		{
			RemoveBot(bot);
			await bot.Scheduler.InvokeAsync(async () => await bot.Stop());
		}

		internal void RemoveBot(Bot bot)
		{
			lock (lockObj)
			{
				Bot? botInList;
				if (activeBots != null
					&& (botInList = GetBotSave(bot.Id)) != null
					&& botInList == bot)
				{
					activeBots[bot.Id] = null;
				}
			}
		}

		public BotInfo[] GetBotInfolist()
		{
			lock (lockObj)
			{
				if (activeBots is null)
					return Array.Empty<BotInfo>();
				return activeBots.Where(x => x != null).Select(x => x!.GetInfo()).ToArray();
			}
		}

		public uint GetRunningBotCount()
		{
			lock (lockObj)
			{
				if (activeBots is null)
					return 0;
				return (uint)activeBots.Count(x => x != null);
			}
		}

		public async Task StopBots()
		{
			List<Bot?> disposeBots;
			lock (lockObj)
			{
				if (activeBots is null)
					return;

				disposeBots = activeBots;
				activeBots = null;
			}

			await Task.WhenAll(disposeBots.Where(x => x != null).Select(x => StopBot(x!)));
		}
	}
}


================================================
FILE: TS3AudioBot/CallerInfo.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot
{
	public class CallerInfo
	{
		/// <summary>Whether this call was initiated from the api.</summary>
		public bool ApiCall { get; }
		/// <summary>Skips all permission checks when set to true.</summary>
		public bool SkipRightsChecks { get; set; } = false;
		/// <summary>Counts execution token for a single call to prevent endless loops.</summary>
		public int CommandComplexityCurrent { get; set; } = 0;
		/// <summary>The maximum execution token count for a single call.</summary>
		public int CommandComplexityMax { get; set; } = 0;
		/// <summary>Whether the caller wants a colored output.</summary>
		public bool IsColor { get; set; }

		public CallerInfo(bool isApi)
		{
			ApiCall = isApi;
		}
	}
}


================================================
FILE: TS3AudioBot/ClientCall.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using TSLib;

namespace TS3AudioBot
{
	public class ClientCall : InvokerData
	{
		/// <summary>The original unmodified string which was received by the client.</summary>
		public string TextMessage { get; }

		public ClientDbId? DatabaseId { get; }
		public ChannelId? ChannelId { get; }
		public ClientId? ClientId { get; }
		public string? NickName { get; }
		public ServerGroupId[]? ServerGroups { get; }
		public ChannelGroupId? ChannelGroup { get; }
		public TextMessageTargetMode? Visibiliy { get; internal set; }

		public ClientCall(Uid clientUid, string textMessage, ClientDbId? databaseId = null,
			ChannelId? channelId = null, ClientId? clientId = null, string? nickName = null,
			TextMessageTargetMode? visibiliy = null, ServerGroupId[]? serverGroups = null,
			ChannelGroupId? channelGroup = null) : base(clientUid)
		{
			TextMessage = textMessage;
			DatabaseId = databaseId;
			ChannelId = channelId;
			ClientId = clientId;
			NickName = nickName;
			Visibiliy = visibiliy;
			ServerGroups = serverGroups;
			ChannelGroup = channelGroup;
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/AstCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Text;

namespace TS3AudioBot.CommandSystem.Ast
{
	internal class AstCommand : AstNode
	{
		public override AstType Type => AstType.Command;

		public List<AstNode> Parameter { get; } = new List<AstNode>();

		public AstCommand(string fullRequest) : base(fullRequest) { }

		public override void Write(StringBuilder strb, int depth)
		{
			strb.Space(depth);
			if (Parameter.Count == 0)
			{
				strb.Append("<Invalid empty command>");
			}
			else
			{
				if (Parameter[0] is AstValue comName)
					strb.Append("!").Append(comName.Value);
				else
					strb.Append("<Invalid command name>");

				for (int i = 1; i < Parameter.Count; i++)
				{
					strb.AppendLine();
					Parameter[i].Write(strb, depth + 1);
				}
			}
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/AstError.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Text;

namespace TS3AudioBot.CommandSystem.Ast
{
	internal class AstError : AstNode
	{
		public override AstType Type => AstType.Error;

		public string Description { get; }

		public AstError(AstNode referenceNode, string description)
			: this(referenceNode.FullRequest, referenceNode.Position, referenceNode.Length, description) { }

		public AstError(string request, int pos, int len, string description) : base(request)
		{
			Position = pos;
			Length = len;
			Description = description;
		}

		public override void Write(StringBuilder strb, int depth)
		{
			strb.AppendLine(FullRequest);
			if (Position == 1) strb.Append('.');
			else if (Position > 1) strb.Append(' ', Position);
			strb.Append('~', Length).Append('^').AppendLine();
			strb.Append("Error: ").AppendLine(Description);
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/AstNode.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Text;

namespace TS3AudioBot.CommandSystem.Ast
{
	public abstract class AstNode
	{
		public abstract AstType Type { get; }

		public string FullRequest { get; }
		public int Position { get; set; }
		public int Length { get; set; }

		protected AstNode(string fullRequest)
		{
			FullRequest = fullRequest;
		}

		public abstract void Write(StringBuilder strb, int depth);
		public sealed override string ToString()
		{
			var strb = new StringBuilder();
			Write(strb, 0);
			return strb.ToString();
		}
	}

	internal static class AstNodeExtensions
	{
		public const int SpacePerTab = 2;
		public static StringBuilder Space(this StringBuilder strb, int depth) => strb.Append(' ', depth * SpacePerTab);
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/AstType.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.Ast
{
	public enum AstType
	{
		Command,
		Value,
		Error,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/AstValue.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Text;

namespace TS3AudioBot.CommandSystem.Ast
{
	internal class AstValue : AstNode
	{
		private string? value;
		private string? tailString;

		public override AstType Type => AstType.Value;
		public StringType StringType { get; }
		public int TailLength { get; set; }

		public string Value
		{
			get => value ??= FullRequest.Substring(Position, Length);
			set { this.value = value; tailString = value; }
		}

		public string TailString
		{
			get
			{
				if (tailString is null)
				{
					if (TailLength == 0)
						tailString = FullRequest.Substring(Position);
					else
						tailString = FullRequest.Substring(Position, TailLength);
				}
				return tailString;
			}
		}

		public AstValue(string fullRequest, StringType stringType) : base(fullRequest)
		{
			StringType = stringType;
		}

		public override void Write(StringBuilder strb, int depth) => strb.Space(depth).Append(Value);
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Ast/StringType.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.Ast
{
	internal enum StringType
	{
		FreeString,
		QuotedString,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/BotCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TS3AudioBot.CommandSystem.Commands;
using TS3AudioBot.Localization;

namespace TS3AudioBot.CommandSystem
{
	[DebuggerDisplay("{DebuggerDisplay, nq}")]
	[JsonObject(MemberSerialization.OptIn)]
	public class BotCommand : FunctionCommand
	{
		private readonly string helpLookupName;
		private string? cachedFullQualifiedName;

		[JsonProperty(PropertyName = "Name")]
		public string InvokeName { get; }
		private readonly string[] requiredRights;
		public string RequiredRight => requiredRights[0];
		[JsonProperty(PropertyName = "Description")]
		public string? Description => LocalizationManager.GetString(helpLookupName);
		public UsageAttribute[] UsageList { get; }
		public string FullQualifiedName
		{
			get
			{
				if (cachedFullQualifiedName is null)
				{
					var strb = new StringBuilder();
					strb.Append(InvokeName);
					strb.Append(" (");
					strb.Append(string.Join(", ", CommandParameter.Where(p => !p.Kind.IsNormal()).Select(p => p.Type.FullName).OrderBy(p => p)));
					strb.Append("|");
					strb.Append(string.Join(", ", CommandParameter.Where(p => p.Kind.IsNormal()).Select(p => p.Type.FullName)));
					strb.Append(")");
					cachedFullQualifiedName = strb.ToString();
				}
				return cachedFullQualifiedName;
			}
		}

		[JsonProperty(PropertyName = "Return")]
		public string Return { get; set; }
		[JsonProperty(PropertyName = "Parameter")]
		public (string name, string type, bool optional)[] Parameter { get; }
		[JsonProperty(PropertyName = "Modules")]
		public (string type, bool optional)[] Modules { get; }

		public string DebuggerDisplay
		{
			get
			{
				var strb = new StringBuilder();
				strb.Append('!').Append(InvokeName);
				strb.Append(" : ");
				foreach (var param in UsageList)
					strb.Append(param.UsageSyntax).Append('/');
				return strb.ToString();
			}
		}

		public BotCommand(CommandBuildInfo buildInfo) : base(buildInfo.Method, buildInfo.Parent)
		{
			InvokeName = buildInfo.CommandData.CommandNameSpace;
			helpLookupName = buildInfo.CommandData.OverrideHelpName ?? ("cmd_" + InvokeName.Replace(" ", "_") + "_help");
			requiredRights = new[] { "cmd." + string.Join(".", InvokeName.Split(' ')) };
			UsageList = buildInfo.UsageList;
			// Serialization
			Return = UnwrapReturnType(CommandReturn).Name;
			Parameter = (
				from x in CommandParameter
				where x.Kind.IsNormal()
				select (x.Name, UnwrapParamType(x.Type).Name, x.Optional)).ToArray();
			Modules = (
				from x in CommandParameter
				where x.Kind == ParamKind.Dependency
				select (x.Type.Name, x.Optional)).ToArray();
		}

		public override string ToString()
		{
			var strb = new StringBuilder();
			strb.Append("\n!")
				.Append(InvokeName);

			foreach (var (name, _, optional) in Parameter)
			{
				strb.Append(' ');
				if (optional)
					strb.Append("[<").Append(name).Append(">]");
				else
					strb.Append('<').Append(name).Append('>');
			}

			strb.Append(": ")
				.Append(Description ?? strings.error_no_help ?? "<No help found>");

			if (UsageList.Length > 0)
			{
				int longest = UsageList.Max(p => p.UsageSyntax.Length) + 1;
				foreach (var para in UsageList)
					strb.Append("\n!").Append(InvokeName).Append(" ").Append(para.UsageSyntax)
						.Append(' ', longest - para.UsageSyntax.Length).Append(para.UsageHelp);
			}
			return strb.ToString();
		}

		public override async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			// Check call complexity
			info.UseComplexityTokens(1);

			// Check permissions
			if (!await info.HasRights(requiredRights))
				throw new CommandException(string.Format(strings.error_missing_right, InvokeName, RequiredRight), CommandExceptionReason.MissingRights);

			return await base.Execute(info, arguments);
		}
	}

	public class CommandBuildInfo
	{
		public object? Parent { get; }
		public MethodInfo Method { get; }
		public CommandAttribute CommandData { get; }
		public UsageAttribute[] UsageList { get; set; }

		public CommandBuildInfo(object? p, MethodInfo m, CommandAttribute comAtt)
		{
			Parent = p;
			Method = m;
			if (!m.IsStatic && p is null)
				throw new ArgumentException("Got instance method without accociated object");
			CommandData = comAtt;
			UsageList = Array.Empty<UsageAttribute>();
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandAttribute.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;

namespace TS3AudioBot.CommandSystem
{
	/// <summary>
	/// Marks a method as callable from the CommandSystem.
	/// The containing class must be registered in the CommandSystem to use this method.
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, Inherited = false)]
	public sealed class CommandAttribute : Attribute
	{
		public string CommandNameSpace { get; }
		public string? OverrideHelpName { get; }

		public CommandAttribute(string commandNameSpace, string? overrideHelpName = null)
		{
			CommandNameSpace = commandNameSpace;
			OverrideHelpName = overrideHelpName;
		}
	}

	/// <summary>
	/// Gives an example on how to use this method.
	/// Can be used to clarify different functionality from various overloads.
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
	public sealed class UsageAttribute : Attribute
	{
		public string UsageSyntax { get; }
		public string UsageHelp { get; }

		public UsageAttribute(string syntax, string help)
		{
			UsageSyntax = syntax;
			UsageHelp = help;
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandException.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Runtime.Serialization;
using TSLib.Messages;

namespace TS3AudioBot.CommandSystem
{
	[Serializable]
	public class CommandException : AudioBotException
	{
		public CommandExceptionReason Reason { get; }

		protected CommandException()
			: this(CommandExceptionReason.Unknown)
		{ }

		public CommandException(string message)
			: base(message)
		{ Reason = CommandExceptionReason.CommandError; }

		protected CommandException(CommandExceptionReason reason)
		{ Reason = reason; }

		public CommandException(string message, CommandExceptionReason reason)
			: base(message)
		{ Reason = reason; }

		public CommandException(string message, Exception inner, CommandExceptionReason reason)
			: base(message, inner)
		{ Reason = reason; }

		protected CommandException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{ }
	}

	[Serializable]
	public class MissingContextCommandException : CommandException
	{
		public Type MissingType { get; }

		protected MissingContextCommandException(Type missingType)
			: base(CommandExceptionReason.MissingContext)
		{ MissingType = missingType; }

		public MissingContextCommandException(string message, Type missingType)
			: base(message, CommandExceptionReason.MissingContext)
		{ MissingType = missingType; }

		public MissingContextCommandException(string message, Exception inner, Type missingType)
			: base(message, inner, CommandExceptionReason.MissingContext)
		{ MissingType = missingType; }
	}

	[Serializable]
	public class TeamSpeakErrorCommandException : CommandException
	{
		public CommandError Error { get; }

		protected TeamSpeakErrorCommandException(CommandError error)
			: base(CommandExceptionReason.MissingContext)
		{ Error = error; }

		public TeamSpeakErrorCommandException(string message, CommandError error)
			: base(message, CommandExceptionReason.MissingContext)
		{ Error = error; }

		public TeamSpeakErrorCommandException(string message, Exception inner, CommandError error)
			: base(message, inner, CommandExceptionReason.MissingContext)
		{ Error = error; }
	}

	public enum CommandExceptionReason
	{
		Unknown = 0,
		InternalError,
		Unauthorized,

		CommandError = 10,
		MissingRights,
		AmbiguousCall,
		MissingParameter,
		NoReturnMatch,
		FunctionNotFound,
		NotSupported,
		MissingContext,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandManager.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using TS3AudioBot.CommandSystem.Ast;
using TS3AudioBot.CommandSystem.CommandResults;
using TS3AudioBot.CommandSystem.Commands;
using TS3AudioBot.CommandSystem.Text;
using TS3AudioBot.Dependency;
using TS3AudioBot.Helper;
using TS3AudioBot.Localization;
using TS3AudioBot.Rights;
using TSLib.Helper;

namespace TS3AudioBot.CommandSystem
{
	/// <summary>Mangement for the bot command system.</summary>
	public class CommandManager
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
		private static readonly Regex CommandNamespaceValidator =
			new Regex(@"^[a-z\d]+( [a-z\d]+)*$", Util.DefaultRegexConfig & ~RegexOptions.IgnoreCase);

		private readonly Dictionary<string, AliasCommand> aliasPaths = new Dictionary<string, AliasCommand>();
		private readonly HashSet<string> commandPaths = new HashSet<string>();
		private readonly HashSet<ICommandBag> baggedCommands = new HashSet<ICommandBag>();
		private readonly RightsManager? rightsManager;

		public CommandGroup RootGroup { get; } = new CommandGroup();

		public CommandManager(RightsManager? rightsManager)
		{
			this.rightsManager = rightsManager;
		}

		public IEnumerable<BotCommand> AllCommands => baggedCommands.SelectMany(x => x.BagCommands);

		public IEnumerable<string> AllRights => AllCommands.Select(x => x.RequiredRight).Concat(baggedCommands.SelectMany(x => x.AdditionalRights));

		#region Management

		public void RegisterCollection(ICommandBag bag)
		{
			if (baggedCommands.Contains(bag))
				return;

			CheckDistinct(bag.BagCommands);
			baggedCommands.Add(bag);

			foreach (var command in bag.BagCommands)
			{
				var result = LoadCommand(command);
				if (!result.Ok)
				{
					Log.Error("Failed to load command bag: " + result.Error);
					UnregisterCollection(bag);
					throw new InvalidOperationException(result.Error);
				}
			}
			rightsManager?.SetRightsList(AllRights);
		}

		public void UnregisterCollection(ICommandBag bag)
		{
			if (baggedCommands.Remove(bag))
			{
				foreach (var com in bag.BagCommands)
				{
					UnloadCommand(com);
				}
				rightsManager?.SetRightsList(AllRights);
			}
		}

		public E<LocalStr> RegisterAlias(string path, string command)
		{
			if (aliasPaths.ContainsKey(path))
				return new LocalStr("Already exists"); // TODO

			var dac = new AliasCommand(command);
			var res = LoadICommand(dac, path);
			if (!res)
				return new LocalStr(res.Error); // TODO

			aliasPaths.Add(path, dac);
			return R.Ok;
		}

		public E<LocalStr> UnregisterAlias(string path)
		{
			if (!aliasPaths.TryGetValue(path, out var com))
				return new LocalStr("Does not exist"); // TODO

			UnloadICommand(com, path);

			aliasPaths.Remove(path);

			return R.Ok;
		}

		public IEnumerable<string> AllAlias => aliasPaths.Keys;

		public AliasCommand? GetAlias(string path) => aliasPaths.TryGetValue(path, out var ali) ? ali : null;

		public static IEnumerable<BotCommand> GetBotCommands(object? obj, Type? type = null) => GetBotCommands(GetCommandMethods(obj, type));

		public static IEnumerable<BotCommand> GetBotCommands(IEnumerable<CommandBuildInfo> methods)
		{
			foreach (var botData in methods)
			{
				botData.UsageList = botData.Method.GetCustomAttributes<UsageAttribute>().ToArray();
				yield return new BotCommand(botData);
			}
		}

		public static IEnumerable<CommandBuildInfo> GetCommandMethods(object? obj, Type? type = null)
		{
			if (obj is null && type is null)
				throw new ArgumentNullException(nameof(type), "No type information given.");
			return GetCommandMethodsIterator();
			IEnumerable<CommandBuildInfo> GetCommandMethodsIterator()
			{
				var objType = type ?? obj!.GetType();

				foreach (var method in objType.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
				{
					var comAtt = method.GetCustomAttribute<CommandAttribute>();
					if (comAtt is null) continue;
					if (obj is null && !method.IsStatic)
					{
						Log.Warn("Method '{0}' needs an instance, but no instance was provided. It will be ignored.", method.Name);
						continue;
					}
					yield return new CommandBuildInfo(obj, method, comAtt);
				}
			}
		}

		private static void CheckDistinct(IReadOnlyCollection<BotCommand> list)
		{
			if (list.Select(c => c.FullQualifiedName).Distinct().Count() < list.Count)
			{
				var duplicates = list.GroupBy(c => c.FullQualifiedName).Where(g => g.Count() > 1).Select(g => g.Key);
				throw new InvalidOperationException("The object contains duplicates: " + string.Join(", ", duplicates));
			}
		}

		private E<string> LoadCommand(BotCommand com)
		{
			if (commandPaths.Contains(com.FullQualifiedName))
				return "Command already exists: " + com.InvokeName;

			commandPaths.Add(com.FullQualifiedName);
			return LoadICommand(com, com.InvokeName);
		}

		private E<string> LoadICommand(ICommand com, string path)
		{
			if (!CommandNamespaceValidator.IsMatch(path))
				return "Command has an invalid invoke name: " + path;

			string[] comPath = path.Split(' ');

			var buildResult = BuildAndGet(comPath.Take(comPath.Length - 1));
			if (!buildResult)
				return GenerateError(buildResult.Error, com as BotCommand);

			var result = InsertInto(buildResult.Value, com, comPath.Last());
			if (!result)
				return GenerateError(result.Error, com as BotCommand);

			return R.Ok;
		}

		private R<CommandGroup, string> BuildAndGet(IEnumerable<string> comPath)
		{
			CommandGroup group = RootGroup;
			// this for loop iterates through the separate names of
			// the command to be added.
			foreach (var comPathPart in comPath)
			{
				switch (group.GetCommand(comPathPart))
				{
				// if a group to hold the next level command doesn't exist
				// it will be created here
				case null:
					var nextGroup = new CommandGroup();
					group.AddCommand(comPathPart, nextGroup);
					group = nextGroup;
					break;

				// if the group already exists we can take it.
				case CommandGroup cgCommand:
					group = cgCommand;
					break;

				// if the element is anything else, we have to replace it
				// with a group and put the old element back into it.
				case FunctionCommand fnCommand:
					var subGroup = new CommandGroup();
					group.RemoveCommand(comPathPart);
					group.AddCommand(comPathPart, subGroup);
					var insertResult = InsertInto(group, fnCommand, comPathPart);
					if (!insertResult.Ok)
						return insertResult.Error;
					group = subGroup;
					break;

				default:
					return "An overloaded command cannot be replaced by a CommandGroup";
				}
			}

			return group;
		}

		private static E<string> InsertInto(CommandGroup group, ICommand com, string name)
		{
			var subCommand = group.GetCommand(name);

			switch (subCommand)
			{
			case null:
				// the group we are trying to insert has no element with the current
				// name, so just insert it
				group.AddCommand(name, com);
				return R.Ok;

			case CommandGroup insertCommand:
				// to add a command to CommandGroup will have to treat it as a subcommand
				// with an empty string as a name
				var noparamCommand = insertCommand.GetCommand(string.Empty);
				if (noparamCommand is null)
				{
					insertCommand.AddCommand(string.Empty, com);
					if (com is BotCommand botCom && botCom.NormalParameters > 0)
						Log.Warn("\"{0}\" has at least one parameter and won't be reachable due to an overloading function.", botCom.FullQualifiedName);
					return R.Ok;
				}
				else
					return "An empty named function under a group cannot be overloaded.";
			}

			if (!(com is FunctionCommand funcCom))
				return $"The command cannot be inserted into a complex node ({name}).";

			switch (subCommand)
			{
			case FunctionCommand subFuncCommand:
				// if we have is a simple function, we need to create a overloader
				// and then add both functions to it
				group.RemoveCommand(name);
				var overloader = new OverloadedFunctionCommand();
				overloader.AddCommand(subFuncCommand);
				overloader.AddCommand(funcCom);
				group.AddCommand(name, overloader);
				break;

			case OverloadedFunctionCommand insertCommand:
				// if we have a overloaded function, we can simply add it
				insertCommand.AddCommand(funcCom);
				break;

			default:
				return "Unknown node to insert to.";
			}

			return R.Ok;
		}

		private static E<string> GenerateError(string msg, BotCommand? involvedCom)
		{
			return $"Command error path: {involvedCom?.InvokeName}"
				+ $"Command: {involvedCom?.FullQualifiedName}"
				+ $"Error: {msg}";
		}

		private void UnloadCommand(BotCommand com)
		{
			if (!commandPaths.Remove(com.FullQualifiedName))
				return;

			UnloadICommand(com, com.InvokeName);
		}

		private void UnloadICommand(ICommand com, string path)
		{
			var comPath = path.Split(' ');

			var node = new CommandUnloadNode(null, RootGroup);

			// build up the list to our desired node
			for (int i = 0; i < comPath.Length - 1; i++)
			{
				if (!(node.Self.GetCommand(comPath[i]) is CommandGroup nextGroup))
					break;

				node = new CommandUnloadNode(node, nextGroup);
			}

			var subGroup = node.Self.GetCommand(comPath.Last());

			switch (subGroup)
			{
			// nothing to remove
			case null:
				return;
			// if the subnode is a plain FunctionCommand then we found our command to delete
			case FunctionCommand _:
			case AliasCommand _:
				node.Self.RemoveCommand(com);
				break;
			// here we can delete our command from the overloader
			case OverloadedFunctionCommand subOverloadGroup:
				if (com is FunctionCommand funcCom)
					subOverloadGroup.RemoveCommand(funcCom);
				else
					return;
				break;
			// now to the special case when a command gets inserted with an empty string
			case CommandGroup insertGroup:
				// since we check precisely that only one command and only a simple FunctionCommand
				// can be added with an empty string, wen can delete it safely this way
				insertGroup.RemoveCommand(string.Empty);
				// add the node for cleanup
				node = new CommandUnloadNode(node, insertGroup);
				break;
			}

			// and finally clean all empty nodes up
			while (node != null)
			{
				if (node.Self.IsEmpty)
					node.ParentNode?.Self.RemoveCommand(node.Self);
				if (node.ParentNode is null)
					break;
				node = node.ParentNode;
			}
		}

		private class CommandUnloadNode
		{
			public CommandUnloadNode? ParentNode { get; set; }
			public CommandGroup Self { get; set; }
			public CommandUnloadNode(CommandUnloadNode? parentNode, CommandGroup self)
			{
				ParentNode = parentNode;
				Self = self;
			}
		}

		#endregion

		#region Execution

		internal static ICommand AstToCommandResult(AstNode node)
		{
			switch (node.Type)
			{
			case AstType.Error:
				throw new CommandException("Found an unconvertable ASTNode of type Error", CommandExceptionReason.InternalError);
			case AstType.Command:
				var cmd = (AstCommand)node;
				var arguments = new ICommand[cmd.Parameter.Count];
				int tailCandidates = 0;
				for (int i = cmd.Parameter.Count - 1; i >= 1; i--)
				{
					var para = cmd.Parameter[i];
					if (!(para is AstValue astVal) || astVal.StringType != StringType.FreeString)
						break;

					arguments[i] = new ResultCommand(new TailString(astVal.Value, astVal.TailString));
					tailCandidates++;
				}
				for (int i = 0; i < cmd.Parameter.Count - tailCandidates; i++)
					arguments[i] = AstToCommandResult(cmd.Parameter[i]);
				return new RootCommand(arguments);
			case AstType.Value:
				var astNode = (AstValue)node;
				return new ResultCommand(astNode.Value);
			default:
				throw Tools.UnhandledDefault(node.Type);
			}
		}

		public static async Task<ICmdResult> Execute(ExecutionInformation info, string command)
		{
			var ast = CommandParser.ParseCommandRequest(command);
			var cmd = AstToCommandResult(ast);
			return new ICmdResult(await cmd.Execute(info, Array.Empty<ICommand>()));
		}

		public static async Task<ICmdResult> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
			=> new ICmdResult(await info.GetModuleOrThrow<CommandManager>().RootGroup.Execute(info, arguments));

		public static string GetTree(ICommand com)
		{
			var strb = new TextModBuilder();
			GetTree(com, strb, 0);
			return strb.ToString();
		}

		private static void GetTree(ICommand com, TextModBuilder strb, int indent)
		{
			switch (com)
			{
			case CommandGroup group:
				strb.AppendFormat("<group>\n".Mod().Color(Color.Red));
				foreach (var subCom in group.Commands)
				{
					strb.Append(new string(' ', (indent + 1) * 2)).Append(subCom.Key);
					GetTree(subCom.Value, strb, indent + 1);
				}
				break;

			case FunctionCommand _:
				strb.AppendFormat("<func>\n".Mod().Color(Color.Green));
				break;

			case OverloadedFunctionCommand ofunc:
				strb.AppendFormat($"<overload({ofunc.Functions.Count})>\n".Mod().Color(Color.Blue));
				break;

			case AliasCommand _:
				strb.AppendFormat($"<alias>\n".Mod().Color(Color.Yellow));
				break;

			default:
				strb.AppendFormat("\n");
				break;
			}
		}

		#endregion
	}

	public readonly struct ICmdResult
	{
		private readonly object? result;

		public ICmdResult(object? result)
		{
			this.result = result;
		}

		public object? AsRaw() => result;

		public string? AsString() => result?.ToString();
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandParser.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Text;
using TS3AudioBot.CommandSystem.Ast;

namespace TS3AudioBot.CommandSystem
{
	internal static class CommandParser
	{
		public const char DefaultCommandChar = '!';
		public const char DefaultDelimeterChar = ' ';

		// This switch follows more or less a DEA to this EBNF
		// COMMAND-EBNF := <COMMAND> | $.*^

		// COMMAND      := '!' <ARGUMENT> (\s+ <ARGUMENT>)*
		// ARGUMENT     := '(' <COMMAND> ')'? | <FREESTRING> | <QUOTESTRING>
		// FREESTRING   := [^)]+
		// QUOTESTRING  := '"' [<anything but ", \" is ok>]* '"'

		public static AstNode ParseCommandRequest(string request, char commandChar = DefaultCommandChar, char delimeterChar = DefaultDelimeterChar)
		{
			AstCommand? root = null;
			var comAst = new Stack<AstCommand>();
			var build = BuildStatus.ParseCommand;
			var strb = new StringBuilder();
			var strPtr = new StringPtr(request);

			var startTrim = request.AsSpan().TrimStart();
			if (startTrim.IsEmpty || startTrim[0] != commandChar)
			{
				return new AstValue(request, StringType.FreeString)
				{
					Length = request.Length,
					Position = 0,
					Value = request,
				};
			}

			while (!strPtr.End)
			{
				AstCommand buildCom;
				switch (build)
				{
				case BuildStatus.ParseCommand:
					// Got a command
					buildCom = new AstCommand(request);
					// Consume CommandChar if left over
					if (strPtr.Char == commandChar)
						strPtr.Next(commandChar);

					if (root is null) root = buildCom;
					else comAst.Peek().Parameter.Add(buildCom);
					comAst.Push(buildCom);
					build = BuildStatus.SelectParam;
					break;

				case BuildStatus.SelectParam:
					strPtr.SkipChar(delimeterChar);

					if (strPtr.End)
					{
						build = BuildStatus.End;
					}
					else
					{
						switch (strPtr.Char)
						{
						case '"':
						case '\'':
							build = BuildStatus.ParseQuotedString;
							//goto case BuildStatus.ParseQuotedString;
							break;

						case '(':
							if (!strPtr.HasNext)
							{
								build = BuildStatus.ParseFreeString;
							}
							else if (strPtr.IsNext(commandChar))
							{
								strPtr.Next('(');
								build = BuildStatus.ParseCommand;
							}
							else
							{
								build = BuildStatus.ParseFreeString;
							}
							break;

						case ')':
							if (comAst.Count <= 0)
							{
								build = BuildStatus.End;
							}
							else
							{
								buildCom = comAst.Pop();
								foreach (var param in buildCom.Parameter)
									if (param is AstValue astVal)
										astVal.TailLength = strPtr.Index - param.Position;
								if (comAst.Count <= 0)
									build = BuildStatus.End;
							}
							strPtr.Next();
							break;

						default:
							build = BuildStatus.ParseFreeString;
							break;
						}
					}
					break;

				case BuildStatus.ParseFreeString:
					var valFreeAst = new AstValue(request, StringType.FreeString);
					using (strPtr.TrackNode(valFreeAst))
					{
						for (; !strPtr.End; strPtr.Next())
						{
							if ((strPtr.Char == '(' && strPtr.HasNext && strPtr.IsNext(commandChar))
								|| strPtr.Char == ')'
								|| strPtr.Char == delimeterChar)
							{
								break;
							}
						}
					}
					buildCom = comAst.Peek();
					buildCom.Parameter.Add(valFreeAst);
					build = BuildStatus.SelectParam;
					break;

				case BuildStatus.ParseQuotedString:
					strb.Clear();

					char quoteChar;
					if (strPtr.TryNext('"'))
						quoteChar = '"';
					else if (strPtr.TryNext('\''))
						quoteChar = '\'';
					else
						throw new Exception("Parser error");

					var valQuoAst = new AstValue(request, StringType.QuotedString);
					using (strPtr.TrackNode(valQuoAst))
					{
						bool escaped = false;
						for (; !strPtr.End; strPtr.Next())
						{
							if (strPtr.Char == '\\')
							{
								escaped = true;
							}
							else if (strPtr.Char == quoteChar)
							{
								if (!escaped)
								{
									strPtr.Next();
									break;
								}
								else
								{
									strb.Length--;
									escaped = false;
								}
							}
							else
							{
								escaped = false;
							}

							strb.Append(strPtr.Char);
						}
					}
					valQuoAst.Value = strb.ToString();
					buildCom = comAst.Peek();
					buildCom.Parameter.Add(valQuoAst);
					build = BuildStatus.SelectParam;
					break;

				case BuildStatus.End:
					strPtr.JumpToEnd();
					break;

				default: throw new InvalidOperationException();
				}
			}

			return root ?? throw new InvalidOperationException("No ast was built");
		}

		private class StringPtr
		{
			private readonly string text;

			public char Char => text[Index];
			public bool End => Index >= text.Length;
			public bool HasNext => Index + 1 < text.Length;
			public int Index { get; private set; }

			public StringPtr(string str)
			{
				text = str;
				Index = 0;
			}

			public void Next()
			{
				Index++;
			}

			public void Next(char mustBe)
			{
				if (Char != mustBe)
					throw new InvalidOperationException();
				Next();
			}

			public bool TryNext(char mustBe)
			{
				if (Char != mustBe)
					return false;
				Next();
				return true;
			}

			public bool IsNext(char what) => HasNext && text[Index + 1] == what;

			public void SkipChar(char c = ' ')
			{
				while (Index < text.Length && text[Index] == c)
					Index++;
			}

			public void JumpToEnd() => Index = text.Length + 1;

			public NodeTracker TrackNode(AstNode? node = null)
			{
				return new NodeTracker(this, node);
			}

			public readonly ref struct NodeTracker
			{
				private readonly int indexStart;
				private readonly StringPtr parent;
				private readonly AstNode? node;
				public NodeTracker(StringPtr p, AstNode? node = null)
				{
					parent = p;
					indexStart = parent.Index;
					this.node = node;
				}

				public readonly void Apply(AstNode node)
				{
					node.Position = indexStart;
					node.Length = parent.Index - indexStart;
				}

				public readonly (int start, int end) Done() => (indexStart, parent.Index);

				public readonly void Dispose() { if (node != null) Apply(node); }
			}
		}

		private enum BuildStatus
		{
			ParseCommand,
			SelectParam,
			ParseFreeString,
			ParseQuotedString,
			End,
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandResults/IAudioResourceResult.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using TS3AudioBot.ResourceFactories;

namespace TS3AudioBot.CommandSystem.CommandResults
{
	public interface IAudioResourceResult
	{
		AudioResource AudioResource { get; }
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandResults/IWrappedResult.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.CommandResults
{
	public interface IWrappedResult
	{
		object? Content { get; }
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandResults/PickObjectCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Reflection;

namespace TS3AudioBot.CommandSystem.CommandResults
{
	public class Pick<T> : IWrappedResult
	{
		private readonly string pickPath;
		private readonly T baseObj;
		private bool isPicked;
		private object? pickedValue;

		public object? Content
		{
			get
			{
				if (!isPicked)
				{
					isPicked = true;
					pickedValue = null;
					pickedValue = DoPick();
				}
				return pickedValue;
			}
		}

		public Pick(T obj, string pickPath)
		{
			baseObj = obj;
			this.pickPath = pickPath;
		}

		private object? DoPick()
		{
			if (baseObj == null)
				return null; // TODO maybe error ?
			if (string.IsNullOrEmpty(pickPath))
				return baseObj;
			var type = baseObj.GetType();
			var prop = type.GetProperty(pickPath, BindingFlags.Public | BindingFlags.Instance);
			if (prop is null)
				throw new CommandException("Property not found" /* TODO LOC */);
			return prop.GetValue(baseObj);
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandResults/TailString.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.CommandResults
{
	public class TailString : IWrappedResult
	{
		public string Content { get; }
		public string Tail { get; }
		object? IWrappedResult.Content => Content;

		public TailString(string contentArg, string tailArg)
		{
			Content = contentArg;
			Tail = tailArg;
		}

		public override string ToString() => Content;
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandSystemExtensions.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TS3AudioBot.Algorithm;
using TS3AudioBot.CommandSystem.Commands;
using TS3AudioBot.Dependency;

namespace TS3AudioBot.CommandSystem
{
	public static class CommandSystemExtensions
	{
		public static IFilter GetFilter(this IInjector injector)
		{
			if (injector.TryGet<IFilter>(out var filter))
				return filter;
			return Filter.DefaultFilter;
		}

		public static Lazy<IFilter> GetFilterLazy(this IInjector injector)
			=> new Lazy<IFilter>(() => injector.GetFilter(), false);

		public static async ValueTask<string> ExecuteToString(this ICommand com, ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			var res = await com.Execute(info, arguments);
			return res?.ToString() ?? "";
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/CommandSystemTypes.cs
================================================
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using TS3AudioBot.CommandSystem.CommandResults;
using TSLib;

namespace TS3AudioBot.CommandSystem
{
	public static class CommandSystemTypes
	{
		/// <summary>
		/// The order of types, the first item has the highest priority,
		/// items not in the list have higher priority as they are special types.
		/// </summary>
		public static readonly Type[] TypeOrder = {
			typeof(bool),
			typeof(sbyte), typeof(byte),
			typeof(short), typeof(ushort),
			typeof(int), typeof(uint),
			typeof(long), typeof(ulong),
			typeof(float), typeof(double),
			typeof(TimeSpan), typeof(DateTime),
			typeof(string) };
		public static readonly HashSet<Type> BasicTypes = new HashSet<Type>(TypeOrder);

		public static readonly HashSet<Type> AdvancedTypes = new HashSet<Type>(new Type[] {
			typeof(IAudioResourceResult),
			typeof(System.Collections.IEnumerable),
			typeof(ResourceFactories.AudioResource),
			typeof(History.AudioLogEntry),
			typeof(Playlists.PlaylistItem),
		}.Concat(TsTypes.All));
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/AliasCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Dependency;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class AliasCommand : ICommand
	{
		private readonly ICommand aliasCommand;
		public string AliasString { get; }

		public AliasCommand(string command)
		{
			var ast = CommandParser.ParseCommandRequest(command);
			aliasCommand = CommandManager.AstToCommandResult(ast);
			AliasString = command;
		}

		public async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			info.UseComplexityTokens(1);

			IReadOnlyList<ICommand>? backupArguments = null;
			if (!info.TryGet<AliasContext>(out var aliasContext))
			{
				aliasContext = new AliasContext();
				info.AddModule(aliasContext);
			}
			else
			{
				backupArguments = aliasContext.Arguments;
			}

			aliasContext.Arguments = arguments.Select(c => new LazyCommand(c)).ToArray();
			var ret = await aliasCommand.Execute(info, Array.Empty<ICommand>());
			aliasContext.Arguments = backupArguments;
			return ret;
		}
	}

	public class AliasContext
	{
		public IReadOnlyList<ICommand>? Arguments { get; set; }
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/AppliedCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class AppliedCommand : ICommand
	{
		private readonly ICommand internCommand;
		private readonly IReadOnlyList<ICommand> internArguments;

		public AppliedCommand(ICommand command, IReadOnlyList<ICommand> arguments)
		{
			internCommand = command;
			internArguments = arguments;
		}

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			var merged = new ICommand[internArguments.Count + arguments.Count];
			internArguments.CopyTo(0, merged, 0);
			arguments.CopyTo(0, merged, internArguments.Count);
			return await internCommand.Execute(info, merged);
		}

		public override string ToString() => $"F\"{internCommand}\"({string.Join(", ", internArguments)})";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/CommandGroup.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Localization;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class CommandGroup : ICommand
	{
		private readonly IDictionary<string, ICommand> commands = new Dictionary<string, ICommand>();

		public void AddCommand(string name, ICommand command) => commands.Add(name, command ?? throw new ArgumentNullException(nameof(command)));
		public bool RemoveCommand(string name) => commands.Remove(name);
		public bool RemoveCommand(ICommand command)
		{
			var com = commands.FirstOrDefault(kvp => kvp.Value == command);
			if (com.Key is null || com.Value is null)
				return false;
			return commands.Remove(com.Key);
		}
		public bool ContainsCommand(string name) => commands.ContainsKey(name);
		public ICommand? GetCommand(string name) => commands.TryGetValue(name, out var com) ? com : null;
		public bool IsEmpty => commands.Count == 0;
		public IEnumerable<KeyValuePair<string, ICommand>> Commands => commands;

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			string result;
			if (arguments.Count == 0)
				result = string.Empty;
			else
				result = await arguments[0].ExecuteToString(info, Array.Empty<ICommand>());

			var filter = info.GetFilter();
			var commandResults = filter.Filter(commands, result).ToArray();

			// The special case when the command is empty and only might match because of fuzzy matching.
			// We only allow this if the command explicitly allows an empty overload.
			if (string.IsNullOrEmpty(result)
				&& (commandResults.Length == 0 || commandResults.Length > 1 || (commandResults.Length == 1 && !string.IsNullOrEmpty(commandResults[0].Key))))
			{
				throw new CommandException(string.Format(strings.cmd_help_info_contains_subfunctions, SuggestionsJoinTrim(commands.Keys)), CommandExceptionReason.AmbiguousCall);
			}

			// We found too many matching commands
			if (commandResults.Length > 1)
				throw new CommandException(string.Format(strings.cmd_help_error_ambiguous_command, SuggestionsJoinTrim(commandResults.Select(g => g.Key))), CommandExceptionReason.AmbiguousCall);
			// We either found no matching command
			if (commandResults.Length == 0)
				throw new CommandException(string.Format(strings.cmd_help_info_contains_subfunctions, SuggestionsJoinTrim(commands.Keys)), CommandExceptionReason.AmbiguousCall);

			var argSubList = arguments.TrySegment(1);
			return await commandResults[0].Value.Execute(info, argSubList);
		}

		private static string SuggestionsJoinTrim(IEnumerable<string> commands)
			=> string.Join(", ", commands.Where(x => !string.IsNullOrEmpty(x)));

		public override string ToString() => "<group>";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/FunctionCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading.Tasks;
using TS3AudioBot.CommandSystem.CommandResults;
using TS3AudioBot.Dependency;
using TS3AudioBot.Helper;
using TS3AudioBot.Localization;
using TS3AudioBot.Web.Api;
using TSLib.Helper;
using static TS3AudioBot.CommandSystem.CommandSystemTypes;
using TryFromFn = System.Func<object?, object?>;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class FunctionCommand : ICommand
	{
		private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

		// Needed for non-static member methods
		private readonly object? callee;
		/// <summary>The method that will be called internally by this command.</summary>
		private readonly MethodInfo internCommand;
		private readonly bool isPlainTask;
		private readonly PropertyInfo? taskValueProp;

		/// <summary>All parameter types, including special types.</summary>
		public ParamInfo[] CommandParameter { get; }
		/// <summary>Return type of method.</summary>
		public Type CommandReturn { get; }
		/// <summary>Count of parameter, without special types.</summary>
		public int NormalParameters { get; }
		/// <summary>
		/// How many free arguments have to be applied to this function.
		/// This includes only user-supplied arguments, e.g. the <see cref="ExecutionInformation"/> is not included.
		/// </summary>
		private int RequiredParameters { get; }

		public FunctionCommand(MethodInfo command, object? obj = null, int? requiredParameters = null)
		{
			internCommand = command;
			CommandParameter = PrecomputeTypes(command.GetParameters());
			CommandReturn = command.ReturnType;
			if (CommandReturn.IsConstructedGenericType && CommandReturn.GetGenericTypeDefinition() == typeof(Task<>))
				taskValueProp = CommandReturn.GetProperty(nameof(Task<object>.Result));
			isPlainTask = CommandReturn == typeof(Task);

			callee = obj;

			NormalParameters = CommandParameter.Count(p => p.Kind.IsNormal());
			RequiredParameters = requiredParameters ?? CommandParameter.Count(p => !p.Optional && p.Kind.IsNormal());
		}

		// Provide some constructors that take lambda expressions directly
		public FunctionCommand(Delegate command, int? requiredParameters = null) : this(command.Method, command.Target, requiredParameters) { }
		public FunctionCommand(Action command) : this(command.Method, command.Target) { }
		public FunctionCommand(Func<string> command) : this(command.Method, command.Target) { }
		public FunctionCommand(Action<string> command) : this(command.Method, command.Target) { }
		public FunctionCommand(Func<string, string> command) : this(command.Method, command.Target) { }

		protected virtual async ValueTask<object?> ExecuteFunction(object?[] parameters)
		{
			try
			{
				var ret = internCommand.Invoke(callee, parameters);
				if (ret is Task task)
				{
					await task;
					if (isPlainTask)
						return null;

					var taskProp = taskValueProp;
					if (taskValueProp is null)
					{
						Log.Warn("Performing really slow Task get, declare your command better to prevent this");
						var taskType = task.GetType();
						if (taskType.IsConstructedGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>))
						{
							taskProp = taskType.GetProperty(nameof(Task<object>.Result)) ?? throw new Exception("Result not found on Task");
						}
					}
					if (taskProp is null)
						return null;

					return taskProp.GetValue(task);
				}
				else
				{
					return ret;
				}
			}
			catch (TargetInvocationException ex) when (!(ex.InnerException is null))
			{
				System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
				throw ex.InnerException;
			}
		}

		/// <summary>
		/// Try to fit the given arguments to the underlying function.
		/// This function will throw an exception if the parameters can't be applied.
		/// The parameters that are extracted from the arguments will be returned if they can be applied successfully.
		/// </summary>
		/// <param name="info">The current call <see cref="ExecutionInformation"/>.</param>
		/// <param name="arguments">The arguments that are applied to this function.</param>
		/// <param name="returnTypes">The possible return types.</param>
		/// <param name="takenArguments">How many arguments could be set.</param>
		private async ValueTask<(object?[] paramObjs, int takenArguments)> FitArguments(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			var parameters = new object?[CommandParameter.Length];
			var filterLazy = info.GetFilterLazy();

			// takenArguments: Index through arguments which have been moved into a parameter
			// p: Iterate through parameters
			var takenArguments = 0;
			for (int p = 0; p < parameters.Length; p++)
			{
				var arg = CommandParameter[p];
				var argType = arg.Type;
				switch (arg.Kind)
				{
				case ParamKind.SpecialArguments:
					parameters[p] = arguments;
					break;

				case ParamKind.Dependency:
					if (info.TryGet(argType, out var obj))
						parameters[p] = obj;
					else if (arg.Optional)
						parameters[p] = null;
					else
						throw new MissingContextCommandException($"Command '{internCommand.Name}' missing execution context '{argType.Name}'", argType);
					break;

				case ParamKind.NormalCommand:
					if (takenArguments >= arguments.Count) { parameters[p] = GetDefault(argType); break; }
					parameters[p] = arguments[takenArguments];
					takenArguments++;
					break;

				case ParamKind.NormalParam:
				case ParamKind.NormalTailString:
					if (takenArguments >= arguments.Count) { parameters[p] = GetDefault(argType); break; }

					var argResultP = await arguments[takenArguments].Execute(info, Array.Empty<ICommand>());
					if (arg.Kind == ParamKind.NormalTailString && argResultP is TailString tailString)
						parameters[p] = tailString.Tail;
					else
						parameters[p] = ConvertParam(argResultP, argType, arg, filterLazy);

					takenArguments++;
					break;

				case ParamKind.NormalArray:
					if (takenArguments >= arguments.Count) { parameters[p] = GetDefault(argType); break; }

					var typeArr = argType.GetElementType()!;
					var args = Array.CreateInstance(typeArr, arguments.Count - takenArguments);
					for (int i = 0; i < args.Length; i++, takenArguments++)
					{
						var argResultA = await arguments[takenArguments].Execute(info, Array.Empty<ICommand>());
						var convResult = ConvertParam(argResultA, typeArr, arg, filterLazy);
						args.SetValue(convResult, i);
					}

					parameters[p] = args;
					break;

				default:
					throw Tools.UnhandledDefault(arg.Kind);
				}
			}

			// Check if we were able to set enough arguments
			int wantArgumentCount = Math.Min(parameters.Length, RequiredParameters);
			if (takenArguments < wantArgumentCount)
				throw ThrowAtLeastNArguments(wantArgumentCount);

			return (parameters, takenArguments);
		}

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			var (parameters, availableArguments) = await FitArguments(info, arguments);

			// Check if we were able to set enough arguments
			int wantArgumentCount = Math.Min(parameters.Length, RequiredParameters);
			if (availableArguments < wantArgumentCount)
				throw ThrowAtLeastNArguments(wantArgumentCount);

			return await ExecuteFunction(parameters);
		}

		private static ParamInfo[] PrecomputeTypes(ParameterInfo[] parameterInfos)
		{
			var precomputed = new ParamInfo[parameterInfos.Length];
			bool foundTail = false;

			for (int i = parameterInfos.Length - 1; i >= 0; i--)
			{
				var parameterInfo = parameterInfos[i];
				var arg = parameterInfo.ParameterType;
				ParamKind kind;

				if (arg == typeof(IReadOnlyList<ICommand>))
					kind = ParamKind.SpecialArguments;
				else if (arg == typeof(ICommand))
					kind = ParamKind.NormalCommand;
				else if (arg.IsArray)
					kind = ParamKind.NormalArray;
				else if (arg.IsEnum
					|| BasicTypes.Contains(arg)
					|| BasicTypes.Contains(UnwrapParamType(arg))
					|| AdvancedTypes.Contains(arg)
					|| AdvancedTypes.Contains(UnwrapParamType(arg)))
					kind = ParamKind.NormalParam;
				// TODO How to distinguish between special type and dependency?
				else
					kind = ParamKind.Dependency;

				TryFromFn? tryFrom = null;
				if (kind == ParamKind.NormalParam || kind == ParamKind.NormalArray)
				{
					var finalType = arg;
					if (kind == ParamKind.NormalArray)
						finalType = finalType.GetElementType() ?? throw new InvalidOperationException("Not an array?");
					finalType = UnwrapParamType(finalType);
					var method = finalType.GetMethod("TryFrom", BindingFlags.Public | BindingFlags.Static);
					if (method != null)
					{
						if (method.ReturnType == typeof(object))
						{
							// return directly: (object) -> object
							tryFrom = (TryFromFn)method.CreateDelegate(typeof(TryFromFn));
						}
						else if (method.ReturnType.IsClass)
						{
							// have: [object]->class
							// can be casted with covariance to [object]->object
							tryFrom = (TryFromFn)method.CreateDelegate(typeof(Func<,>).MakeGenericType(typeof(object), method.ReturnType));
						}
						else if(method.ReturnType.IsValueType)
						{
							static TryFromFn? TryCreateWith(MethodInfo method, Type retType)
							{
								if (method.ReturnType != retType) return null;
								try
								{
									var tryFromToObjectWrapper = new DynamicMethod(
										"TryFromToObjectWrapper", typeof(object), new[] { typeof(object) }, typeof(FunctionCommand).Module);
									var il = tryFromToObjectWrapper.GetILGenerator();
									il.Emit(OpCodes.Ldarg_0);
									il.Emit(OpCodes.Call, method);
									il.Emit(OpCodes.Box, retType);
									il.Emit(OpCodes.Ret);
									return (TryFromFn)tryFromToObjectWrapper.CreateDelegate(typeof(TryFromFn));
								}
								catch { return null; }
							}
							// have: [object]->value
							// box with (object)([object]->value)
							tryFrom ??= TryCreateWith(method, method.ReturnType);
							// have: [object]->value?
							// box with (object)([object]->value?)
							tryFrom ??= TryCreateWith(method, typeof(Nullable<>).MakeGenericType(method.ReturnType));
						}
					}
				}

				if (kind.IsNormal())
				{
					// If we have the last normal parameter, check if it fits the criteria
					// to be extened to a tail string.
					// If it does not, we set foundTail to true anyway, since no other
					// fitting parameter would be a tail anymore
					if (!foundTail && arg == typeof(string))
						kind = ParamKind.NormalTailString;
					foundTail = true;
				}

				precomputed[i] = new ParamInfo(
					parameterInfo,
					kind,
					parameterInfo.IsOptional || parameterInfo.GetCustomAttribute<ParamArrayAttribute>() != null,
					tryFrom);
			}

			return precomputed;
		}

		public static Type UnwrapParamType(Type type)
		{
			if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
				return type.GenericTypeArguments[0];
			return type;
		}

		public static Type UnwrapReturnType(Type type)
		{
			if (type.IsConstructedGenericType)
			{
				var genDef = type.GetGenericTypeDefinition();
				if (genDef == typeof(Nullable<>))
					return type.GenericTypeArguments[0];
				if (genDef == typeof(JsonValue<>))
					return type.GenericTypeArguments[0];
				if (genDef == typeof(JsonArray<>))
					return type.GenericTypeArguments[0].MakeArrayType();
			}
			return type;
		}

		[return: NotNullIfNotNull("value")]
		private static object? UnwrapReturn(object? value)
		{
			if (value is null)
				return null;

			if (value is IWrappedResult wrapped)
				return wrapped.Content;

			var type = value.GetType();
			if (type.IsConstructedGenericType)
			{
				var genDef = type.GetGenericTypeDefinition();
				if (genDef == typeof(Nullable<>))
					return type.GetProperty("Value")!.GetValue(value);
			}
			return value;
		}

		public static CommandException ThrowAtLeastNArguments(int count)
		{
			if (count <= 0)
				throw new ArgumentOutOfRangeException(nameof(count), count, "The count must be at least 1");
			var throwString = count switch
			{
				1 => strings.error_cmd_at_least_one_argument,
				2 => strings.error_cmd_at_least_two_argument,
				3 => strings.error_cmd_at_least_three_argument,
				4 => strings.error_cmd_at_least_four_argument,
				_ => string.Format(strings.error_cmd_at_least_n_arguments, count),
			};
			return new CommandException(throwString, CommandExceptionReason.MissingParameter);
		}

		[return: NotNullIfNotNull("value")]
		public static object? ConvertParam(object? value, Type targetType, ParamInfo param, Lazy<Algorithm.IFilter> filter)
		{
			if (value is null)
				return null;
			if (targetType == typeof(string))
				return value.ToString();

			var valueType = value.GetType();
			if (targetType == valueType || targetType.IsAssignableFrom(valueType))
				return value;
			value = UnwrapReturn(value);
			valueType = value.GetType();
			if (targetType == valueType || targetType.IsAssignableFrom(valueType))
				return value;

			if (param.TryFrom != null)
			{
				var tryResult = param.TryFrom(value);
				if (tryResult != null)
				{
					return tryResult;
				}
			}

			if (targetType.IsEnum)
			{
				var strValue = value.ToString() ?? throw new ArgumentNullException(nameof(value));
				var enumVals = Enum.GetValues(targetType).Cast<Enum>();
				var result = filter.Value.Filter(enumVals.Select(x => new KeyValuePair<string, Enum>(x.ToString(), x)), strValue).Select(x => x.Value).FirstOrDefault();
				if (result is null)
					throw new CommandException(string.Format(strings.error_cmd_could_not_convert_to, strValue, targetType.Name), CommandExceptionReason.MissingParameter);
				return result;
			}
			var unwrappedTargetType = UnwrapParamType(targetType);
			if (valueType == typeof(string) && unwrappedTargetType == typeof(TimeSpan))
			{
				var time = TextUtil.ParseTime((string)value);
				if (time is null)
					throw new CommandException(string.Format(strings.error_cmd_could_not_convert_to, value, nameof(TimeSpan)), CommandExceptionReason.MissingParameter);
				return time.Value;
			}

			// Autoconvert
			try { return Convert.ChangeType(value, unwrappedTargetType, CultureInfo.InvariantCulture); }
			catch (FormatException ex) { throw new CommandException(string.Format(strings.error_cmd_could_not_convert_to, value, unwrappedTargetType.Name), ex, CommandExceptionReason.MissingParameter); }
			catch (OverflowException ex) { throw new CommandException(strings.error_cmd_number_too_big, ex, CommandExceptionReason.MissingParameter); }
			catch (InvalidCastException ex) { throw new CommandException(string.Format(strings.error_cmd_could_not_convert_to, value, unwrappedTargetType.Name), ex, CommandExceptionReason.MissingParameter); }
		}

		private static object? GetDefault(Type type)
		{
			if (type.IsArray)
			{
				var typeArr = type.GetElementType()!;
				return Array.CreateInstance(typeArr, 0);
			}
			if (type.IsValueType)
			{
				return Activator.CreateInstance(type);
			}
			return null;
		}
	}

	public enum ParamKind
	{
		Unknown,
		SpecialArguments,
		Dependency,
		NormalCommand,
		NormalParam,
		NormalArray,
		NormalTailString,
	}

	[DebuggerDisplay("{Kind} {Name,nq}{Optional ? \"?\" : \"\",nq} ({Type.Name,nq})")]
	public class ParamInfo
	{
		public ParamKind Kind { get; }
		public bool Optional { get; }
		public ParameterInfo Param { get; }
		public TryFromFn? TryFrom { get; }
		public Type Type => Param.ParameterType;
		public string Name => Param.Name ?? "<no name>";

		public ParamInfo(ParameterInfo param, ParamKind kind, bool optional, TryFromFn? tryFrom)
		{
			Param = param;
			Kind = kind;
			Optional = optional;
			TryFrom = tryFrom;
		}
	}

	public static class FunctionCommandExtensions
	{
		public static bool IsNormal(this ParamKind kind)
			=> kind == ParamKind.NormalParam
			|| kind == ParamKind.NormalArray
			|| kind == ParamKind.NormalCommand
			|| kind == ParamKind.NormalTailString;
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/ICommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace TS3AudioBot.CommandSystem.Commands
{
	public interface ICommand
	{
		/// <summary>Execute this command.</summary>
		/// <param name="info">All global informations for this execution.</param>
		/// <param name="arguments">
		/// The arguments for this command.
		/// They are evaluated lazy which means they will only be evaluated if needed.
		/// </param>
		/// <param name="returnTypes">
		/// The possible return types that should be returned by this execution.
		/// They are ordered by priority so, if possible, the first return type should be picked, then the second and so on.
		///
		/// These types can contain primitive types, the actual return value will then be wrapped into a <see cref="CommandResults.IPrimitiveResult{T}"/>.
		/// null inside the list allows an empty result.
		/// </param>
		/// <returns>
		/// <para>The result of this command.</para>
		/// <para>null is an empty result.</para>
		/// </returns>
		ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments);
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/LazyCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class LazyCommand : ICommand
	{
		private readonly ICommand innerCommand;
		private bool executed = false;
		/// <summary>
		/// The cached result, if available.
		/// </summary>
		private object? result;

		public LazyCommand(ICommand innerCommandArg)
		{
			innerCommand = innerCommandArg;
		}

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			if (!executed)
			{
				result = await innerCommand.Execute(info, arguments);
				executed = true;
				return result;
			}
			return result;
		}

		public override string ToString() => $"L({innerCommand})";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/OverloadedFunctionCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Localization;

namespace TS3AudioBot.CommandSystem.Commands
{
	public class OverloadedFunctionCommand : ICommand
	{
		public List<FunctionCommand> Functions { get; }

		public OverloadedFunctionCommand() : this(Array.Empty<FunctionCommand>()) { }
		public OverloadedFunctionCommand(IEnumerable<FunctionCommand> functionsArg)
		{
			Functions = functionsArg.ToList();
		}

		public void AddCommand(FunctionCommand command)
		{
			Functions.Add(command);
			SortList();
		}
		public bool RemoveCommand(FunctionCommand command) => Functions.Remove(command);

		private void SortList()
		{
			Functions.Sort((f1, f2) =>
			{
				// The first function in the list should be the most specialized.
				// If the execute the command we will iterate through the list from the beginning
				// and choose the first matching function.

				// Sort out special arguments
				// and remove the nullable wrapper
				var params1 = (from p in f1.CommandParameter
							   where p.Kind.IsNormal()
							   select FunctionCommand.UnwrapParamType(p.Type)).ToList();

				var params2 = (from p in f2.CommandParameter
							   where p.Kind.IsNormal()
							   select FunctionCommand.UnwrapParamType(p.Type)).ToList();

				for (int i = 0; i < params1.Count; i++)
				{
					// Prefer functions with higher parameter count
					if (i >= params2.Count)
						return -1;
					// Not found returns -1, so more important than any found index
					int i1 = Array.IndexOf(CommandSystemTypes.TypeOrder, params1[i]);
					int i2 = Array.IndexOf(CommandSystemTypes.TypeOrder, params2[i]);
					// Prefer lower argument
					if (i1 < i2)
						return -1;
					if (i1 > i2)
						return 1;
				}
				if (params2.Count > params1.Count)
					return 1;

				return 0;
			});
		}

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			// Make arguments lazy, we only want to execute them once
			arguments = arguments.Select(c => new LazyCommand(c)).ToArray();
			CommandException? contextException = null;
			foreach (var f in Functions)
			{
				// Try to call each overload
				try
				{
					return await f.Execute(info, arguments);
				}
				catch (CommandException cmdEx)
					when (cmdEx.Reason == CommandExceptionReason.MissingParameter
						|| cmdEx.Reason == CommandExceptionReason.MissingContext)
				{
					// When we encounter a missing module problem we store it for later, as it is more helpful
					// im most cases to know that some commands *could* have matched if the module were there.
					if (cmdEx.Reason == CommandExceptionReason.MissingContext)
						contextException = cmdEx;
				}
			}
			if (contextException != null)
				System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(contextException).Throw();
			throw new CommandException(strings.error_cmd_no_matching_overload, CommandExceptionReason.MissingParameter);
		}

		public override string ToString() => "<overload>";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/ResultCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace TS3AudioBot.CommandSystem.Commands
{
	/// <summary>
	/// A command that stores a result and returns it.
	/// </summary>
	public class ResultCommand : ICommand
	{
		public object? Content { get; }

		public ResultCommand(object? contentArg)
		{
			Content = contentArg;
		}

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
		{
			return Content;
		}

		public override string ToString() => "<result>";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Commands/RootCommand.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TS3AudioBot.Dependency;

namespace TS3AudioBot.CommandSystem.Commands
{
	/// <summary>
	/// A special group founction that extracts the root group from the current execution context
	/// </summary>
	public class RootCommand : ICommand
	{
		private readonly IReadOnlyList<ICommand> internArguments;

		public RootCommand(IReadOnlyList<ICommand> arguments)
		{
			internArguments = arguments;
		}

		public virtual async ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<ICommand> arguments)
		{
			if (!info.TryGet<CommandManager>(out var cmdSys))
				throw new CommandException("Could not find local commandsystem tree", CommandExceptionReason.MissingContext);

			IReadOnlyList<ICommand> merged;
			if (arguments.Count == 0)
				merged = internArguments;
			else if (internArguments.Count == 0)
				merged = arguments;
			else
				merged = internArguments.Concat(arguments).ToArray();
			return await cmdSys.RootGroup.Execute(info, merged);
		}

		public override string ToString() => $"RootCmd({string.Join(", ", internArguments)})";
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/ExecutionInformation.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using TS3AudioBot.Dependency;

namespace TS3AudioBot.CommandSystem
{
	public class ExecutionInformation : ChainedInjector<BasicInjector>
	{
		public ExecutionInformation() : this(NullInjector.Instance) { }
		public ExecutionInformation(IInjector parent) : base(parent, new BasicInjector())
		{
			this.AddModule(this);
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/ICommandBag.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System.Collections.Generic;

namespace TS3AudioBot.CommandSystem
{
	public interface ICommandBag
	{
		IReadOnlyCollection<BotCommand> BagCommands { get; }
		IReadOnlyCollection<string> AdditionalRights { get; }
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/StaticList.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;

namespace TS3AudioBot.CommandSystem
{
	internal static class StaticList
	{
		public static IReadOnlyList<T> TrySegment<T>(this IReadOnlyList<T> list, int start)
		{
			if (start == 0)
				return list;
			if (start >= list.Count)
				return Array.Empty<T>();
			return list switch
			{
				T[] array => new ArraySegment<T>(array, start, array.Length - start),
				ArraySegment<T> arrayseg => arrayseg[start..],
				_ => list.Skip(start).ToArray(),
			};
		}

		public static IReadOnlyList<T> TrySegment<T>(this IReadOnlyList<T> list, int start, int length)
		{
			return list switch
			{
				T[] array => new ArraySegment<T>(array, start, length),
				ArraySegment<T> arrayseg => arrayseg.Slice(start, length),
				_ => list.Skip(start).Take(length).ToArray(),
			};
		}

		public static void CopyTo<T>(this IReadOnlyList<T> list, int srcOffset, T[] target, int dstOffset)
			=> CopyTo(list, srcOffset, target, dstOffset, list.Count - srcOffset);

		public static void CopyTo<T>(this IReadOnlyList<T> list, int srcOffset, T[] target, int dstOffset, int length)
		{
			switch (list)
			{
			case T[] array:
				Array.Copy(array, srcOffset, target, dstOffset, length);
				break;

			case ArraySegment<T> segArray:
				if (srcOffset + length > segArray.Count)
					throw new ArgumentOutOfRangeException(nameof(length));
				Array.Copy(segArray.Array!, segArray.Offset + srcOffset, target, dstOffset, length);
				break;

			default:
				for (int i = 0; i < length; i++)
					target[dstOffset + i] = list[srcOffset + i];
				break;
			}
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/AppliedTextMod.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.Text
{
	public readonly struct AppliedTextMod
	{
		public string? Text { get; }
		public TextMod Mod { get; }

		public AppliedTextMod(string? text)
		{
			Text = text;
			Mod = TextMod.None;
		}

		public AppliedTextMod(string? text, TextMod mod)
		{
			Text = text;
			Mod = mod;
		}

		public readonly AppliedTextMod Color(Color color) => new AppliedTextMod(Text, Mod.Color(color));
		public readonly AppliedTextMod Bold() => new AppliedTextMod(Text, Mod.Bold());
		public readonly AppliedTextMod Italic() => new AppliedTextMod(Text, Mod.Italic());
		public readonly AppliedTextMod Underline() => new AppliedTextMod(Text, Mod.Underline());
		public readonly AppliedTextMod Strike() => new AppliedTextMod(Text, Mod.Strike());

		public static implicit operator AppliedTextMod(string? text) => new AppliedTextMod(text);

		public override readonly string? ToString() => Text;
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/Color.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Text;

namespace TS3AudioBot.CommandSystem.Text
{
	public readonly struct Color
	{
		public byte R { get; }
		public byte G { get; }
		public byte B { get; }
		public ColorFlags Flags { get; }

		public static readonly Color Black = new Color(0, 0, 0);
		public static readonly Color DarkGray = new Color(64, 64, 64);
		public static readonly Color Gray = new Color(128, 128, 128);
		public static readonly Color LightGray = new Color(192, 192, 192);
		public static readonly Color Red = new Color(255, 0, 0);
		public static readonly Color Green = new Color(0, 255, 0);
		public static readonly Color Blue = new Color(0, 0, 255);
		public static readonly Color Yellow = new Color(255, 255, 0);
		public static readonly Color Cyan = new Color(0, 255, 255);
		public static readonly Color Pink = new Color(255, 0, 255);
		public static readonly Color Orange = new Color(255, 128, 0);
		public static readonly Color White = new Color(255, 255, 255);
		public static readonly Color Transparent = new Color(0, 0, 0, ColorFlags.Transparent);

		private static readonly Dictionary<Color, string> ColorOptimizer = new Dictionary<Color, string>();

		static Color()
		{
			var colors = new[] {
				( new Color(0, 255, 255), "aqua" ),
				( new Color(240, 255, 255), "azure" ),
				( new Color(245, 245, 220), "beige" ),
				( new Color(255, 228, 196), "bisque" ),
				( new Color(0, 0, 0), "black" ),
				( new Color(0, 0, 255), "blue" ),
				( new Color(165, 42, 42), "brown" ),
				( new Color(255, 127, 80), "coral" ),
				( new Color(0, 255, 255), "cyan" ),
				( new Color(255, 215, 0), "gold" ),
				( new Color(128, 128, 128), "gray" ),
				( new Color(0, 128, 0), "green" ),
				( new Color(75, 0, 130), "indigo" ),
				( new Color(255, 255, 240), "ivory" ),
				( new Color(240, 230, 140), "khaki" ),
				( new Color(0, 255, 0), "lime" ),
				( new Color(250, 240, 230), "linen" ),
				( new Color(128, 0, 0), "maroon" ),
				( new Color(0, 0, 128), "navy" ),
				( new Color(128, 128, 0), "olive" ),
				( new Color(255, 165, 0), "orange" ),
				( new Color(218, 112, 214), "orchid" ),
				( new Color(205, 133, 63), "peru" ),
				( new Color(255, 192, 203), "pink" ),
				( new Color(221, 160, 221), "plum" ),
				( new Color(128, 0, 128), "purple" ),
				( new Color(255, 0, 0), "red" ),
				( new Color(250, 128, 114), "salmon" ),
				( new Color(160, 82, 45), "sienna" ),
				( new Color(192, 192, 192), "silver" ),
				( new Color(255, 250, 250), "snow" ),
				( new Color(210, 180, 140), "tan" ),
				( new Color(0, 128, 128), "teal" ),
				( new Color(255, 99, 71), "tomato" ),
				( new Color(238, 130, 238), "violet" ),
				( new Color(245, 222, 179), "wheat" ),
				( new Color(255, 255, 255), "white" ),
				( new Color(255, 255, 0), "yellow" ),
			};

			foreach (var values in colors)
			{
				var col = values.Item1;
				if (values.Item2.Length < 4
					|| (values.Item2.Length < 7 && (!IsDouble(col.R) || !IsDouble(col.G) || !IsDouble(col.B))))
				{
					if (!ColorOptimizer.TryGetValue(col, out var name) || name.Length > values.Item2.Length)
					{
						ColorOptimizer[col] = values.Item2;
					}
				}
			}
		}

		public Color(byte r, byte g, byte b) : this(r, g, b, ColorFlags.Solid) { }
		public Color(byte r, byte g, byte b, ColorFlags flags)
		{
			R = r;
			G = g;
			B = b;
			Flags = flags;
		}

		private static bool IsDouble(byte num) => (num & 0x0F) == (num >> 4);

		public readonly void GetL(StringBuilder strb)
		{
			if (Flags.HasFlag(ColorFlags.Transparent))
				strb.Append("[COLOR=transparent]");
			else if (ColorOptimizer.TryGetValue(this, out var optValue))
				strb.AppendFormat("[COLOR={0}]", optValue);
			else if (IsDouble(R) && IsDouble(G) && IsDouble(B))
				strb.AppendFormat("[COLOR=#{0:X}{1:X}{2:X}]", R & 0x0F, G & 0x0F, B & 0x0F);
			else
				strb.AppendFormat("[COLOR=#{0:X2}{1:X2}{2:X2}]", R, G, B);
		}

		public override readonly bool Equals(object? obj)
		{
			if (obj is Color col)
			{
				return this == col;
			}
			return false;
		}

		public override readonly int GetHashCode() => (int)Flags << 24 | R << 16 | G << 8 | B;

		public override readonly string ToString()
		{
			var strb = new StringBuilder();
			GetL(strb);
			return strb.ToString();
		}

		public static bool operator ==(Color a, Color b) => a.R == b.R && a.G == b.G && a.B == b.B && a.Flags == b.Flags;

		public static bool operator !=(Color a, Color b) => a.R != b.R || a.G != b.G || a.B != b.B || a.Flags != b.Flags;
	}

	[Flags]
	public enum ColorFlags : byte
	{
		Solid = 0,
		Transparent = 1 << 0,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/LongTextBehaviour.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.Text
{
	public enum LongTextBehaviour
	{
		/// <summary>Splits the message preferably at line breaks.</summary>
		Split,
		/// <summary>Splits at exact maximum length per message.</summary>
		SplitHard,
		/// <summary>Discards the message.</summary>
		Drop,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/LongTextTransform.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Collections.Generic;
using System.Linq;
using TSLib.Commands;
using TSLib.Helper;

namespace TS3AudioBot.CommandSystem.Text
{
	public static class LongTextTransform
	{
		private static readonly byte[] SeparatorWeight = new byte[] { (byte)'\n', (byte)',', (byte)' ' };

		public static IEnumerable<string> Split(string text, LongTextBehaviour behaviour, int maxMessageSize, int limit = int.MaxValue)
		{
			if (maxMessageSize < 4)
				throw new ArgumentOutOfRangeException(nameof(maxMessageSize), "The minimum split length must be at least 4 bytes to fit all utf8 characters");

			// Assuming worst case that each UTF-8 character which epands to 4 bytes.
			// If the message is still shorter we can safely return in 1 block.
			if (text.Length * 4 <= maxMessageSize)
				return new[] { text };

			var bytes = Tools.Utf8Encoder.GetBytes(text);

			// If the entire text UTF-8 encoded fits in one message we can return early.
			if (bytes.Length * 2 < maxMessageSize)
				return new[] { text };

			var list = new List<string>();
			Span<Ind> splitIndices = stackalloc Ind[SeparatorWeight.Length];

			var block = bytes.AsSpan();
			while (block.Length > 0)
			{
				int tokenCnt = 0;
				int i = 0;
				bool filled = false;

				for (; i < block.Length; i++)
				{
					tokenCnt += TsString.IsDoubleChar(block[i]) ? 2 : 1;

					if (tokenCnt > maxMessageSize)
					{
						if (behaviour == LongTextBehaviour.Drop)
							return Enumerable.Empty<string>();

						filled = true;
						break;
					}

					for (int j = 0; j < SeparatorWeight.Length; j++)
					{
						if (block[i] == SeparatorWeight[j])
						{
							splitIndices[j] = new Ind(i, tokenCnt);
						}
					}
				}

				if (!filled)
				{
					list.Add(block.NewUtf8String());
					break;
				}

				bool hasSplit = false;
				if (behaviour != LongTextBehaviour.SplitHard)
				{
					for (int j = 0; j < SeparatorWeight.Length; j++)
					{
						if (!hasSplit && splitIndices[j].i > 0)
						{
							list.Add(block.Slice(0, splitIndices[j].i + 1).NewUtf8String());
							block = block.Slice(splitIndices[j].i + 1);
							hasSplit = true;
						}
					}
					splitIndices.Fill(new Ind());
				}

				if (!hasSplit)
				{
					// UTF-8 adjustment
					while (i > 0 && (block[i] & 0xC0) == 0x80)
						i--;

					list.Add(block.Slice(0, i).NewUtf8String());
					block = block.Slice(i);
				}

				if (--limit == 0)
					break;
			}
			return list;
		}

		private readonly struct Ind
		{
			public readonly int i;
			public readonly int tok;

			public Ind(int i, int tok)
			{
				this.i = i;
				this.tok = tok;
			}

			public override readonly string ToString() => $"i:{i} tok:{tok}";
		}
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/TextMod.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;

namespace TS3AudioBot.CommandSystem.Text
{
	public readonly struct TextMod : IEquatable<TextMod>
	{
		public static readonly TextMod None = new TextMod(0, null);

		public TextModFlag Flags { get; }
		public Color? HasColor { get; }

		public TextMod(TextModFlag flags, Color? color = null)
		{
			Flags = flags;
			HasColor = color;
		}

		public readonly TextMod Color(Color color) => new TextMod(Flags | TextModFlag.Color, color);
		public readonly TextMod Bold() => new TextMod(Flags | TextModFlag.Bold, HasColor);
		public readonly TextMod Italic() => new TextMod(Flags | TextModFlag.Italic, HasColor);
		public readonly TextMod Strike() => new TextMod(Flags | TextModFlag.Strike, HasColor);
		public readonly TextMod Underline() => new TextMod(Flags | TextModFlag.Underline, HasColor);

		public static string Format(AppliedTextMod format, params AppliedTextMod[] para)
			=> new TextModBuilder().AppendFormat(format, para).ToString();

		public static string Format(bool color, AppliedTextMod format, params AppliedTextMod[] para)
		{
			if (color)
				return Format(format, para);
			if (string.IsNullOrEmpty(format.Text))
				return string.Empty;
			return string.Format(format.Text, para);
		}

		public readonly bool Equals(TextMod other) => Flags == other.Flags && HasColor == other.HasColor;
		public override readonly bool Equals(object? obj) => obj is TextMod tm && Equals(tm);
		public static bool operator ==(TextMod a, TextMod b) => a.Flags == b.Flags && a.HasColor == b.HasColor;
		public static bool operator !=(TextMod a, TextMod b) => a.Flags != b.Flags || a.HasColor != b.HasColor;
		public override readonly int GetHashCode() => ((int)Flags << 28) | HasColor.GetHashCode();
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/TextModBuilder.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;
using System.Text;
using System.Text.RegularExpressions;

namespace TS3AudioBot.CommandSystem.Text
{
	public class TextModBuilder
	{
		private readonly bool color;
		private readonly StringBuilder strb;
		private TextMod cur = TextMod.None;

		public int Length { get => strb.Length; set => strb.Length = value; }

		public TextModBuilder(bool color = true)
			: this(new StringBuilder(), color) { }

		public TextModBuilder(StringBuilder strb, bool color = true)
		{
			this.strb = strb ?? throw new ArgumentNullException(nameof(strb));
			this.color = color;
		}

		public TextModBuilder Append(AppliedTextMod atm) => Append(atm.Text, atm.Mod);

		public TextModBuilder Append(string? text, TextMod mod)
		{
			if (color)
				StartText(strb, text, ref cur, mod);
			else
				strb.Append(text);
			return this;
		}

		public TextModBuilder AppendLine(AppliedTextMod atm)
		{
			Append(atm.Text, atm.Mod);
			strb.Append('\n');
			return this;
		}

		public TextModBuilder AppendLine(string? text, TextMod mod)
		{
			Append(text, mod);
			strb.Append('\n');
			return this;
		}

		public TextModBuilder AppendFormat(AppliedTextMod format, params AppliedTextMod[] para)
		{
			if (para.Length == 0)
			{
				Append(format);
			}
			else
			{
				var parts = Regex.Split(format.Text, @"{\d+}");

				for (int i = 0; i < parts.Length - 1; i++)
				{
					Append(parts[i], format.Mod);
					Append(para[i]);
				}
				Append(parts[^1], format.Mod);
			}

			return this;
		}

		private static void StartText(StringBuilder strb, string? text, ref TextMod cur, TextMod mod)
		{
			if (string.IsNullOrEmpty(text))
				return;
			var curFlags = cur.Flags;
			var modFlags = mod.Flags;
			var close = curFlags & ~modFlags;
			if ((curFlags & modFlags).HasFlag(TextModFlag.Color) && cur.HasColor != mod.HasColor) close |= TextModFlag.Color;
			var trimClose = GetShortest(close);
			curFlags = End(strb, curFlags, trimClose);
			curFlags &= (~(trimClose - 1) | modFlags);
			curFlags = Start(strb, curFlags, mod);
			cur = new TextMod(curFlags, mod.HasColor);
			strb.Append(text);
		}

		private static TextModFlag Start(StringBuilder strb, TextModFlag cur, TextMod mod)
		{
			var flag = ~cur & mod.Flags;
			if (flag.HasFlag(TextModFlag.Bold))
				strb.Append("[B]");
			if (flag.HasFlag(TextModFlag.Italic))
				strb.Append("[I]");
			if (flag.HasFlag(TextModFlag.Strike))
				strb.Append("[S]");
			if (flag.HasFlag(TextModFlag.Underline))
				strb.Append("[U]");
			if (flag.HasFlag(TextModFlag.Color))
				mod.HasColor!.Value.GetL(strb);
			return cur | mod.Flags;
		}

		private static TextModFlag End(StringBuilder strb, TextModFlag cur, TextModFlag mod)
		{
			var flag = mod;
			if (flag.HasFlag(TextModFlag.Bold))
				strb.Append("[/B]");
			if (flag.HasFlag(TextModFlag.Italic))
				strb.Append("[/I]");
			if (flag.HasFlag(TextModFlag.Strike))
				strb.Append("[/S]");
			if (flag.HasFlag(TextModFlag.Underline))
				strb.Append("[/U]");
			if (flag.HasFlag(TextModFlag.Color))
				strb.Append("[/COLOR]");
			return cur & ~mod;
		}

		private static TextModFlag GetShortest(TextModFlag mod)
		{
			if (mod.HasFlag(TextModFlag.Bold)) return TextModFlag.Bold;
			if (mod.HasFlag(TextModFlag.Italic)) return TextModFlag.Italic;
			if (mod.HasFlag(TextModFlag.Strike)) return TextModFlag.Strike;
			if (mod.HasFlag(TextModFlag.Underline)) return TextModFlag.Underline;
			if (mod.HasFlag(TextModFlag.Color)) return TextModFlag.Color;
			return 0;
		}

		public override string ToString() => strb.ToString();
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/TextModFlag.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

using System;

namespace TS3AudioBot.CommandSystem.Text
{
	[Flags]
	public enum TextModFlag
	{
		None = 0,
		Color = 1 << 0,
		Underline = 1 << 1,
		Strike = 1 << 2,
		Italic = 1 << 3,
		Bold = 1 << 4,
	}
}


================================================
FILE: TS3AudioBot/CommandSystem/Text/TextModHelper.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// You should have received a copy of the Open Software License along with this
// program. If not, see <https://opensource.org/licenses/OSL-3.0>.

namespace TS3AudioBot.CommandSystem.Text
{
	public static class TextModHelper
	{
		public static AppliedTextMod Mod(this string str) => str;
	}
}


================================================
FILE: TS3AudioBot/Config/Config.cs
================================================
// TS3AudioBot - An advanced Musicbot for Teamspeak 3
// Copyright (C) 2017  TS3AudioBot contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Open Software License v. 3.0
//
// Yo
Download .txt
gitextract_1muog4nl/

├── ClassLibrary4.csproj
├── ClassLibrary4.sln
├── LICENSE
├── README.md
├── TS3AudioBot/
│   ├── Algorithm/
│   │   ├── IFilterAlgorithm.cs
│   │   ├── LruCache.cs
│   │   └── TimedCache.cs
│   ├── Audio/
│   │   ├── AudioValues.cs
│   │   ├── CustomTargetPipe.cs
│   │   ├── FfmpegProducer.cs
│   │   ├── IPlayerSource.cs
│   │   ├── IVoiceTarget.cs
│   │   ├── PlayInfo.cs
│   │   ├── PlayInfoEventArgs.cs
│   │   ├── PlayManager.cs
│   │   ├── Player.cs
│   │   ├── SongEndEventArgs.cs
│   │   ├── SongInfoChanged.cs
│   │   ├── StallCheckPipe.cs
│   │   └── StreamAudioPlayerSource.cs
│   ├── Bot.cs
│   ├── BotManager.cs
│   ├── CallerInfo.cs
│   ├── ClientCall.cs
│   ├── CommandSystem/
│   │   ├── Ast/
│   │   │   ├── AstCommand.cs
│   │   │   ├── AstError.cs
│   │   │   ├── AstNode.cs
│   │   │   ├── AstType.cs
│   │   │   ├── AstValue.cs
│   │   │   └── StringType.cs
│   │   ├── BotCommand.cs
│   │   ├── CommandAttribute.cs
│   │   ├── CommandException.cs
│   │   ├── CommandManager.cs
│   │   ├── CommandParser.cs
│   │   ├── CommandResults/
│   │   │   ├── IAudioResourceResult.cs
│   │   │   ├── IWrappedResult.cs
│   │   │   ├── PickObjectCommand.cs
│   │   │   └── TailString.cs
│   │   ├── CommandSystemExtensions.cs
│   │   ├── CommandSystemTypes.cs
│   │   ├── Commands/
│   │   │   ├── AliasCommand.cs
│   │   │   ├── AppliedCommand.cs
│   │   │   ├── CommandGroup.cs
│   │   │   ├── FunctionCommand.cs
│   │   │   ├── ICommand.cs
│   │   │   ├── LazyCommand.cs
│   │   │   ├── OverloadedFunctionCommand.cs
│   │   │   ├── ResultCommand.cs
│   │   │   └── RootCommand.cs
│   │   ├── ExecutionInformation.cs
│   │   ├── ICommandBag.cs
│   │   ├── StaticList.cs
│   │   └── Text/
│   │       ├── AppliedTextMod.cs
│   │       ├── Color.cs
│   │       ├── LongTextBehaviour.cs
│   │       ├── LongTextTransform.cs
│   │       ├── TextMod.cs
│   │       ├── TextModBuilder.cs
│   │       ├── TextModFlag.cs
│   │       └── TextModHelper.cs
│   ├── Config/
│   │   ├── Config.cs
│   │   ├── ConfigArray.cs
│   │   ├── ConfigDynamicTable.cs
│   │   ├── ConfigEnumerable.cs
│   │   ├── ConfigHelper.cs
│   │   ├── ConfigPart.cs
│   │   ├── ConfigStructs.cs
│   │   ├── ConfigTable.cs
│   │   ├── ConfigUpgrade2.cs
│   │   └── ConfigValue.cs
│   ├── Core.cs
│   ├── DbStore.cs
│   ├── Dependency/
│   │   ├── BasicInjector.cs
│   │   ├── ChainedInjector.cs
│   │   ├── DependencyBuilder.cs
│   │   ├── IInjector.cs
│   │   ├── InjectorExtensions.cs
│   │   ├── Module.cs
│   │   └── NullInjector.cs
│   ├── Environment/
│   │   ├── Stats.cs
│   │   ├── SystemData.cs
│   │   └── SystemMonitor.cs
│   ├── Error.cs
│   ├── Helper/
│   │   ├── AttributeStrings.cs
│   │   ├── Const.cs
│   │   ├── Diagnose/
│   │   │   ├── SelfDiagnoseLevel.cs
│   │   │   └── SelfDiagnoseMessage.cs
│   │   ├── IJsonConfig.cs
│   │   ├── ImageUtil.cs
│   │   ├── Interactive.cs
│   │   ├── LimitStream.cs
│   │   ├── TextUtil.cs
│   │   ├── TomlTools.cs
│   │   ├── Util.cs
│   │   └── WebWrapper.cs
│   ├── History/
│   │   ├── AudioLogEntry.cs
│   │   ├── HistoryManager.cs
│   │   ├── HistorySaveData.cs
│   │   ├── IHistoryFormatter.cs
│   │   ├── SearchQuery.cs
│   │   └── SmartHistoryFormatter.cs
│   ├── InvokerData.cs
│   ├── Limits.cs
│   ├── Localization/
│   │   ├── DynamicResourceManager.cs
│   │   ├── LocalStr.cs
│   │   ├── LocalizationManager.cs
│   │   ├── strings.Designer.cs
│   │   └── strings.resx
│   ├── MainCommands.cs
│   ├── Playlists/
│   │   ├── LoopMode.cs
│   │   ├── Parser/
│   │   │   └── JspfContent.cs
│   │   ├── Playlist.cs
│   │   ├── PlaylistApiExtensions.cs
│   │   ├── PlaylistIO.cs
│   │   ├── PlaylistItem.cs
│   │   ├── PlaylistManager.cs
│   │   └── Shuffle/
│   │       ├── IShuffleAlgorithm.cs
│   │       ├── LinearFeedbackShiftRegister.cs
│   │       ├── ListedShuffle.cs
│   │       └── NormalOrder.cs
│   ├── Plugins/
│   │   ├── ITabPlugin.cs
│   │   ├── Plugin.cs
│   │   ├── PluginCommandBag.cs
│   │   ├── PluginExtensions.cs
│   │   ├── PluginManager.cs
│   │   ├── PluginObjects.cs
│   │   ├── PluginResponse.cs
│   │   └── PluginStatus.cs
│   ├── Properties/
│   │   └── PublishProfiles/
│   │       ├── FolderProfile.pubxml
│   │       └── FolderProfile.pubxml.user
│   ├── Properties.cs
│   ├── ResourceFactories/
│   │   ├── AudioResource.cs
│   │   ├── AudioTags/
│   │   │   ├── AudioTagReader.cs
│   │   │   ├── BinaryReaderBigEndianExtensions.cs
│   │   │   └── M3uReader.cs
│   │   ├── BandcampResolver.cs
│   │   ├── IPlaylistResolver.cs
│   │   ├── IResolver.cs
│   │   ├── IResourceResolver.cs
│   │   ├── ISearchResolver.cs
│   │   ├── IThumbnailResolver.cs
│   │   ├── MatchCertainty.cs
│   │   ├── MediaResolver.cs
│   │   ├── PlayResource.cs
│   │   ├── ResolveContext.cs
│   │   ├── ResourceResolver.cs
│   │   ├── SongInfo.cs
│   │   ├── SoundcloudResolver.cs
│   │   ├── TwitchResolver.cs
│   │   ├── Youtube/
│   │   │   ├── Json.cs
│   │   │   ├── LoaderPriority.cs
│   │   │   ├── VideoCodec.cs
│   │   │   ├── VideoData.cs
│   │   │   └── YoutubeResolver.cs
│   │   └── YoutubeDlHelper.cs
│   ├── Resources/
│   │   ├── DefaultRights.toml
│   │   └── NLog.config
│   ├── Rights/
│   │   ├── CreateFileSettings.cs
│   │   ├── ExecuteContext.cs
│   │   ├── Matchers/
│   │   │   ├── MatchApiCallerIp.cs
│   │   │   ├── MatchBot.cs
│   │   │   ├── MatchChannelGroupId.cs
│   │   │   ├── MatchClientGroupId.cs
│   │   │   ├── MatchClientUid.cs
│   │   │   ├── MatchHost.cs
│   │   │   ├── MatchIsApi.cs
│   │   │   ├── MatchPermission.cs
│   │   │   ├── MatchToken.cs
│   │   │   ├── MatchVisibility.cs
│   │   │   ├── Matcher.cs
│   │   │   └── PermCompare.cs
│   │   ├── ParseContext.cs
│   │   ├── RightsDecl.cs
│   │   ├── RightsGroup.cs
│   │   ├── RightsManager.cs
│   │   └── RightsRule.cs
│   ├── Sessions/
│   │   ├── AnonymousSession.cs
│   │   ├── ApiToken.cs
│   │   ├── SessionManager.cs
│   │   ├── TokenManager.cs
│   │   └── UserSession.cs
│   ├── Setup.cs
│   ├── TS3AudioBot.csproj
│   ├── TS3AudioBot.csproj.user
│   ├── Ts3Client.cs
│   ├── Upgrader.cs
│   ├── Web/
│   │   ├── Api/
│   │   │   ├── ApiCall.cs
│   │   │   ├── DataStream.cs
│   │   │   ├── JsonArray.cs
│   │   │   ├── JsonEmpty.cs
│   │   │   ├── JsonError.cs
│   │   │   ├── JsonObject.cs
│   │   │   ├── JsonValue.cs
│   │   │   ├── OpenApiGenerator.cs
│   │   │   ├── TimeSpanConverter.cs
│   │   │   └── WebApi.cs
│   │   ├── Model/
│   │   │   ├── CurrentSongInfo.cs
│   │   │   ├── PlaylistInfo.cs
│   │   │   ├── PlaylistItemGetData.cs
│   │   │   └── QueueInfo.cs
│   │   └── WebServer.cs
│   ├── build.csx
│   └── obj/
│       ├── Debug/
│       │   ├── TS3AudioBot.1.0.0.nuspec
│       │   ├── net7.0/
│       │   │   └── TS3AudioBot.csproj.AssemblyReference.cache
│       │   ├── net7.0-windows/
│       │   │   ├── .NETCoreApp,Version=v7.0.AssemblyAttributes.cs
│       │   │   ├── TS3AudioBot.AssemblyInfo.cs
│       │   │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │   │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TS3AudioBot.Localization.strings.resources
│       │   │   ├── TS3AudioBot.assets.cache
│       │   │   ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │   │   ├── TS3AudioBot.csproj.CopyComplete
│       │   │   ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │   │   ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │   │   ├── TS3AudioBot.csproj.GenerateResource.cache
│       │   │   ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │   │   ├── TS3AudioBot.dll.config
│       │   │   ├── TS3AudioBot.genruntimeconfig.cache
│       │   │   └── TS3AudioBot.pdb
│       │   └── netcoreapp3.1/
│       │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       ├── TS3AudioBot.AssemblyInfo.cs
│       │       ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TS3AudioBot.Localization.strings.resources
│       │       ├── TS3AudioBot.assets.cache
│       │       ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       ├── TS3AudioBot.csproj.CopyComplete
│       │       ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       ├── TS3AudioBot.dll.config
│       │       ├── TS3AudioBot.genruntimeconfig.cache
│       │       └── TS3AudioBot.pdb
│       ├── Release/
│       │   ├── net7.0-windows/
│       │   │   ├── .NETCoreApp,Version=v7.0.AssemblyAttributes.cs
│       │   │   ├── TS3AudioBot.AssemblyInfo.cs
│       │   │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │   │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TS3AudioBot.assets.cache
│       │   │   └── TS3AudioBot.csproj.AssemblyReference.cache
│       │   └── netcoreapp3.1/
│       │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       ├── PublishOutputs.25bc18e9a6.txt
│       │       ├── TS3AudioBot.AssemblyInfo.cs
│       │       ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TS3AudioBot.Localization.strings.resources
│       │       ├── TS3AudioBot.assets.cache
│       │       ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       ├── TS3AudioBot.csproj.CopyComplete
│       │       ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       ├── TS3AudioBot.dll.config
│       │       ├── TS3AudioBot.genruntimeconfig.cache
│       │       ├── TS3AudioBot.pdb
│       │       ├── linux-x64/
│       │       │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │       │   ├── PublishOutputs.c54adf16a2.txt
│       │       │   ├── TS3AudioBot.AssemblyInfo.cs
│       │       │   ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │       │   ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │       │   ├── TS3AudioBot.Localization.strings.resources
│       │       │   ├── TS3AudioBot.assets.cache
│       │       │   ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │       │   ├── TS3AudioBot.csproj.CopyComplete
│       │       │   ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │       │   ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │       │   ├── TS3AudioBot.csproj.GenerateResource.cache
│       │       │   ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │       │   ├── TS3AudioBot.deps.json
│       │       │   ├── TS3AudioBot.dll.config
│       │       │   ├── TS3AudioBot.genruntimeconfig.cache
│       │       │   ├── TS3AudioBot.pdb
│       │       │   └── apphost
│       │       └── win-x64/
│       │           ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │           ├── PublishOutputs.cf05aea114.txt
│       │           ├── TS3AudioBot.AssemblyInfo.cs
│       │           ├── TS3AudioBot.AssemblyInfoInputs.cache
│       │           ├── TS3AudioBot.GeneratedMSBuildEditorConfig.editorconfig
│       │           ├── TS3AudioBot.Localization.strings.resources
│       │           ├── TS3AudioBot.assets.cache
│       │           ├── TS3AudioBot.csproj.AssemblyReference.cache
│       │           ├── TS3AudioBot.csproj.CopyComplete
│       │           ├── TS3AudioBot.csproj.CoreCompileInputs.cache
│       │           ├── TS3AudioBot.csproj.FileListAbsolute.txt
│       │           ├── TS3AudioBot.csproj.GenerateResource.cache
│       │           ├── TS3AudioBot.csproj.SuggestedBindingRedirects.cache
│       │           ├── TS3AudioBot.deps.json
│       │           ├── TS3AudioBot.dll.config
│       │           ├── TS3AudioBot.genruntimeconfig.cache
│       │           └── TS3AudioBot.pdb
│       ├── TS3AudioBot.csproj.nuget.dgspec.json
│       ├── TS3AudioBot.csproj.nuget.g.props
│       ├── TS3AudioBot.csproj.nuget.g.targets
│       ├── project.assets.json
│       ├── project.nuget.cache
│       └── publish/
│           ├── linux-x64/
│           │   ├── TS3AudioBot.csproj.nuget.dgspec.json
│           │   ├── TS3AudioBot.csproj.nuget.g.props
│           │   ├── TS3AudioBot.csproj.nuget.g.targets
│           │   ├── project.assets.json
│           │   └── project.nuget.cache
│           └── win-x64/
│               ├── TS3AudioBot.csproj.nuget.dgspec.json
│               ├── TS3AudioBot.csproj.nuget.g.props
│               ├── TS3AudioBot.csproj.nuget.g.targets
│               ├── project.assets.json
│               └── project.nuget.cache
├── TSLib/
│   ├── Audio/
│   │   ├── AudioInterfaces.cs
│   │   ├── AudioMeta.cs
│   │   ├── AudioPacketReader.cs
│   │   ├── AudioPipeExtensions.cs
│   │   ├── AudioTools.cs
│   │   ├── CheckActivePipe.cs
│   │   ├── ClientMixdown.cs
│   │   ├── DecoderPipe.cs
│   │   ├── EncoderPipe.cs
│   │   ├── Opus/
│   │   │   ├── LICENSE
│   │   │   ├── NativeMethods.cs
│   │   │   ├── OPUS_LICENSE
│   │   │   ├── OpusDecoder.cs
│   │   │   ├── OpusEncoder.cs
│   │   │   └── README
│   │   ├── PassiveMergePipe.cs
│   │   ├── PassiveSplitterPipe.cs
│   │   ├── PreciseAudioTimer.cs
│   │   ├── PreciseTimedPipe.cs
│   │   ├── StaticMetaPipe.cs
│   │   ├── StreamAudioProducer.cs
│   │   └── VolumePipe.cs
│   ├── Commands/
│   │   ├── CommandMultiParameter.cs
│   │   ├── CommandOption.cs
│   │   ├── CommandParameter.cs
│   │   ├── ICommandPart.cs
│   │   ├── TsCommand.cs
│   │   ├── TsCommand.gen.cs
│   │   ├── TsCommand.gen.tt
│   │   ├── TsConst.cs
│   │   └── TsString.cs
│   ├── ConnectionData.cs
│   ├── DisconnectEventArgs.cs
│   ├── EventDispatcher.cs
│   ├── Full/
│   │   ├── Book/
│   │   │   ├── Book.cs
│   │   │   └── SpecialTypes.cs
│   │   ├── GenerationWindow.cs
│   │   ├── IdentityData.cs
│   │   ├── License.cs
│   │   ├── NetworkStats.cs
│   │   ├── Packet.cs
│   │   ├── PacketHandler.cs
│   │   ├── PacketType.cs
│   │   ├── QuickerLz.cs
│   │   ├── RingQueue.cs
│   │   ├── TsCrypt.cs
│   │   ├── TsFullClient.cs
│   │   ├── TsFullClient.gen.cs
│   │   └── TsFullClient.gen.tt
│   ├── Generated/
│   │   ├── Book.cs
│   │   ├── Book.tt
│   │   ├── BookParser.ttinclude
│   │   ├── ErrorParser.ttinclude
│   │   ├── M2B.cs
│   │   ├── M2B.tt
│   │   ├── M2BParser.ttinclude
│   │   ├── MessageParser.ttinclude
│   │   ├── Messages.cs
│   │   ├── Messages.tt
│   │   ├── NotificationUtil.ttinclude
│   │   ├── TsErrorCode.cs
│   │   ├── TsErrorCode.tt
│   │   ├── TsPermission.cs
│   │   ├── TsPermission.tt
│   │   ├── TsVersion.gen.cs
│   │   ├── TsVersion.gen.tt
│   │   └── Util.ttinclude
│   ├── Helper/
│   │   ├── AsyncEventHandler.cs
│   │   ├── CommandErrorExtensions.cs
│   │   ├── DebugUtil.cs
│   │   ├── LogId.cs
│   │   ├── MissingEnumCaseException.cs
│   │   ├── NativeLibraryLoader.cs
│   │   ├── R.cs
│   │   ├── SpanExtensions.cs
│   │   ├── SpanSplitter.cs
│   │   └── Tools.cs
│   ├── LazyNotification.cs
│   ├── MessageProcessor.cs
│   ├── Messages/
│   │   ├── BaseTypes.cs
│   │   ├── Deserializer.cs
│   │   ├── MessageAdditions.cs
│   │   ├── PermissionTransform.cs
│   │   └── ResponseDictionary.cs
│   ├── Properties.cs
│   ├── Query/
│   │   ├── TsQueryClient.cs
│   │   ├── TsQueryClient.gen.cs
│   │   └── TsQueryClient.gen.tt
│   ├── Scheduler/
│   │   ├── DedicatedTaskScheduler.cs
│   │   ├── DispatcherHelper.cs
│   │   └── TickWorker.cs
│   ├── TSLib.csproj
│   ├── TsBaseFunctions.FileTransfer.cs
│   ├── TsBaseFunctions.cs
│   ├── TsBaseFunctions.gen.cs
│   ├── TsBaseFunctions.gen.tt
│   ├── TsDnsResolver.cs
│   ├── TsEnums.cs
│   ├── TsPermissionHelper.cs
│   ├── TsVersion.cs
│   ├── Types.cs
│   ├── Types.gen.cs
│   ├── Types.gen.tt
│   ├── WaitBlock.cs
│   ├── bin/
│   │   ├── Debug/
│   │   │   ├── netcoreapp3.1/
│   │   │   │   ├── TSLib.deps.json
│   │   │   │   └── TSLib.pdb
│   │   │   ├── netstandard2.0/
│   │   │   │   ├── TSLib.deps.json
│   │   │   │   └── TSLib.pdb
│   │   │   └── netstandard2.1/
│   │   │       ├── TSLib.deps.json
│   │   │       └── TSLib.pdb
│   │   └── Release/
│   │       ├── netcoreapp3.1/
│   │       │   ├── TSLib.deps.json
│   │       │   └── TSLib.pdb
│   │       ├── netstandard2.0/
│   │       │   ├── TSLib.deps.json
│   │       │   └── TSLib.pdb
│   │       └── netstandard2.1/
│   │           ├── TSLib.deps.json
│   │           └── TSLib.pdb
│   ├── dnc2_compat/
│   │   ├── Extensions.cs
│   │   ├── Range.cs
│   │   └── info.txt
│   └── obj/
│       ├── Debug/
│       │   ├── netcoreapp3.1/
│       │   │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   ├── netstandard2.0/
│       │   │   ├── .NETStandard,Version=v2.0.AssemblyAttributes.cs
│       │   │   ├── NuGet/
│       │   │   │   ├── 2C8E6E8C03FF0327F78E3CB90559803756F36314/
│       │   │   │   │   └── Nullable/
│       │   │   │   │       └── 1.2.1/
│       │   │   │   │           └── Nullable/
│       │   │   │   │               └── NullableAttributes.cs
│       │   │   │   └── 7BA94E4E53727142735FA3B08F79617CD03664FD/
│       │   │   │       └── Nullable/
│       │   │   │           └── 1.2.1/
│       │   │   │               └── Nullable/
│       │   │   │                   └── NullableAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   └── netstandard2.1/
│       │       ├── .NETStandard,Version=v2.1.AssemblyAttributes.cs
│       │       ├── TSLib.AssemblyInfo.cs
│       │       ├── TSLib.AssemblyInfoInputs.cache
│       │       ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TSLib.assets.cache
│       │       ├── TSLib.csproj.AssemblyReference.cache
│       │       ├── TSLib.csproj.CoreCompileInputs.cache
│       │       ├── TSLib.csproj.FileListAbsolute.txt
│       │       └── TSLib.pdb
│       ├── Release/
│       │   ├── netcoreapp3.1/
│       │   │   ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   ├── netstandard2.0/
│       │   │   ├── .NETStandard,Version=v2.0.AssemblyAttributes.cs
│       │   │   ├── NuGet/
│       │   │   │   ├── 2C8E6E8C03FF0327F78E3CB90559803756F36314/
│       │   │   │   │   └── Nullable/
│       │   │   │   │       └── 1.2.1/
│       │   │   │   │           └── Nullable/
│       │   │   │   │               └── NullableAttributes.cs
│       │   │   │   └── 7BA94E4E53727142735FA3B08F79617CD03664FD/
│       │   │   │       └── Nullable/
│       │   │   │           └── 1.2.1/
│       │   │   │               └── Nullable/
│       │   │   │                   └── NullableAttributes.cs
│       │   │   ├── TSLib.AssemblyInfo.cs
│       │   │   ├── TSLib.AssemblyInfoInputs.cache
│       │   │   ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │   │   ├── TSLib.assets.cache
│       │   │   ├── TSLib.csproj.AssemblyReference.cache
│       │   │   ├── TSLib.csproj.CoreCompileInputs.cache
│       │   │   ├── TSLib.csproj.FileListAbsolute.txt
│       │   │   └── TSLib.pdb
│       │   └── netstandard2.1/
│       │       ├── .NETStandard,Version=v2.1.AssemblyAttributes.cs
│       │       ├── TSLib.AssemblyInfo.cs
│       │       ├── TSLib.AssemblyInfoInputs.cache
│       │       ├── TSLib.GeneratedMSBuildEditorConfig.editorconfig
│       │       ├── TSLib.assets.cache
│       │       ├── TSLib.csproj.AssemblyReference.cache
│       │       ├── TSLib.csproj.CoreCompileInputs.cache
│       │       ├── TSLib.csproj.FileListAbsolute.txt
│       │       └── TSLib.pdb
│       ├── TSLib.csproj.nuget.dgspec.json
│       ├── TSLib.csproj.nuget.g.props
│       ├── TSLib.csproj.nuget.g.targets
│       ├── project.assets.json
│       └── project.nuget.cache
├── YunBot.cs
└── obj/
    ├── ClassLibrary4.csproj.nuget.dgspec.json
    ├── ClassLibrary4.csproj.nuget.g.props
    ├── ClassLibrary4.csproj.nuget.g.targets
    ├── Debug/
    │   ├── netcoreapp3.0/
    │   │   ├── ClassLibrary4.assets.cache
    │   │   └── ClassLibrary4.csproj.FileListAbsolute.txt
    │   └── netcoreapp3.1/
    │       ├── .NETCoreApp,Version=v3.1.AssemblyAttributes.cs
    │       ├── ClassLibrary4.AssemblyInfo.cs
    │       ├── ClassLibrary4.AssemblyInfoInputs.cache
    │       ├── ClassLibrary4.GeneratedMSBuildEditorConfig.editorconfig
    │       ├── ClassLibrary4.assets.cache
    │       ├── ClassLibrary4.csproj.AssemblyReference.cache
    │       ├── ClassLibrary4.csproj.CopyComplete
    │       ├── ClassLibrary4.csproj.CoreCompileInputs.cache
    │       ├── ClassLibrary4.csproj.FileListAbsolute.txt
    │       └── YunBot.pdb
    ├── project.assets.json
    └── project.nuget.cache
Download .txt
Showing preview only (352K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3673 symbols across 279 files)

FILE: TS3AudioBot/Algorithm/IFilterAlgorithm.cs
  type IFilter (line 15) | public interface IFilter
    method Filter (line 17) | IEnumerable<KeyValuePair<string, T>> Filter<T>(IEnumerable<KeyValuePai...
  class Filter (line 20) | public static class Filter
    method GetFilterByName (line 24) | public static IFilter? GetFilterByName(string filter)
    method GetFilterByNameOrDefault (line 36) | public static IFilter GetFilterByNameOrDefault(string filter) => GetFi...
  class Ic3Filter (line 40) | internal sealed class Ic3Filter : IFilter
    method Ic3Filter (line 42) | private Ic3Filter() { }
    method Filter (line 46) | IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<Key...
  class ExactFilter (line 70) | internal sealed class ExactFilter : IFilter
    method ExactFilter (line 72) | private ExactFilter() { }
    method Filter (line 76) | IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<Key...
  class HammingFilter (line 82) | internal sealed class HammingFilter : IFilter
    method HammingFilter (line 84) | private HammingFilter() { }
    method Filter (line 88) | IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<Key...
  class SubstringFilter (line 94) | internal sealed class SubstringFilter : IFilter
    method SubstringFilter (line 96) | private SubstringFilter() { }
    method Filter (line 100) | IEnumerable<KeyValuePair<string, T>> IFilter.Filter<T>(IEnumerable<Key...

FILE: TS3AudioBot/Algorithm/LruCache.cs
  class LruCache (line 15) | public class LruCache<TK, TV> where TK : notnull
    method LruCache (line 21) | public LruCache(int capacity)
    method TryGetValue (line 26) | public bool TryGetValue(TK key, [MaybeNullWhen(false)] out TV value)
    method Set (line 38) | public void Set(TK key, TV value)
    method Remove (line 54) | public bool Remove(TK key) => cacheDict.Remove(key);
    method Renew (line 56) | private void Renew(LinkedListNode<(TK, TV)> node)
    method RemoveOldest (line 62) | private void RemoveOldest()
    method Clear (line 71) | public void Clear()

FILE: TS3AudioBot/Algorithm/TimedCache.cs
  class TimedCache (line 18) | public class TimedCache<TK, TV> where TK : notnull
    method TimedCache (line 23) | public TimedCache() : this(TimeSpan.FromSeconds(3)) { }
    method TimedCache (line 25) | public TimedCache(TimeSpan timeout)
    method TryGetValue (line 31) | public bool TryGetValue(TK key, [MaybeNullWhen(false)] out TV value)
    method Set (line 44) | public void Set(TK key, TV value)
    method Clear (line 49) | public void Clear()
    method CleanCache (line 54) | private void CleanCache()
    type TimedData (line 63) | private struct TimedData

FILE: TS3AudioBot/Audio/AudioValues.cs
  class AudioValues (line 15) | public static class AudioValues
    method HumanVolumeToFactor (line 27) | public static float HumanVolumeToFactor(float value)
    method FactorToHumanVolume (line 39) | public static float FactorToHumanVolume(float value)

FILE: TS3AudioBot/Audio/CustomTargetPipe.cs
  class CustomTargetPipe (line 20) | internal class CustomTargetPipe : IVoiceTarget, IAudioPassiveConsumer
    method CustomTargetPipe (line 65) | public CustomTargetPipe(TsFullClient client)
    method Write (line 71) | public void Write(Span<byte> data, Meta? meta)
    method SetGroupWhisper (line 96) | public void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget ...
    method WhisperChannelSubscribe (line 103) | public void WhisperChannelSubscribe(bool temp, params ChannelId[] chan...
    method WhisperChannelUnsubscribe (line 122) | public void WhisperChannelUnsubscribe(bool temp, params ChannelId[] ch...
    method WhisperClientSubscribe (line 144) | public void WhisperClientSubscribe(params ClientId[] userId)
    method WhisperClientUnsubscribe (line 153) | public void WhisperClientUnsubscribe(params ClientId[] userId)
    method ClearTemporary (line 162) | public void ClearTemporary()
    method UpdatedSubscriptionCache (line 178) | private void UpdatedSubscriptionCache()

FILE: TS3AudioBot/Audio/FfmpegProducer.cs
  class FfmpegProducer (line 26) | public class FfmpegProducer : IPlayerSource, ISampleInfo, IDisposable
    method FfmpegProducer (line 49) | public FfmpegProducer(ConfToolsFfmpeg config, DedicatedTaskScheduler s...
    method AudioStart (line 56) | public Task AudioStart(string url, TimeSpan? startOff = null)
    method AudioStartIcy (line 62) | public async Task AudioStartIcy(string url) => await StartFfmpegProces...
    method AudioStop (line 64) | public void AudioStop()
    method Seek (line 73) | public Task Seek(TimeSpan position) { SetPosition(position); return Ta...
    method Read (line 75) | public int Read(byte[] buffer, int offset, int length, out Meta? meta)
    method OnReadEmpty (line 124) | private (bool ret, bool trigger) OnReadEmpty(FfmpegInstance instance)
    method OnReadEmptyIcy (line 155) | private (bool ret, bool trigger) OnReadEmptyIcy(FfmpegInstance instance)
    method SetPosition (line 178) | private R<FfmpegInstance, string> SetPosition(TimeSpan value)
    method StartFfmpegProcess (line 193) | private R<FfmpegInstance, string> StartFfmpegProcess(string url, TimeS...
    method StartFfmpegProcessIcy (line 220) | private async Task<R<FfmpegInstance, string>> StartFfmpegProcessIcy(st...
    method StartFfmpegProcessInternal (line 263) | private R<FfmpegInstance, string> StartFfmpegProcessInternal(FfmpegIns...
    method StopFfmpegProcess (line 303) | private void StopFfmpegProcess()
    method GetCurrentSongLength (line 313) | private TimeSpan? GetCurrentSongLength() => ffmpegInstance?.ParsedSong...
    method AssertNotMainScheduler (line 315) | private void AssertNotMainScheduler()
    method Dispose (line 321) | public void Dispose()
    class FfmpegInstance (line 326) | private class FfmpegInstance
      method FfmpegInstance (line 342) | public FfmpegInstance(string url, PreciseAudioTimer timer) : this(ur...
      method FfmpegInstance (line 343) | public FfmpegInstance(string url, PreciseAudioTimer timer, Stream ic...
      method Close (line 354) | public void Close()
      method FfmpegProcess_ErrorDataReceived (line 372) | public void FfmpegProcess_ErrorDataReceived(object sender, DataRecei...
      method ReadStreamLoop (line 399) | public void ReadStreamLoop(Id id)
      method ParseIcyMeta (line 480) | private static SongInfoChanged ParseIcyMeta(string metaString)

FILE: TS3AudioBot/Audio/IPlayerSource.cs
  type IPlayerSource (line 16) | public interface IPlayerSource : IAudioPassiveProducer
    method Seek (line 24) | Task Seek(TimeSpan position);

FILE: TS3AudioBot/Audio/IVoiceTarget.cs
  type IVoiceTarget (line 17) | public interface IVoiceTarget
    method SetGroupWhisper (line 23) | void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget target,...
    method WhisperChannelSubscribe (line 32) | void WhisperChannelSubscribe(bool temp, params ChannelId[] channel);
    method WhisperChannelUnsubscribe (line 37) | void WhisperChannelUnsubscribe(bool temp, params ChannelId[] channel);
    method ClearTemporary (line 38) | void ClearTemporary();
    method WhisperClientSubscribe (line 39) | void WhisperClientSubscribe(params ClientId[] userId);
    method WhisperClientUnsubscribe (line 40) | void WhisperClientUnsubscribe(params ClientId[] userId);

FILE: TS3AudioBot/Audio/PlayInfo.cs
  class PlayInfo (line 16) | public sealed class PlayInfo
    method PlayInfo (line 23) | public PlayInfo(TimeSpan? startOffset = null)
    method Merge (line 28) | public PlayInfo Merge(PlayInfo other) => Merge(this, other);
    method Merge (line 30) | [return: NotNullIfNotNull("self")]
    method MergeDefault (line 43) | public static PlayInfo MergeDefault(PlayInfo? self, PlayInfo? other)
  type IMetaContainer (line 47) | public interface IMetaContainer
  class MetaContainerExtensions (line 52) | public static class MetaContainerExtensions
    method MergeMeta (line 54) | public static T MergeMeta<T>(this T container, PlayInfo? other) where ...

FILE: TS3AudioBot/Audio/PlayInfoEventArgs.cs
  class PlayInfoEventArgs (line 15) | public sealed class PlayInfoEventArgs : EventArgs
    method PlayInfoEventArgs (line 23) | public PlayInfoEventArgs(InvokerData invoker, PlayResource playResourc...

FILE: TS3AudioBot/Audio/PlayManager.cs
  class PlayManager (line 25) | public class PlayManager
    method PlayManager (line 44) | public PlayManager(ConfBot config, Player playerConnection, PlaylistMa...
    method Enqueue (line 53) | public Task Enqueue(InvokerData invoker, AudioResource ar, PlayInfo? m...
    method Enqueue (line 54) | public async Task Enqueue(InvokerData invoker, string message, string?...
    method Enqueue (line 65) | public Task Enqueue(InvokerData invoker, IEnumerable<PlaylistItem> items)
    method Enqueue (line 71) | public Task Enqueue(InvokerData invoker, PlaylistItem item)
    method UpdateItem (line 78) | private static PlaylistItem UpdateItem(InvokerData invoker, PlaylistIt...
    method PostEnqueue (line 85) | private async Task PostEnqueue(InvokerData invoker, int startIndex)
    method Play (line 98) | public async Task Play(InvokerData invoker, AudioResource ar, PlayInfo...
    method Play (line 119) | public async Task Play(InvokerData invoker, string link, string? audio...
    method Play (line 131) | public Task Play(InvokerData invoker, IEnumerable<PlaylistItem> items,...
    method Play (line 139) | public Task Play(InvokerData invoker, PlaylistItem item)
    method Play (line 152) | public Task Play(InvokerData invoker) => StartCurrent(invoker);
    method Play (line 159) | public Task Play(InvokerData invoker, PlayResource play)
    method StartResource (line 168) | private async Task StartResource(InvokerData invoker, PlaylistItem item)
    method StartResource (line 181) | private async Task StartResource(InvokerData invoker, PlayResource play)
    method StartCurrent (line 206) | private async Task StartCurrent(InvokerData invoker, bool manually = t...
    method Next (line 222) | public async Task Next(InvokerData invoker, bool manually = true)
    method Previous (line 242) | public async Task Previous(InvokerData invoker, bool manually = true)
    method SongStoppedEvent (line 262) | public async Task SongStoppedEvent(object? sender, EventArgs e) => awa...
    method Stop (line 264) | public Task Stop() => StopInternal(false);
    method StopInternal (line 266) | private async Task StopInternal(bool songEndedByCallback)
    method Update (line 288) | public async Task Update(SongInfoChanged newInfo)
    method ParseAttributes (line 306) | public static PlayInfo? ParseAttributes(string[] attrs)

FILE: TS3AudioBot/Audio/Player.cs
  class Player (line 22) | public class Player : IDisposable
    method Player (line 36) | public Player(ConfRoot confRoot, ConfBot config, DedicatedTaskSchedule...
    method SetTarget (line 53) | public void SetTarget(IAudioPassiveConsumer target)
    method ScaleBitrate (line 59) | private static int ScaleBitrate(int value) => Tools.Clamp(value, 1, 25...
    method TriggerSongEnd (line 64) | private void TriggerSongEnd(object? o, EventArgs e) => scheduler.Invok...
    method TriggerSongUpdated (line 65) | private void TriggerSongUpdated(object? o, SongInfoChanged e) => sched...
    method Play (line 67) | public async Task Play(PlayResource res)
    method Play (line 76) | public void Play(IPlayerSource source)
    method CleanSource (line 95) | private void CleanSource(IPlayerSource? source)
    method Stop (line 105) | public void Stop()
    method StopAll (line 112) | public void StopAll()
    method Seek (line 123) | public Task Seek(TimeSpan position) => CurrentPlayerSource?.Seek(posit...
    method SetStall (line 139) | public void SetStall() => StallCheckPipe.SetStall();
    method MixInStreamOnce (line 141) | [Obsolete(AttributeStrings.UnderDevelopment)]
    method Dispose (line 153) | public void Dispose()

FILE: TS3AudioBot/Audio/SongEndEventArgs.cs
  class SongEndEventArgs (line 14) | public class SongEndEventArgs : EventArgs
    method SongEndEventArgs (line 17) | public SongEndEventArgs(bool songEndedByCallback) { SongEndedByCallbac...

FILE: TS3AudioBot/Audio/SongInfoChanged.cs
  class SongInfoChanged (line 14) | public class SongInfoChanged : EventArgs

FILE: TS3AudioBot/Audio/StallCheckPipe.cs
  class StallCheckPipe (line 15) | public class StallCheckPipe : IAudioPipe
    method StallCheckPipe (line 27) | public StallCheckPipe()
    method Write (line 33) | public void Write(Span<byte> data, Meta? meta)
    method SetStall (line 58) | public void SetStall()

FILE: TS3AudioBot/Audio/StreamAudioPlayerSource.cs
  class StreamAudioPlayerSource (line 16) | public class StreamAudioPlayerSource : IPlayerSource, IAudioActiveConsumer
    method StreamAudioPlayerSource (line 27) | public StreamAudioPlayerSource() { }
    method StreamAudioPlayerSource (line 29) | public StreamAudioPlayerSource(IAudioPassiveProducer stream) : this()
    method Read (line 34) | public int Read(byte[] buffer, int offset, int length, out Meta? meta)
    method Reset (line 53) | public void Reset() => hasFired = false;
    method Dispose (line 55) | public void Dispose() => InStream?.Dispose();
    method Seek (line 57) | public Task Seek(TimeSpan position) { throw new NotSupportedException(...

FILE: TS3AudioBot/Bot.cs
  class Bot (line 37) | public sealed class Bot
    method Bot (line 63) | public Bot(Id id, ConfBot config, BotInjector injector)
    method Run (line 181) | public Task<E<string>> Run()
    method Stop (line 189) | public async Task Stop()
    method OnBotConnected (line 212) | private async Task OnBotConnected(object? sender, EventArgs e)
    method OnBotDisconnected (line 228) | private async Task OnBotDisconnected(object? sender, DisconnectEventAr...
    method OnBotStoppedReconnecting (line 240) | private Task OnBotStoppedReconnecting(object? sender, EventArgs e)
    method OnMessageReceived (line 245) | private async Task OnMessageReceived(object? sender, TextMessage textM...
    method OnClientLeftView (line 336) | private void OnClientLeftView(object? sender, ClientLeftView eventArgs)
    method BeforeResourceStarted (line 342) | private async Task BeforeResourceStarted(object? sender, PlayInfoEvent...
    method AfterResourceStarted (line 365) | private async Task AfterResourceStarted(object? sender, PlayInfoEventA...
    method UpdateBotStatus (line 377) | public Task UpdateBotStatus()
    method UpdateBotStatusInternal (line 382) | private async Task UpdateBotStatusInternal()
    method GenerateStatusImage (line 405) | public Task GenerateStatusImage(bool playing, PlayInfoEventArgs? start...
    method GenerateStatusImageInternal (line 410) | private async Task GenerateStatusImageInternal(bool playing, PlayInfoE...
    method CallScript (line 475) | private async Task CallScript(ExecutionInformation info, string comman...
    method CreateExecInfo (line 502) | private ExecutionInformation CreateExecInfo(InvokerData? invoker = nul...
    method TryCatchCommand (line 513) | private async Task TryCatchCommand(ExecutionInformation info, bool ans...
    method OnIdle (line 544) | private async void OnIdle()
    method EnableIdleTickWorker (line 556) | private void EnableIdleTickWorker()
    method DisableIdleTickWorker (line 568) | private void DisableIdleTickWorker() => idleTickWorker.Disable();
    method OnAloneChanged (line 574) | private async Task OnAloneChanged(object? sender, AloneChanged e)
    method GetInfo (line 602) | public BotInfo GetInfo() => new BotInfo
  class BotInfo (line 611) | public class BotInfo
    method ToString (line 618) | public override string ToString() => $"Id: {Id} Name: {Name} Server: {...
  type BotStatus (line 621) | public enum BotStatus

FILE: TS3AudioBot/BotManager.cs
  class BotManager (line 22) | public class BotManager
    method BotManager (line 32) | public BotManager(ConfRoot confRoot, CoreInjector coreInjector)
    method RunBots (line 38) | public async Task RunBots(bool interactive)
    method CreateNewBot (line 104) | public ConfBot CreateNewBot() => confRoot.CreateBot();
    method CreateAndRunNewBot (line 106) | public Task<R<BotInfo, string>> CreateAndRunNewBot()
    method RunBotTemplate (line 112) | public async Task<R<BotInfo, string>> RunBotTemplate(string name)
    method RunBot (line 120) | public async Task<R<BotInfo, string>> RunBot(ConfBot config)
    method InstantiateNewBot (line 141) | private (Bot? bot, BotInfo? info) InstantiateNewBot(ConfBot config)
    method InsertBot (line 168) | private void InsertBot(Bot bot)
    method GetFreeId (line 177) | private int? GetFreeId()
    method GetBotSave (line 196) | private Bot? GetBotSave(int id)
    method GetBotSave (line 204) | private Bot? GetBotSave(string name)
    method GetBotLock (line 213) | public Bot? GetBotLock(int id)
    method GetBotLock (line 227) | public Bot? GetBotLock(string name)
    method IterateAll (line 241) | internal void IterateAll(Action<Bot> body)
    method StopBot (line 255) | public async Task StopBot(Bot bot)
    method RemoveBot (line 261) | internal void RemoveBot(Bot bot)
    method GetBotInfolist (line 275) | public BotInfo[] GetBotInfolist()
    method GetRunningBotCount (line 285) | public uint GetRunningBotCount()
    method StopBots (line 295) | public async Task StopBots()

FILE: TS3AudioBot/CallerInfo.cs
  class CallerInfo (line 12) | public class CallerInfo
    method CallerInfo (line 25) | public CallerInfo(bool isApi)

FILE: TS3AudioBot/ClientCall.cs
  class ClientCall (line 14) | public class ClientCall : InvokerData
    method ClientCall (line 27) | public ClientCall(Uid clientUid, string textMessage, ClientDbId? datab...

FILE: TS3AudioBot/CommandSystem/Ast/AstCommand.cs
  class AstCommand (line 15) | internal class AstCommand : AstNode
    method AstCommand (line 21) | public AstCommand(string fullRequest) : base(fullRequest) { }
    method Write (line 23) | public override void Write(StringBuilder strb, int depth)

FILE: TS3AudioBot/CommandSystem/Ast/AstError.cs
  class AstError (line 14) | internal class AstError : AstNode
    method AstError (line 20) | public AstError(AstNode referenceNode, string description)
    method AstError (line 23) | public AstError(string request, int pos, int len, string description) ...
    method Write (line 30) | public override void Write(StringBuilder strb, int depth)

FILE: TS3AudioBot/CommandSystem/Ast/AstNode.cs
  class AstNode (line 14) | public abstract class AstNode
    method AstNode (line 22) | protected AstNode(string fullRequest)
    method Write (line 27) | public abstract void Write(StringBuilder strb, int depth);
    method ToString (line 28) | public sealed override string ToString()
  class AstNodeExtensions (line 36) | internal static class AstNodeExtensions
    method Space (line 39) | public static StringBuilder Space(this StringBuilder strb, int depth) ...

FILE: TS3AudioBot/CommandSystem/Ast/AstType.cs
  type AstType (line 12) | public enum AstType

FILE: TS3AudioBot/CommandSystem/Ast/AstValue.cs
  class AstValue (line 14) | internal class AstValue : AstNode
    method AstValue (line 44) | public AstValue(string fullRequest, StringType stringType) : base(full...
    method Write (line 49) | public override void Write(StringBuilder strb, int depth) => strb.Spac...

FILE: TS3AudioBot/CommandSystem/Ast/StringType.cs
  type StringType (line 12) | internal enum StringType

FILE: TS3AudioBot/CommandSystem/BotCommand.cs
  class BotCommand (line 23) | [DebuggerDisplay("{DebuggerDisplay, nq}")]
    method BotCommand (line 76) | public BotCommand(CommandBuildInfo buildInfo) : base(buildInfo.Method,...
    method ToString (line 94) | public override string ToString()
    method Execute (line 122) | public override async ValueTask<object?> Execute(ExecutionInformation ...
  class CommandBuildInfo (line 135) | public class CommandBuildInfo
    method CommandBuildInfo (line 142) | public CommandBuildInfo(object? p, MethodInfo m, CommandAttribute comAtt)

FILE: TS3AudioBot/CommandSystem/CommandAttribute.cs
  class CommandAttribute (line 18) | [AttributeUsage(AttributeTargets.Method, Inherited = false)]
    method CommandAttribute (line 24) | public CommandAttribute(string commandNameSpace, string? overrideHelpN...
  class UsageAttribute (line 35) | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultipl...
    method UsageAttribute (line 41) | public UsageAttribute(string syntax, string help)

FILE: TS3AudioBot/CommandSystem/CommandException.cs
  class CommandException (line 16) | [Serializable]
    method CommandException (line 21) | protected CommandException()
    method CommandException (line 25) | public CommandException(string message)
    method CommandException (line 29) | protected CommandException(CommandExceptionReason reason)
    method CommandException (line 32) | public CommandException(string message, CommandExceptionReason reason)
    method CommandException (line 36) | public CommandException(string message, Exception inner, CommandExcept...
    method CommandException (line 40) | protected CommandException(SerializationInfo info, StreamingContext co...
  class MissingContextCommandException (line 45) | [Serializable]
    method MissingContextCommandException (line 50) | protected MissingContextCommandException(Type missingType)
    method MissingContextCommandException (line 54) | public MissingContextCommandException(string message, Type missingType)
    method MissingContextCommandException (line 58) | public MissingContextCommandException(string message, Exception inner,...
  class TeamSpeakErrorCommandException (line 63) | [Serializable]
    method TeamSpeakErrorCommandException (line 68) | protected TeamSpeakErrorCommandException(CommandError error)
    method TeamSpeakErrorCommandException (line 72) | public TeamSpeakErrorCommandException(string message, CommandError error)
    method TeamSpeakErrorCommandException (line 76) | public TeamSpeakErrorCommandException(string message, Exception inner,...
  type CommandExceptionReason (line 81) | public enum CommandExceptionReason

FILE: TS3AudioBot/CommandSystem/CommandManager.cs
  class CommandManager (line 29) | public class CommandManager
    method CommandManager (line 42) | public CommandManager(RightsManager? rightsManager)
    method RegisterCollection (line 53) | public void RegisterCollection(ICommandBag bag)
    method UnregisterCollection (line 74) | public void UnregisterCollection(ICommandBag bag)
    method RegisterAlias (line 86) | public E<LocalStr> RegisterAlias(string path, string command)
    method UnregisterAlias (line 100) | public E<LocalStr> UnregisterAlias(string path)
    method GetAlias (line 114) | public AliasCommand? GetAlias(string path) => aliasPaths.TryGetValue(p...
    method GetBotCommands (line 116) | public static IEnumerable<BotCommand> GetBotCommands(object? obj, Type...
    method GetBotCommands (line 118) | public static IEnumerable<BotCommand> GetBotCommands(IEnumerable<Comma...
    method GetCommandMethods (line 127) | public static IEnumerable<CommandBuildInfo> GetCommandMethods(object? ...
    method CheckDistinct (line 150) | private static void CheckDistinct(IReadOnlyCollection<BotCommand> list)
    method LoadCommand (line 159) | private E<string> LoadCommand(BotCommand com)
    method LoadICommand (line 168) | private E<string> LoadICommand(ICommand com, string path)
    method BuildAndGet (line 186) | private R<CommandGroup, string> BuildAndGet(IEnumerable<string> comPath)
    method InsertInto (line 228) | private static E<string> InsertInto(CommandGroup group, ICommand com, ...
    method GenerateError (line 282) | private static E<string> GenerateError(string msg, BotCommand? involve...
    method UnloadCommand (line 289) | private void UnloadCommand(BotCommand com)
    method UnloadICommand (line 297) | private void UnloadICommand(ICommand com, string path)
    class CommandUnloadNode (line 352) | private class CommandUnloadNode
      method CommandUnloadNode (line 356) | public CommandUnloadNode(CommandUnloadNode? parentNode, CommandGroup...
    method AstToCommandResult (line 367) | internal static ICommand AstToCommandResult(AstNode node)
    method Execute (line 397) | public static async Task<ICmdResult> Execute(ExecutionInformation info...
    method Execute (line 404) | public static async Task<ICmdResult> Execute(ExecutionInformation info...
    method GetTree (line 407) | public static string GetTree(ICommand com)
    method GetTree (line 414) | private static void GetTree(ICommand com, TextModBuilder strb, int ind...
  type ICmdResult (line 448) | public readonly struct ICmdResult
    method ICmdResult (line 452) | public ICmdResult(object? result)
    method AsRaw (line 457) | public object? AsRaw() => result;
    method AsString (line 459) | public string? AsString() => result?.ToString();

FILE: TS3AudioBot/CommandSystem/CommandParser.cs
  class CommandParser (line 17) | internal static class CommandParser
    method ParseCommandRequest (line 30) | public static AstNode ParseCommandRequest(string request, char command...
    class StringPtr (line 202) | private class StringPtr
      method StringPtr (line 211) | public StringPtr(string str)
      method Next (line 217) | public void Next()
      method Next (line 222) | public void Next(char mustBe)
      method TryNext (line 229) | public bool TryNext(char mustBe)
      method IsNext (line 237) | public bool IsNext(char what) => HasNext && text[Index + 1] == what;
      method SkipChar (line 239) | public void SkipChar(char c = ' ')
      method JumpToEnd (line 245) | public void JumpToEnd() => Index = text.Length + 1;
      method TrackNode (line 247) | public NodeTracker TrackNode(AstNode? node = null)
      type NodeTracker (line 252) | public readonly ref struct NodeTracker
        method NodeTracker (line 257) | public NodeTracker(StringPtr p, AstNode? node = null)
        method Apply (line 264) | public readonly void Apply(AstNode node)
        method Done (line 270) | public readonly (int start, int end) Done() => (indexStart, parent...
        method Dispose (line 272) | public readonly void Dispose() { if (node != null) Apply(node); }
    type BuildStatus (line 276) | private enum BuildStatus

FILE: TS3AudioBot/CommandSystem/CommandResults/IAudioResourceResult.cs
  type IAudioResourceResult (line 14) | public interface IAudioResourceResult

FILE: TS3AudioBot/CommandSystem/CommandResults/IWrappedResult.cs
  type IWrappedResult (line 12) | public interface IWrappedResult

FILE: TS3AudioBot/CommandSystem/CommandResults/PickObjectCommand.cs
  class Pick (line 14) | public class Pick<T> : IWrappedResult
    method Pick (line 35) | public Pick(T obj, string pickPath)
    method DoPick (line 41) | private object? DoPick()

FILE: TS3AudioBot/CommandSystem/CommandResults/TailString.cs
  class TailString (line 12) | public class TailString : IWrappedResult
    method TailString (line 18) | public TailString(string contentArg, string tailArg)
    method ToString (line 24) | public override string ToString() => Content;

FILE: TS3AudioBot/CommandSystem/CommandSystemExtensions.cs
  class CommandSystemExtensions (line 19) | public static class CommandSystemExtensions
    method GetFilter (line 21) | public static IFilter GetFilter(this IInjector injector)
    method GetFilterLazy (line 28) | public static Lazy<IFilter> GetFilterLazy(this IInjector injector)
    method ExecuteToString (line 31) | public static async ValueTask<string> ExecuteToString(this ICommand co...

FILE: TS3AudioBot/CommandSystem/CommandSystemTypes.cs
  class CommandSystemTypes (line 17) | public static class CommandSystemTypes

FILE: TS3AudioBot/CommandSystem/Commands/AliasCommand.cs
  class AliasCommand (line 18) | public class AliasCommand : ICommand
    method AliasCommand (line 23) | public AliasCommand(string command)
    method Execute (line 30) | public async ValueTask<object?> Execute(ExecutionInformation info, IRe...
  class AliasContext (line 52) | public class AliasContext

FILE: TS3AudioBot/CommandSystem/Commands/AppliedCommand.cs
  class AppliedCommand (line 15) | public class AppliedCommand : ICommand
    method AppliedCommand (line 20) | public AppliedCommand(ICommand command, IReadOnlyList<ICommand> argume...
    method Execute (line 26) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method ToString (line 34) | public override string ToString() => $"F\"{internCommand}\"({string.Jo...

FILE: TS3AudioBot/CommandSystem/Commands/CommandGroup.cs
  class CommandGroup (line 18) | public class CommandGroup : ICommand
    method AddCommand (line 22) | public void AddCommand(string name, ICommand command) => commands.Add(...
    method RemoveCommand (line 23) | public bool RemoveCommand(string name) => commands.Remove(name);
    method RemoveCommand (line 24) | public bool RemoveCommand(ICommand command)
    method ContainsCommand (line 31) | public bool ContainsCommand(string name) => commands.ContainsKey(name);
    method GetCommand (line 32) | public ICommand? GetCommand(string name) => commands.TryGetValue(name,...
    method Execute (line 36) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method SuggestionsJoinTrim (line 66) | private static string SuggestionsJoinTrim(IEnumerable<string> commands)
    method ToString (line 69) | public override string ToString() => "<group>";

FILE: TS3AudioBot/CommandSystem/Commands/FunctionCommand.cs
  class FunctionCommand (line 30) | public class FunctionCommand : ICommand
    method FunctionCommand (line 53) | public FunctionCommand(MethodInfo command, object? obj = null, int? re...
    method FunctionCommand (line 69) | public FunctionCommand(Delegate command, int? requiredParameters = nul...
    method FunctionCommand (line 70) | public FunctionCommand(Action command) : this(command.Method, command....
    method FunctionCommand (line 71) | public FunctionCommand(Func<string> command) : this(command.Method, co...
    method FunctionCommand (line 72) | public FunctionCommand(Action<string> command) : this(command.Method, ...
    method FunctionCommand (line 73) | public FunctionCommand(Func<string, string> command) : this(command.Me...
    method ExecuteFunction (line 75) | protected virtual async ValueTask<object?> ExecuteFunction(object?[] p...
    method FitArguments (line 122) | private async ValueTask<(object?[] paramObjs, int takenArguments)> Fit...
    method Execute (line 196) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method PrecomputeTypes (line 208) | private static ParamInfo[] PrecomputeTypes(ParameterInfo[] parameterIn...
    method UnwrapParamType (line 305) | public static Type UnwrapParamType(Type type)
    method UnwrapReturnType (line 312) | public static Type UnwrapReturnType(Type type)
    method UnwrapReturn (line 327) | [return: NotNullIfNotNull("value")]
    method ThrowAtLeastNArguments (line 346) | public static CommandException ThrowAtLeastNArguments(int count)
    method ConvertParam (line 361) | [return: NotNullIfNotNull("value")]
    method GetDefault (line 411) | private static object? GetDefault(Type type)
  type ParamKind (line 426) | public enum ParamKind
  class ParamInfo (line 437) | [DebuggerDisplay("{Kind} {Name,nq}{Optional ? \"?\" : \"\",nq} ({Type.Na...
    method ParamInfo (line 447) | public ParamInfo(ParameterInfo param, ParamKind kind, bool optional, T...
  class FunctionCommandExtensions (line 456) | public static class FunctionCommandExtensions
    method IsNormal (line 458) | public static bool IsNormal(this ParamKind kind)

FILE: TS3AudioBot/CommandSystem/Commands/ICommand.cs
  type ICommand (line 15) | public interface ICommand
    method Execute (line 34) | ValueTask<object?> Execute(ExecutionInformation info, IReadOnlyList<IC...

FILE: TS3AudioBot/CommandSystem/Commands/LazyCommand.cs
  class LazyCommand (line 15) | public class LazyCommand : ICommand
    method LazyCommand (line 24) | public LazyCommand(ICommand innerCommandArg)
    method Execute (line 29) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method ToString (line 40) | public override string ToString() => $"L({innerCommand})";

FILE: TS3AudioBot/CommandSystem/Commands/OverloadedFunctionCommand.cs
  class OverloadedFunctionCommand (line 18) | public class OverloadedFunctionCommand : ICommand
    method OverloadedFunctionCommand (line 22) | public OverloadedFunctionCommand() : this(Array.Empty<FunctionCommand>...
    method OverloadedFunctionCommand (line 23) | public OverloadedFunctionCommand(IEnumerable<FunctionCommand> function...
    method AddCommand (line 28) | public void AddCommand(FunctionCommand command)
    method RemoveCommand (line 33) | public bool RemoveCommand(FunctionCommand command) => Functions.Remove...
    method SortList (line 35) | private void SortList()
    method Execute (line 74) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method ToString (line 101) | public override string ToString() => "<overload>";

FILE: TS3AudioBot/CommandSystem/Commands/ResultCommand.cs
  class ResultCommand (line 18) | public class ResultCommand : ICommand
    method ResultCommand (line 22) | public ResultCommand(object? contentArg)
    method Execute (line 28) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method ToString (line 34) | public override string ToString() => "<result>";

FILE: TS3AudioBot/CommandSystem/Commands/RootCommand.cs
  class RootCommand (line 20) | public class RootCommand : ICommand
    method RootCommand (line 24) | public RootCommand(IReadOnlyList<ICommand> arguments)
    method Execute (line 29) | public virtual async ValueTask<object?> Execute(ExecutionInformation i...
    method ToString (line 44) | public override string ToString() => $"RootCmd({string.Join(", ", inte...

FILE: TS3AudioBot/CommandSystem/ExecutionInformation.cs
  class ExecutionInformation (line 14) | public class ExecutionInformation : ChainedInjector<BasicInjector>
    method ExecutionInformation (line 16) | public ExecutionInformation() : this(NullInjector.Instance) { }
    method ExecutionInformation (line 17) | public ExecutionInformation(IInjector parent) : base(parent, new Basic...

FILE: TS3AudioBot/CommandSystem/ICommandBag.cs
  type ICommandBag (line 14) | public interface ICommandBag

FILE: TS3AudioBot/CommandSystem/StaticList.cs
  class StaticList (line 16) | internal static class StaticList
    method TrySegment (line 18) | public static IReadOnlyList<T> TrySegment<T>(this IReadOnlyList<T> lis...
    method TrySegment (line 32) | public static IReadOnlyList<T> TrySegment<T>(this IReadOnlyList<T> lis...
    method CopyTo (line 42) | public static void CopyTo<T>(this IReadOnlyList<T> list, int srcOffset...
    method CopyTo (line 45) | public static void CopyTo<T>(this IReadOnlyList<T> list, int srcOffset...

FILE: TS3AudioBot/CommandSystem/Text/AppliedTextMod.cs
  type AppliedTextMod (line 12) | public readonly struct AppliedTextMod
    method AppliedTextMod (line 17) | public AppliedTextMod(string? text)
    method AppliedTextMod (line 23) | public AppliedTextMod(string? text, TextMod mod)
    method Color (line 29) | public readonly AppliedTextMod Color(Color color) => new AppliedTextMo...
    method Bold (line 30) | public readonly AppliedTextMod Bold() => new AppliedTextMod(Text, Mod....
    method Italic (line 31) | public readonly AppliedTextMod Italic() => new AppliedTextMod(Text, Mo...
    method Underline (line 32) | public readonly AppliedTextMod Underline() => new AppliedTextMod(Text,...
    method Strike (line 33) | public readonly AppliedTextMod Strike() => new AppliedTextMod(Text, Mo...
    method ToString (line 37) | public override readonly string? ToString() => Text;

FILE: TS3AudioBot/CommandSystem/Text/Color.cs
  type Color (line 16) | public readonly struct Color
    method Color (line 39) | static Color()
    method Color (line 96) | public Color(byte r, byte g, byte b) : this(r, g, b, ColorFlags.Solid)...
    method Color (line 97) | public Color(byte r, byte g, byte b, ColorFlags flags)
    method IsDouble (line 105) | private static bool IsDouble(byte num) => (num & 0x0F) == (num >> 4);
    method GetL (line 107) | public readonly void GetL(StringBuilder strb)
    method Equals (line 119) | public override readonly bool Equals(object? obj)
    method GetHashCode (line 128) | public override readonly int GetHashCode() => (int)Flags << 24 | R << ...
    method ToString (line 130) | public override readonly string ToString()
  type ColorFlags (line 142) | [Flags]

FILE: TS3AudioBot/CommandSystem/Text/LongTextBehaviour.cs
  type LongTextBehaviour (line 12) | public enum LongTextBehaviour

FILE: TS3AudioBot/CommandSystem/Text/LongTextTransform.cs
  class LongTextTransform (line 18) | public static class LongTextTransform
    method Split (line 22) | public static IEnumerable<string> Split(string text, LongTextBehaviour...
    type Ind (line 107) | private readonly struct Ind
      method Ind (line 112) | public Ind(int i, int tok)
      method ToString (line 118) | public override readonly string ToString() => $"i:{i} tok:{tok}";

FILE: TS3AudioBot/CommandSystem/Text/TextMod.cs
  type TextMod (line 14) | public readonly struct TextMod : IEquatable<TextMod>
    method TextMod (line 21) | public TextMod(TextModFlag flags, Color? color = null)
    method Color (line 27) | public readonly TextMod Color(Color color) => new TextMod(Flags | Text...
    method Bold (line 28) | public readonly TextMod Bold() => new TextMod(Flags | TextModFlag.Bold...
    method Italic (line 29) | public readonly TextMod Italic() => new TextMod(Flags | TextModFlag.It...
    method Strike (line 30) | public readonly TextMod Strike() => new TextMod(Flags | TextModFlag.St...
    method Underline (line 31) | public readonly TextMod Underline() => new TextMod(Flags | TextModFlag...
    method Format (line 33) | public static string Format(AppliedTextMod format, params AppliedTextM...
    method Format (line 36) | public static string Format(bool color, AppliedTextMod format, params ...
    method Equals (line 45) | public readonly bool Equals(TextMod other) => Flags == other.Flags && ...
    method Equals (line 46) | public override readonly bool Equals(object? obj) => obj is TextMod tm...
    method GetHashCode (line 49) | public override readonly int GetHashCode() => ((int)Flags << 28) | Has...

FILE: TS3AudioBot/CommandSystem/Text/TextModBuilder.cs
  class TextModBuilder (line 16) | public class TextModBuilder
    method TextModBuilder (line 24) | public TextModBuilder(bool color = true)
    method TextModBuilder (line 27) | public TextModBuilder(StringBuilder strb, bool color = true)
    method Append (line 33) | public TextModBuilder Append(AppliedTextMod atm) => Append(atm.Text, a...
    method Append (line 35) | public TextModBuilder Append(string? text, TextMod mod)
    method AppendLine (line 44) | public TextModBuilder AppendLine(AppliedTextMod atm)
    method AppendLine (line 51) | public TextModBuilder AppendLine(string? text, TextMod mod)
    method AppendFormat (line 58) | public TextModBuilder AppendFormat(AppliedTextMod format, params Appli...
    method StartText (line 79) | private static void StartText(StringBuilder strb, string? text, ref Te...
    method Start (line 95) | private static TextModFlag Start(StringBuilder strb, TextModFlag cur, ...
    method End (line 111) | private static TextModFlag End(StringBuilder strb, TextModFlag cur, Te...
    method GetShortest (line 127) | private static TextModFlag GetShortest(TextModFlag mod)
    method ToString (line 137) | public override string ToString() => strb.ToString();

FILE: TS3AudioBot/CommandSystem/Text/TextModFlag.cs
  type TextModFlag (line 14) | [Flags]

FILE: TS3AudioBot/CommandSystem/Text/TextModHelper.cs
  class TextModHelper (line 12) | public static class TextModHelper
    method Mod (line 14) | public static AppliedTextMod Mod(this string str) => str;

FILE: TS3AudioBot/Config/Config.cs
  class ConfRoot (line 19) | public partial class ConfRoot
    method Open (line 24) | public static ConfRoot? Open(string file)
    method Create (line 38) | public static ConfRoot? Create(string file)
    method OpenOrCreate (line 52) | public static ConfRoot? OpenOrCreate(string file) => File.Exists(file)...
    method CheckAndSet (line 54) | private bool CheckAndSet(string file)
    method CheckPaths (line 63) | private bool CheckPaths()
    method Save (line 78) | public bool Save() => Save(fileName!, true);
    method GetFilePath (line 81) | public string GetFilePath(string _)
    method NameToPath (line 86) | internal R<FileInfo, LocalStr> NameToPath(string name)
    method CreateBot (line 94) | public ConfBot CreateBot()
    method GetAllBots (line 101) | public ConfBot[]? GetAllBots()
    method GetBotConfig (line 117) | public R<ConfBot, Exception> GetBotConfig(string name)
    method InitializeBotConfig (line 138) | private void InitializeBotConfig(ConfBot config)
    method ClearBotConfigCache (line 144) | public void ClearBotConfigCache()
    method ClearBotConfigCache (line 149) | public void ClearBotConfigCache(string name)
    method AddToConfigCache (line 154) | internal void AddToConfigCache(ConfBot config)
    method CreateBotConfig (line 161) | public E<LocalStr> CreateBotConfig(string name)
    method DeleteBotConfig (line 181) | public E<LocalStr> DeleteBotConfig(string name)
    method CopyBotConfig (line 203) | public E<LocalStr> CopyBotConfig(string from, string to)
  class ConfBot (line 231) | public partial class ConfBot
    method SaveNew (line 237) | public E<LocalStr> SaveNew(string name)
    method SaveWhenExists (line 253) | public E<LocalStr> SaveWhenExists()
    method SaveInternal (line 266) | private E<LocalStr> SaveInternal(string file)
    method GetParent (line 277) | internal ConfRoot GetParent() => (Parent as ConfRoot) ?? throw new Inv...

FILE: TS3AudioBot/Config/ConfigArray.cs
  class ConfigArray (line 18) | public class ConfigArray<T> : ConfigValue<IReadOnlyList<T>> where T : no...
    method ConfigArray (line 20) | public ConfigArray(string key, IReadOnlyList<T> defaultVal, string doc...
    method FromToml (line 22) | public override void FromToml(TomlObject? tomlObject)
    method ToJson (line 30) | public override void ToJson(JsonWriter writer)
    method FromJson (line 40) | public override E<string> FromJson(JsonReader reader)

FILE: TS3AudioBot/Config/ConfigDynamicTable.cs
  class ConfigDynamicTable (line 17) | [DebuggerDisplay("dyntable:{Key}")]
    method ConfigDynamicTable (line 23) | public ConfigDynamicTable(Func<string, T> createFactory)
    method FromToml (line 28) | public override void FromToml(TomlObject? tomlObject)
    method GetAllChildren (line 47) | public override IEnumerable<ConfigPart> GetAllChildren() => GetAllItem...
    method GetChild (line 49) | public override ConfigPart? GetChild(string key) => GetItem(key);
    method GetOrCreateChild (line 51) | public ConfigPart GetOrCreateChild(string key) => GetOrCreateItem(key);
    method Derive (line 53) | public override void Derive(ConfigPart derived)
    method GetItem (line 58) | public T? GetItem(string key) => dynamicTables.TryGetValue(key, out va...
    method GetAllItems (line 60) | public IEnumerable<T> GetAllItems() => dynamicTables.Values;
    method CreateItem (line 62) | public T CreateItem(string key)
    method GetOrCreateItem (line 69) | public T GetOrCreateItem(string key) => GetItem(key) ?? CreateItem(key);
    method RemoveItem (line 71) | public void RemoveItem(string key)
  type IDynamicTable (line 78) | public interface IDynamicTable
    method GetOrCreateChild (line 80) | ConfigPart GetOrCreateChild(string key);

FILE: TS3AudioBot/Config/ConfigEnumerable.cs
  class ConfigEnumerable (line 17) | public abstract class ConfigEnumerable : ConfigPart
    method ClearEvents (line 27) | public override void ClearEvents()
    method FromToml (line 33) | public override void FromToml(TomlObject? tomlObject)
    method ToToml (line 51) | public override void ToToml(bool writeDefaults, bool writeDocumentation)
    method ToJson (line 61) | public override void ToJson(JsonWriter writer)
    method FromJson (line 72) | public override E<string> FromJson(JsonReader reader)
    method GetChild (line 107) | public abstract ConfigPart? GetChild(string key);
    method GetAllChildren (line 109) | public abstract IEnumerable<ConfigPart> GetAllChildren();
    method Create (line 113) | protected static T Create<T>(string key, string doc = "") where T : Co...
    method Create (line 122) | protected static T Create<T>(string key, ConfigEnumerable? parent, Tom...
    method Init (line 127) | protected static T Init<T>(T part, ConfigEnumerable? parent, TomlObjec...
    method CreateRoot (line 134) | public static T CreateRoot<T>() where T : ConfigEnumerable, new() => C...
    method Load (line 136) | public static R<T, Exception> Load<T>(string path) where T : ConfigEnu...
    method Save (line 144) | public E<Exception> Save(string path, bool writeDefaults, bool writeDo...

FILE: TS3AudioBot/Config/ConfigHelper.cs
  class ConfigHelper (line 18) | public static class ConfigHelper
    method ByPathAsArray (line 22) | public static ConfigPart[] ByPathAsArray(this ConfigPart config, strin...
    method TryReadValue (line 34) | public static E<string> TryReadValue<T>(this JsonReader reader, out T ...

FILE: TS3AudioBot/Config/ConfigPart.cs
  class ConfigPart (line 21) | [DebuggerDisplay("unknown:{Key}")]
    method ConfigPart (line 32) | protected ConfigPart() { }
    method ConfigPart (line 34) | protected ConfigPart(string key)
    method FromToml (line 40) | public abstract void FromToml(TomlObject? tomlObject);
    method ToToml (line 41) | public abstract void ToToml(bool writeDefaults, bool writeDocumentation);
    method Derive (line 42) | public abstract void Derive(ConfigPart derived);
    method FromJson (line 43) | public abstract E<string> FromJson(JsonReader reader);
    method ToJson (line 44) | public abstract void ToJson(JsonWriter writer);
    method ClearEvents (line 45) | public abstract void ClearEvents();
    method CreateDocumentation (line 47) | protected void CreateDocumentation(TomlObject tomlObject)
    method ToString (line 57) | public override string ToString() => this.ToJson();
    method ByPath (line 61) | public IEnumerable<ConfigPart> ByPath(string path)
    method ProcessIdentifier (line 69) | private IEnumerable<ConfigPart> ProcessIdentifier(ReadOnlyMemory<char>...
    method ProcessArray (line 136) | private IEnumerable<ConfigPart> ProcessArray(ReadOnlyMemory<char> pathM)
    method ProcessDot (line 189) | private IEnumerable<ConfigPart> ProcessDot(ReadOnlyMemory<char> pathM)
    method GetArrayItemByIndex (line 202) | private ConfigPart? GetArrayItemByIndex(ReadOnlySpan<char> index)
    method GetAllArrayItems (line 219) | private IEnumerable<ConfigPart> GetAllArrayItems()
    method GetSubItemByName (line 226) | private ConfigPart? GetSubItemByName(ReadOnlySpan<char> name)
    method GetAllSubItems (line 234) | private IEnumerable<ConfigPart> GetAllSubItems()

FILE: TS3AudioBot/Config/ConfigStructs.cs
  class ConfRoot (line 19) | public partial class ConfRoot : ConfigTable
  class ConfConfigs (line 36) | public class ConfConfigs : ConfigTable
  class ConfDb (line 48) | public class ConfDb : ConfigTable
  class ConfFactories (line 54) | public class ConfFactories : ConfigTable
  class ConfResolverYoutube (line 61) | public class ConfResolverYoutube : ConfigTable
  class ConfTools (line 72) | public class ConfTools : ConfigTable
  class ConfToolsFfmpeg (line 82) | public class ConfToolsFfmpeg : ConfigTable
  class ConfRights (line 87) | public class ConfRights : ConfigTable
  class ConfPlugins (line 93) | public class ConfPlugins : ConfigTable
  class ConfPluginsLoad (line 101) | public class ConfPluginsLoad : ConfigTable
  class ConfWeb (line 106) | public class ConfWeb : ConfigTable
  class ConfWebApi (line 117) | public class ConfWebApi : ConfigTable
  class ConfWebInterface (line 126) | public class ConfWebInterface : ConfigTable
  class ConfBot (line 134) | public partial class ConfBot : ConfigTable
  class ConfCommands (line 158) | public class ConfCommands : ConfigTable
  class ConfCommandsAlias (line 180) | public class ConfCommandsAlias : ConfigDynamicTable<ConfigValue<string>>
    method ConfCommandsAlias (line 182) | public ConfCommandsAlias() : base(key => new ConfigValue<string>(key, ...
  class ConfConnect (line 185) | public class ConfConnect : ConfigTable
  class ConfReconnect (line 206) | public class ConfReconnect : ConfigTable
  class ConfIdentity (line 216) | public class ConfIdentity : ConfigTable
  class ConfAudio (line 227) | public class ConfAudio : ConfigTable
  class ConfAudioVolume (line 246) | public class ConfAudioVolume : ConfigTable
  class ConfPlaylists (line 255) | public class ConfPlaylists : ConfigTable
  class ConfHistory (line 260) | public class ConfHistory : ConfigTable
  class ConfData (line 268) | public class ConfData : ConfigTable
  class ConfEvents (line 273) | public class ConfEvents : ConfigTable
  class ConfPath (line 300) | public class ConfPath : ConfigTable
  class ConfPassword (line 307) | public class ConfPassword : ConfigTable
    method Get (line 315) | public TSLib.Password Get()
  class ConfTsVersion (line 331) | public class ConfTsVersion : ConfigTable
  class ConfTimeExtensions (line 340) | public static class ConfTimeExtensions
    method GetValueAsTime (line 342) | public static TimeSpan? GetValueAsTime(this ConfigArray<string> conf, ...
    method ValidateTime (line 356) | public static E<string> ValidateTime(IReadOnlyList<string> value)

FILE: TS3AudioBot/Config/ConfigTable.cs
  class ConfigTable (line 17) | [DebuggerDisplay("table:{Key}")]
    method ConfigTable (line 22) | protected ConfigTable()
    method GetConfigPartProperties (line 29) | private IEnumerable<System.Reflection.PropertyInfo> GetConfigPartPrope...
    method GetMember (line 36) | private void GetMember()
    method FromToml (line 42) | public override void FromToml(TomlObject? tomlObject)
    method Derive (line 53) | public override void Derive(ConfigPart derived)
    method GetChild (line 63) | public override ConfigPart? GetChild(string key) => Properties.Find(x ...
    method GetAllChildren (line 65) | public override IEnumerable<ConfigPart> GetAllChildren() => Properties;

FILE: TS3AudioBot/Config/ConfigUpgrade2.cs
  class ConfigUpgrade2 (line 21) | internal static class ConfigUpgrade2
    method Upgrade (line 27) | public static void Upgrade(string path)

FILE: TS3AudioBot/Config/ConfigValue.cs
  class ConfigValue (line 19) | [DebuggerDisplay("{Key}:{Value}")]
    method ConfigValue (line 54) | public ConfigValue(string key, T defaultVal, string doc = "") : base(key)
    method InvokeChange (line 61) | private void InvokeChange(object? sender, ConfigChangedEventArgs<T> ar...
    method ClearEvents (line 63) | public override void ClearEvents() => Changed = null;
    method FromToml (line 65) | public override void FromToml(TomlObject? tomlObject)
    method ToToml (line 86) | public override void ToToml(bool writeDefaults, bool writeDocumentation)
    method Derive (line 109) | public override void Derive(ConfigPart derived)
    method ToJson (line 119) | public override void ToJson(JsonWriter writer)
    method FromJson (line 124) | public override E<string> FromJson(JsonReader reader)
    method ToString (line 139) | public override string ToString() => Value.ToString() ?? "<null>";
  class ConfigChangedEventArgs (line 144) | public class ConfigChangedEventArgs<T> : EventArgs
    method ConfigChangedEventArgs (line 148) | public ConfigChangedEventArgs(T newValue)

FILE: TS3AudioBot/Core.cs
  class Core (line 27) | public sealed class Core
    method Core (line 35) | public Core(DedicatedTaskScheduler scheduler, string? configFilePath =...
    method Run (line 44) | public async Task Run(ParameterData setup)
    method UnhandledExceptionHandler (line 95) | public void UnhandledExceptionHandler(object sender, UnhandledExceptio...
    method UnobservedTaskExceptionHandler (line 101) | public static void UnobservedTaskExceptionHandler(object? sender, Unob...
    method ConsoleInterruptHandler (line 106) | public void ConsoleInterruptHandler(object sender, ConsoleCancelEventA...
    method Stop (line 125) | public void Stop() => _ = scheduler.InvokeAsync(StopAsync);
    method StopAsync (line 127) | private async Task StopAsync()

FILE: TS3AudioBot/DbStore.cs
  class DbStore (line 17) | public class DbStore : IDisposable
    method DbStore (line 24) | public DbStore(ConfDb config)
    method GetMetaData (line 32) | public DbMetaData GetMetaData(string table)
    method UpdateMetaData (line 43) | public void UpdateMetaData(DbMetaData metaData)
    method GetCollection (line 48) | public LiteCollection<T> GetCollection<T>(string name)
    method DropCollection (line 53) | public void DropCollection(string name) => database.DropCollection(name);
    method CleanFile (line 55) | public void CleanFile()
    method Dispose (line 60) | public void Dispose()
  class DbMetaData (line 66) | public class DbMetaData

FILE: TS3AudioBot/Dependency/BasicInjector.cs
  class BasicInjector (line 15) | public class BasicInjector : IInjector
    method BasicInjector (line 18) | public BasicInjector() { dynamicObjects = new Dictionary<Type, object>...
    method GetModule (line 19) | public object? GetModule(Type type) => dynamicObjects.TryGetValue(type...
    method AddModule (line 20) | public void AddModule(Type type, object obj) => dynamicObjects[type] =...
    method GetAllModules (line 21) | public IEnumerable<object> GetAllModules() => dynamicObjects.Values;

FILE: TS3AudioBot/Dependency/ChainedInjector.cs
  class ChainedInjector (line 14) | public class ChainedInjector<T> : IInjector where T : class, IInjector
    method ChainedInjector (line 19) | public ChainedInjector(IInjector parent, T own)
    method GetModule (line 25) | public virtual object? GetModule(Type type)
    method AddModule (line 28) | public virtual void AddModule(Type type, object obj)

FILE: TS3AudioBot/Dependency/DependencyBuilder.cs
  class CoreInjector (line 16) | public sealed class CoreInjector : BasicInjector { }
  class BotInjector (line 17) | public sealed class BotInjector : ChainedInjector<BasicInjector>
    method BotInjector (line 21) | public BotInjector(IInjector parent) : base(parent, new BasicInjector())
    method GetModule (line 25) | public override object? GetModule(Type type)
    method HideParentModule (line 35) | public void HideParentModule(Type type)
    method HideParentModule (line 40) | public void HideParentModule<T>() => HideParentModule(typeof(T));
    method ClearHiddenParentModules (line 41) | public void ClearHiddenParentModules() => hiddenParentModules = null;
  class DependencyBuilder (line 48) | public class DependencyBuilder
    method DependencyBuilder (line 55) | public DependencyBuilder(IInjector injector)
    method RequestModule (line 60) | public DependencyBuilder RequestModule<TService>() where TService : cl...
    method RequestModule (line 62) | public DependencyBuilder RequestModule<TService, TImplementation>() wh...
    method RequestModule (line 64) | private DependencyBuilder RequestModule(Type tService, Type tImplement...
    method Build (line 83) | public bool Build()
    method GetContructorParam (line 103) | internal static Type[]? GetContructorParam(Type type)
    method ToString (line 111) | public override string ToString()

FILE: TS3AudioBot/Dependency/IInjector.cs
  type IInjector (line 18) | public interface IInjector
    method GetModule (line 20) | object? GetModule(Type type);
    method AddModule (line 21) | void AddModule(Type type, object obj);

FILE: TS3AudioBot/Dependency/InjectorExtensions.cs
  class InjectorExtensions (line 17) | public static class InjectorExtensions
    method GetModule (line 19) | public static T? GetModule<T>(this IInjector injector) where T : class
    method GetModuleOrThrow (line 24) | public static T GetModuleOrThrow<T>(this IInjector injector) where T :...
    method TryGet (line 31) | public static bool TryGet<T>(this IInjector injector, [NotNullWhen(tru...
    method TryGet (line 37) | public static bool TryGet(this IInjector injector, Type t, [NotNullWhe...
    method AddModule (line 43) | public static void AddModule<T>(this IInjector injector, T obj) where ...
    method TryCreate (line 48) | public static bool TryCreate<T>(this IInjector injector, [NotNullWhen(...
    method TryCreate (line 62) | public static bool TryCreate(this IInjector injector, Type type, [NotN...
    method FillProperties (line 82) | public static void FillProperties(this IInjector injector, object obj)

FILE: TS3AudioBot/Dependency/Module.cs
  class Module (line 15) | internal class Module
    method Module (line 21) | public Module(Type tService, Type tImplementation)
    method ToString (line 28) | public override string ToString() => $"{TService.Name}({(TService != T...

FILE: TS3AudioBot/Dependency/NullInjector.cs
  class NullInjector (line 14) | public sealed class NullInjector : IInjector
    method NullInjector (line 17) | private NullInjector() { }
    method GetModule (line 18) | public object? GetModule(Type type) => null;
    method AddModule (line 19) | public void AddModule(Type type, object obj) { }

FILE: TS3AudioBot/Environment/Stats.cs
  class Stats (line 24) | public class Stats
    method Stats (line 59) | public Stats(ConfRoot conf, DbStore database, BotManager botManager, D...
    method UpdateMeta (line 96) | private void UpdateMeta()
    method StartTimer (line 102) | public void StartTimer(bool upload)
    method SendStats (line 108) | private async Task SendStats(StatsPing sendPacket)
    method GetStatsTill (line 126) | private StatsPing GetStatsTill(DateTime date)
    method TrackPoint (line 143) | private async void TrackPoint()
    method TrackSongLoad (line 176) | public void TrackSongLoad(string? factory, bool successful, bool fromU...
    method TrackCommandCall (line 184) | public void TrackCommandCall(bool byUser)
    method TrackCommandApiCall (line 190) | public void TrackCommandApiCall()
    method TrackSongStart (line 196) | public void TrackSongStart(Id bot, string factory)
    method TrackSongStop (line 204) | public void TrackSongStop(Id bot)
    method GetDefaultStatsPing (line 213) | private static StatsPing GetDefaultStatsPing()
    method CreateExample (line 223) | public static string CreateExample()
  class StatsPing (line 245) | internal class StatsPing : StatsData
  class StatsMeta (line 253) | internal class StatsMeta
    method GenNextIndex (line 261) | public int GenNextIndex()
  class StatsData (line 268) | internal class StatsData
    method ShouldSerializeSongStats (line 285) | public bool ShouldSerializeSongStats() => SongStats.Count > 0;
    method ShouldSerializeCommandCalls (line 286) | public bool ShouldSerializeCommandCalls() => CommandCalls != 0;
    method ShouldSerializeCommandFromUser (line 287) | public bool ShouldSerializeCommandFromUser() => CommandFromUser != 0;
    method ShouldSerializeCommandFromApi (line 288) | public bool ShouldSerializeCommandFromApi() => CommandFromApi != 0;
    method Add (line 290) | public void Add(StatsData other)
    method Reset (line 301) | public void Reset()
  class StatsFactory (line 313) | internal class StatsFactory
    method ShouldSerializePlayRequests (line 322) | public bool ShouldSerializePlayRequests() => PlayRequests != 0;
    method ShouldSerializePlaySucessful (line 323) | public bool ShouldSerializePlaySucessful() => PlaySucessful != 0;
    method ShouldSerializePlayFromUser (line 324) | public bool ShouldSerializePlayFromUser() => PlayFromUser != 0;
    method ShouldSerializeSearchRequests (line 325) | public bool ShouldSerializeSearchRequests() => SearchRequests != 0;
    method ShouldSerializePlaytime (line 326) | public bool ShouldSerializePlaytime() => Playtime != TimeSpan.Zero;
    method Add (line 328) | public void Add(StatsFactory other)
    method Reset (line 337) | public void Reset()

FILE: TS3AudioBot/Environment/SystemData.cs
  class SystemData (line 21) | public static class SystemData
    method GenPlatformDat (line 29) | private static string GenPlatformDat()
    method RunBash (line 91) | private static void RunBash(string param, Action<StreamReader> action)
    method GenRuntimeData (line 116) | private static PlatformVersion GenRuntimeData()
    method GetNetCoreVersion (line 133) | private static PlatformVersion? GetNetCoreVersion()
    method GetMonoVersion (line 147) | private static PlatformVersion? GetMonoVersion()
    method GetNetFrameworkVersion (line 160) | private static PlatformVersion GetNetFrameworkVersion()
    method ParseToSemVer (line 167) | private static Version? ParseToSemVer(string? version)
  type Runtime (line 185) | public enum Runtime
  class BuildData (line 193) | public partial class BuildData
    method BuildData (line 201) | public BuildData()
    method ToLongString (line 206) | public string ToLongString() => $"\nVersion: {Version}\nBranch: {Branc...
    method ToString (line 207) | public override string ToString() => $"{Version}/{Branch}/{(CommitSha....
    method GetDataInternal (line 209) | partial void GetDataInternal();
  class PlatformVersion (line 212) | public class PlatformVersion
    method PlatformVersion (line 218) | public PlatformVersion(Runtime runtime, string fullName, Version? semVer)
    method ToString (line 225) | public override string ToString() => FullName;
  class SemVerExtension (line 228) | public static class SemVerExtension
    method AsSemVer (line 230) | public static string AsSemVer(this Version version) => $"{version.Majo...

FILE: TS3AudioBot/Environment/SystemMonitor.cs
  class SystemMonitor (line 20) | public class SystemMonitor
    method SystemMonitor (line 33) | public SystemMonitor(DedicatedTaskScheduler scheduler)
    method CreateSnapshot (line 38) | public void CreateSnapshot()
    method GetReport (line 76) | public SystemMonitorReport GetReport()
  class SystemMonitorReport (line 99) | public class SystemMonitorReport
    method SystemMonitorReport (line 104) | public SystemMonitorReport(long[] memory, float[] cpu)
  type SystemMonitorSnapshot (line 111) | public struct SystemMonitorSnapshot

FILE: TS3AudioBot/Error.cs
  class Error (line 6) | public static class Error
    method LocalStr (line 8) | public static AudioBotException LocalStr(string text) => new AudioBotE...
    method Exception (line 9) | public static AudioBotException Exception(Exception ex) => new AudioBo...
    method Str (line 10) | public static AudioBotException Str(string text) => new AudioBotExcept...
    method LocalStr (line 12) | public static AudioBotException LocalStr(this AudioBotException ex, st...
    method Str (line 13) | public static AudioBotException Str(this AudioBotException ex, string ...
    method Throw (line 14) | public static void Throw(this AudioBotException ex) => throw ex;
  class AudioBotException (line 17) | [Serializable]
    method AudioBotException (line 26) | public AudioBotException(Exception? ex = null)
    method AudioBotException (line 30) | public AudioBotException(string message, Exception? inner = null)
    method AudioBotException (line 36) | protected AudioBotException(SerializationInfo info, StreamingContext c...

FILE: TS3AudioBot/Helper/AttributeStrings.cs
  class AttributeStrings (line 12) | internal static class AttributeStrings

FILE: TS3AudioBot/Helper/Const.cs
  class SessionConst (line 12) | internal static class SessionConst
  class BotPaths (line 17) | public static class BotPaths
  class FilesConst (line 24) | public static class FilesConst

FILE: TS3AudioBot/Helper/Diagnose/SelfDiagnoseLevel.cs
  type SelfDiagnoseLevel (line 12) | public enum SelfDiagnoseLevel

FILE: TS3AudioBot/Helper/Diagnose/SelfDiagnoseMessage.cs
  class SelfDiagnoseMessage (line 14) | public class SelfDiagnoseMessage
    method SelfDiagnoseMessage (line 22) | public SelfDiagnoseMessage(string description, string category, SelfDi...

FILE: TS3AudioBot/Helper/IJsonConfig.cs
  type IJsonSerializable (line 17) | public interface IJsonSerializable
    method ToJson (line 20) | void ToJson(JsonWriter writer);
    method FromJson (line 21) | E<string> FromJson(JsonReader reader);
  class JsonSerializableExtensions (line 24) | public static class JsonSerializableExtensions
    method FromJson (line 26) | public static E<string> FromJson(this IJsonSerializable jsonConfig, st...
    method ToJson (line 36) | public static string ToJson(this IJsonSerializable jsonConfig)
  class IJsonSerializableConverter (line 49) | public class IJsonSerializableConverter : JsonConverter
    method CanConvert (line 54) | public override bool CanConvert(Type objectType)
    method ReadJson (line 59) | public override object? ReadJson(JsonReader reader, Type objectType, o...
    method WriteJson (line 64) | public override void WriteJson(JsonWriter writer, object? value, JsonS...

FILE: TS3AudioBot/Helper/ImageUtil.cs
  class ImageUtil (line 21) | internal static class ImageUtil
    method ResizeImageSave (line 27) | public static async Task<ImageHolder> ResizeImageSave(Stream imgStream...
    method ResizeImage (line 49) | private static ImageHolder ResizeImage(Stream imgStream, int resizeMax...
    method SaveAdaptive (line 65) | private static ImageHolder SaveAdaptive(Image img)
  class ImageHolder (line 84) | class ImageHolder : IDisposable
    method ImageHolder (line 89) | public ImageHolder(Stream stream, string mime)
    method Dispose (line 95) | public void Dispose() => Stream.Dispose();

FILE: TS3AudioBot/Helper/Interactive.cs
  class Interactive (line 15) | public static class Interactive
    method UserAgree (line 17) | public static bool UserAgree(bool defaultTo = true)
    method LoopAction (line 29) | public static string? LoopAction(string question, Func<string, bool> a...
    method LoopActionAsync (line 43) | public static async Task<string?> LoopActionAsync(string question, Fun...

FILE: TS3AudioBot/Helper/LimitStream.cs
  class LimitStream (line 15) | public class LimitStream : Stream
    method LimitStream (line 19) | public LimitStream(Stream baseStream, long maxLength)
    method Flush (line 34) | public override void Flush() => baseStream.Flush();
    method Read (line 36) | public override int Read(byte[] buffer, int offset, int count)
    method Seek (line 49) | public override long Seek(long offset, SeekOrigin origin) => throw new...
    method SetLength (line 51) | public override void SetLength(long value) => baseStream.SetLength(val...
    method Write (line 53) | public override void Write(byte[] buffer, int offset, int count)
    method Dispose (line 61) | protected override void Dispose(bool disposing)
  class EntityTooLargeException (line 70) | [Serializable]
    method EntityTooLargeException (line 75) | public EntityTooLargeException() { }
    method EntityTooLargeException (line 76) | public EntityTooLargeException(long maxLen) : base(string.Format(ErrMs...

FILE: TS3AudioBot/Helper/TextUtil.cs
  class TextUtil (line 19) | public static class TextUtil
    method GetAnswer (line 21) | public static Answer GetAnswer(string answer)
    method ExtractUrlFromBb (line 34) | public static string ExtractUrlFromBb(string ts3Link)
    method StripQuotes (line 46) | public static string StripQuotes(string quotedString, bool throwWhenIn...
    method GenToken (line 61) | public static string GenToken(int length)
    method ParseTime (line 75) | public static TimeSpan? ParseTime(string value)
    method ParseTimeAsSimple (line 83) | private static TimeSpan? ParseTimeAsSimple(string value)
    method ParseTimeAsDigital (line 109) | private static TimeSpan? ParseTimeAsDigital(string value)
    method ParseTimeAsXml (line 130) | private static TimeSpan? ParseTimeAsXml(string value)
    method ValidateTime (line 136) | public static E<string> ValidateTime(string value)
  type Answer (line 144) | public enum Answer

FILE: TS3AudioBot/Helper/TomlTools.cs
  class TomlTools (line 21) | public static class TomlTools
    method TryGetValueArray (line 25) | public static bool TryGetValueArray<T>(this TomlObject tomlObj, [NotNu...
    method TryGetValue (line 50) | public static bool TryGetValue<T>(this TomlObject tomlObj, [MaybeNullW...
    method SerializeTime (line 139) | public static string SerializeTime(TimeSpan time)
    method Set (line 162) | public static TomlObject Set<T>(this TomlTable tomlTable, string key, ...
    method ByPath (line 233) | public static IEnumerable<TomlObject> ByPath(this TomlObject obj, stri...
    method ProcessIdentifier (line 239) | private static IEnumerable<TomlObject> ProcessIdentifier(TomlObject ob...
    method ProcessArray (line 306) | private static IEnumerable<TomlObject> ProcessArray(TomlObject obj, Re...
    method ProcessDot (line 359) | private static IEnumerable<TomlObject> ProcessDot(TomlObject obj, Read...
    method IsArray (line 372) | internal static bool IsArray(ReadOnlySpan<char> name)
    method IsIdentifier (line 375) | internal static bool IsIdentifier(ReadOnlySpan<char> name)
    method IsDot (line 378) | internal static bool IsDot(ReadOnlySpan<char> name)
    method GetArrayItemByIndex (line 381) | private static TomlObject? GetArrayItemByIndex(this TomlObject obj, Re...
    method GetAllArrayItems (line 403) | private static IEnumerable<TomlObject> GetAllArrayItems(this TomlObjec...
    method GetSubItemByName (line 412) | private static TomlObject? GetSubItemByName(this TomlObject obj, ReadO...
    method GetAllSubItems (line 419) | private static IEnumerable<TomlObject> GetAllSubItems(this TomlObject ...
    method DumpToJson (line 428) | public static string DumpToJson(this TomlObject obj)
    method DumpToJson (line 440) | public static void DumpToJson(this TomlObject obj, JsonWriter writer)

FILE: TS3AudioBot/Helper/Util.cs
  class Util (line 28) | public static class Util
    method FormatBytesHumanReadable (line 36) | public static string FormatBytesHumanReadable(long bytes)
    method FromSeed (line 44) | public static string FromSeed(int seed)
    method ToSeed (line 65) | public static int ToSeed(string seed)
    method Pow (line 79) | private static long Pow(long b, int pow)
    method GetEmbeddedFile (line 92) | public static Stream? GetEmbeddedFile(string name)
    method TryCast (line 98) | public static bool TryCast<T>(this JToken token, string key, [MaybeNul...
    method IsSafeFileName (line 117) | public static E<LocalStr> IsSafeFileName(string name)
    method SelectOk (line 128) | public static IEnumerable<TResult> SelectOk<TSource, TResult, TErr>(th...
    method HasExitedSafe (line 131) | public static bool HasExitedSafe(this Process process)
    method GetOrNew (line 137) | public static V GetOrNew<K, V>(this IDictionary<K, V> dict, K key) whe...
    method CatchToLog (line 147) | public static async Task CatchToLog(this Task t, NLog.Logger logger, N...
    method Try (line 159) | public static async Task<T?> Try<T>(this Task<T> t) where T : class
    method Try (line 165) | public static T? Try<T>(Func<T> t) where T : class
    method UnwrapThrow (line 171) | public static void UnwrapThrow(this E<LocalStr> r)
    method UnwrapThrow (line 177) | public static T UnwrapThrow<T>(this R<T, LocalStr> r) where T : notnull
    method UnwrapToLog (line 185) | public static bool UnwrapToLog(this E<LocalStr> r, NLog.Logger logger,...
    method UnwrapToLog (line 192) | public static bool UnwrapToLog(this E<CommandError> r, NLog.Logger log...

FILE: TS3AudioBot/Helper/WebWrapper.cs
  class WebWrapper (line 23) | public static class WebWrapper
    method WebWrapper (line 31) | static WebWrapper()
    method Request (line 44) | public static HttpRequestMessage Request(string? link) => Request(Crea...
    method Request (line 45) | public static HttpRequestMessage Request(Uri uri) => new HttpRequestMe...
    method WithMethod (line 49) | public static HttpRequestMessage WithMethod(this HttpRequestMessage re...
    method WithHeader (line 55) | public static HttpRequestMessage WithHeader(this HttpRequestMessage re...
    method WithTimeout (line 61) | public static HttpRequestMessage WithTimeout(this HttpRequestMessage r...
    method Send (line 69) | public static async Task Send(this HttpRequestMessage request)
    method AsString (line 84) | public static async Task<string> AsString(this HttpRequestMessage requ...
    method AsJson (line 100) | public static async Task<T> AsJson<T>(this HttpRequestMessage request)
    method ToAction (line 124) | public static async Task ToAction(this HttpRequestMessage request, Fun...
    method ToAction (line 140) | public static async Task<T> ToAction<T>(this HttpRequestMessage reques...
    method ToStream (line 156) | public static Task ToStream(this HttpRequestMessage request, Func<Stre...
    method UnsafeResponse (line 159) | public static async Task<HttpResponseMessage> UnsafeResponse(this Http...
    method UnsafeStream (line 175) | public static async Task<Stream> UnsafeStream(this HttpRequestMessage ...
    method GetSingle (line 180) | public static string? GetSingle(this HttpHeaders headers, string name)
    method SendDefaultAsync (line 183) | private static async Task<HttpResponseMessage> SendDefaultAsync(this H...
    method ToLoggedError (line 190) | private static AudioBotException ToLoggedError(Exception ex)
    method CreateUri (line 202) | private static Uri CreateUri(string? link)
    method CheckOkReturnCodeOrThrow (line 209) | private static void CheckOkReturnCodeOrThrow(HttpResponseMessage respo...
  class RedirectHandler (line 223) | public class RedirectHandler : DelegatingHandler
    method RedirectHandler (line 227) | public RedirectHandler(HttpMessageHandler innerHandler)
    method SendAsync (line 231) | protected override async Task<HttpResponseMessage> SendAsync(HttpReque...

FILE: TS3AudioBot/History/AudioLogEntry.cs
  class AudioLogEntry (line 17) | public class AudioLogEntry : IAudioResourceResult
    method AudioLogEntry (line 33) | public AudioLogEntry()
    method AudioLogEntry (line 39) | public AudioLogEntry(int id, AudioResource resource, string userUid) :...
    method SetName (line 46) | public void SetName(string newName)
    method ToString (line 51) | public override string ToString()

FILE: TS3AudioBot/History/HistoryManager.cs
  class HistoryManager (line 24) | public sealed class HistoryManager
    method HistoryManager (line 39) | static HistoryManager()
    method HistoryManager (line 45) | public HistoryManager(ConfHistory config, DbStore database)
    method RestoreFromFile (line 103) | private void RestoreFromFile()
    method LogAudioResource (line 108) | public AudioLogEntry? LogAudioResource(HistorySaveData saveData)
    method LogEntryPlay (line 137) | private void LogEntryPlay(AudioLogEntry ale)
    method CreateLogEntry (line 150) | private R<AudioLogEntry, Exception> CreateLogEntry(HistorySaveData sav...
    method FindByUniqueId (line 182) | private AudioLogEntry? FindByUniqueId(string uniqueId) => audioLogEntr...
    method Search (line 188) | public IEnumerable<AudioLogEntry> Search(SeachQuery search)
    method SearchParsed (line 214) | public string SearchParsed(SeachQuery query) => Format(Search(query));
    method Format (line 216) | public string Format(AudioLogEntry ale)
    method Format (line 218) | public string Format(IEnumerable<AudioLogEntry> aleList)
    method FindEntryByResource (line 221) | public AudioLogEntry? FindEntryByResource(AudioResource resource)
    method GetEntryById (line 230) | public R<AudioLogEntry, LocalStr> GetEntryById(uint id)
    method RemoveEntry (line 239) | public bool RemoveEntry(AudioLogEntry ale)
    method RenameEntry (line 250) | public void RenameEntry(AudioLogEntry ale, string newName)
    method RemoveBrokenLinks (line 261) | public async Task RemoveBrokenLinks(ResolveContext resourceFactory)
    method FilterList (line 287) | private async Task<List<AudioLogEntry>> FilterList(ResolveContext reso...
    method UpdadeDbIdToUid (line 309) | public async Task UpdadeDbIdToUid(Ts3Client ts3Client)

FILE: TS3AudioBot/History/HistorySaveData.cs
  class HistorySaveData (line 16) | public class HistorySaveData
    method HistorySaveData (line 21) | public HistorySaveData(AudioResource resource, Uid? invokerUid)

FILE: TS3AudioBot/History/IHistoryFormatter.cs
  type IHistoryFormatter (line 15) | public interface IHistoryFormatter
    method ProcessQuery (line 17) | string ProcessQuery(AudioLogEntry entry, Func<AudioLogEntry, string> f...
    method ProcessQuery (line 18) | string ProcessQuery(IEnumerable<AudioLogEntry> entries, Func<AudioLogE...
  type HistoryDisplayColumn (line 22) | public enum HistoryDisplayColumn

FILE: TS3AudioBot/History/SearchQuery.cs
  class SeachQuery (line 14) | public class SeachQuery
    method SeachQuery (line 21) | public SeachQuery()

FILE: TS3AudioBot/History/SmartHistoryFormatter.cs
  class SmartHistoryFormatter (line 18) | public class SmartHistoryFormatter : IHistoryFormatter
    method ProcessQuery (line 28) | public string ProcessQuery(AudioLogEntry entry, Func<AudioLogEntry, st...
    method ProcessQuery (line 33) | public string ProcessQuery(IEnumerable<AudioLogEntry> entries, Func<Au...
    method DefaultAleFormat (line 131) | public static string DefaultAleFormat(AudioLogEntry e)
    method SubstringToken (line 138) | private static string SubstringToken(string value, int token)
    class Line (line 150) | private class Line
      method Line (line 156) | public Line(string value, int tokenLength)
      method ToString (line 163) | public override string ToString() => $"[{TokenLength:0000}+{BonusTok...

FILE: TS3AudioBot/InvokerData.cs
  class InvokerData (line 14) | public class InvokerData
    method InvokerData (line 21) | public InvokerData(Uid clientUid)

FILE: TS3AudioBot/Limits.cs
  class Limits (line 12) | public static class Limits

FILE: TS3AudioBot/Localization/DynamicResourceManager.cs
  class DynamicResourceManager (line 18) | internal class DynamicResourceManager : ResourceManager
    method DynamicResourceManager (line 22) | public DynamicResourceManager(string baseName, Assembly assembly) : ba...
    method SetResourceSet (line 26) | public void SetResourceSet(CultureInfo culture, ResourceSet set)
    method GetResourceSet (line 31) | public override ResourceSet? GetResourceSet(CultureInfo culture, bool ...
    method GetString (line 46) | public override string? GetString(string name, CultureInfo? culture)

FILE: TS3AudioBot/Localization/LocalStr.cs
  type LocalStr (line 15) | public readonly struct LocalStr
    method LocalStr (line 21) | public LocalStr(string str)
    method ToString (line 26) | public override readonly string ToString() => Str;

FILE: TS3AudioBot/Localization/LocalizationManager.cs
  class LocalizationManager (line 22) | public class LocalizationManager
    method LocalizationManager (line 31) | static LocalizationManager()
    method LoadLanguage (line 46) | public async ValueTask<E<string>> LoadLanguage(string lang, bool force...
    method ApplyLanguage (line 67) | public void ApplyLanguage()
    method LoadLanguageAssembly (line 77) | private static async Task<E<string>> LoadLanguageAssembly(LanguageData...
    method GetWithFallbackCultures (line 149) | private static IEnumerable<CultureInfo> GetWithFallbackCultures(Cultur...
    method GetCultureFileInfo (line 159) | private static FileInfo GetCultureFileInfo(CultureInfo culture)
    method DownloadAvaliableLanguages (line 162) | private static async Task<HashSet<string>?> DownloadAvaliableLanguages()
    method GetString (line 177) | public static string? GetString(string name)
    class LanguageData (line 182) | private class LanguageData

FILE: TS3AudioBot/Localization/strings.Designer.cs
  class strings (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
    method strings (line 31) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...

FILE: TS3AudioBot/MainCommands.cs
  class MainCommands (line 51) | public static class MainCommands
    class MainCommandsBag (line 55) | internal class MainCommandsBag : ICommandBag
    method CommandAdd (line 72) | [Command("add")]
    method CommandAdd (line 76) | [Command("add")]
    method CommandAliasAdd (line 80) | [Command("alias add")]
    method CommandAliasRemove (line 90) | [Command("alias remove")]
    method CommandAliasList (line 99) | [Command("alias list")]
    method CommandAliasShow (line 103) | [Command("alias show")]
    method CommandApiToken (line 107) | [Command("api token")]
    method CommandBotAvatarSet (line 129) | [Command("bot avatar set")]
    method CommandBotAvatarClear (line 141) | [Command("bot avatar clear")]
    method CommandBotBadges (line 144) | [Command("bot badges")]
    method CommandBotDescriptionSet (line 147) | [Command("bot description set")]
    method CommandBotDiagnose (line 150) | [Command("bot diagnose", "_undocumented")]
    method CommandBotDisconnect (line 218) | [Command("bot disconnect")]
    method CommandBotCommander (line 221) | [Command("bot commander")]
    method CommandBotCommanderOn (line 227) | [Command("bot commander on")]
    method CommandBotCommanderOff (line 229) | [Command("bot commander off")]
    method CommandBotCome (line 232) | [Command("bot come")]
    method CommandBotConnectTo (line 241) | [Command("bot connect template")]
    method CommandBotConnectNew (line 250) | [Command("bot connect to")]
    method CommandBotInfo (line 263) | [Command("bot info")]
    method CommandBotInfoClient (line 266) | [Command("bot info client", "_undocumented")]
    method CommandBotInfo (line 269) | [Command("bot info template", "cmd_bot_info_help")]
    method CommandBotList (line 279) | [Command("bot list")]
    method GetOfflineBotInfo (line 302) | private static R<BotInfo, LocalStr> GetOfflineBotInfo(ConfRoot config,...
    method GetOfflineBotInfo (line 311) | private static BotInfo GetOfflineBotInfo(ConfBot botConfig)
    method CommandBotMove (line 322) | [Command("bot move")]
    method CommandBotName (line 325) | [Command("bot name")]
    method CommandBotSetup (line 328) | [Command("bot save")]
    method CommandBotSetup (line 331) | [Command("bot setup")]
    method CommandBotTemplate (line 337) | [Command("bot template", "cmd_bot_use_help")]
    method CommandBotUse (line 344) | [Command("bot use")]
    method CommandBotUseInternal (line 351) | private static async Task<object?> CommandBotUseInternal(ExecutionInfo...
    method CommandClear (line 368) | [Command("clear")]
    method CommandParse (line 371) | [Command("command parse", "cmd_parse_command_help")]
    method CommandTree (line 381) | [Command("command tree", "_undocumented")]
    method CommandData (line 387) | [Command("data song cover get", "_undocumented")]
    method CommandEval (line 402) | [Command("eval")]
    method CommandFrom (line 426) | [Command("from", "_undocumented")]
    method CommandGet (line 430) | [Command("get", "_undocumented")]
    method CommandGetId (line 443) | [Command("getmy id")]
    method CommandGetUid (line 446) | [Command("getmy uid")]
    method CommandGetName (line 449) | [Command("getmy name")]
    method CommandGetDbId (line 452) | [Command("getmy dbid")]
    method CommandGetChannel (line 455) | [Command("getmy channel")]
    method CommandGetUser (line 458) | [Command("getmy all")]
    method CommandGetUidById (line 462) | [Command("getuser uid byid")]
    method CommandGetNameById (line 464) | [Command("getuser name byid")]
    method CommandGetDbIdById (line 466) | [Command("getuser dbid byid")]
    method CommandGetChannelById (line 468) | [Command("getuser channel byid")]
    method CommandGetUserById (line 470) | [Command("getuser all byid")]
    method CommandGetIdByName (line 476) | [Command("getuser id byname")]
    method CommandGetUserByName (line 478) | [Command("getuser all byname")]
    method CommandGetNameByDbId (line 484) | [Command("getuser name bydbid")]
    method CommandGetUidByDbId (line 486) | [Command("getuser uid bydbid")]
    method CommandHelp (line 492) | [Command("help")]
    method CommandHelpAll (line 508) | [Command("help all", "_undocumented")]
    method CommandHelpCommand (line 522) | [Command("help command", "_undocumented")]
    method CommandHelpPlay (line 572) | [Command("help play", "_undocumented")]
    method CommandHistoryQueue (line 578) | [Command("history add")]
    method CommandHistoryClean (line 585) | [Command("history clean")]
    method CommandHistoryCleanRemove (line 607) | [Command("history clean removedefective")]
    method CommandHistoryCleanUpgrade (line 629) | [Command("history clean upgrade", "_undocumented")]
    method CommandHistoryDelete (line 635) | [Command("history delete")]
    method CommandHistoryFrom (line 663) | [Command("history from")]
    method CommandHistoryId (line 674) | [Command("history id", "cmd_history_id_uint_help")]
    method CommandHistoryId (line 681) | [Command("history id", "cmd_history_id_string_help")]
    method CommandHistoryLast (line 692) | [Command("history last", "cmd_history_last_int_help")]
    method CommandHistoryLast (line 700) | [Command("history last", "cmd_history_last_help")]
    method CommandHistoryPlay (line 709) | [Command("history play")]
    method CommandHistoryRename (line 716) | [Command("history rename")]
    method CommandHistoryTill (line 727) | [Command("history till", "cmd_history_till_DateTime_help")]
    method CommandHistoryTill (line 735) | [Command("history till", "cmd_history_till_string_help")]
    method CommandHistoryTitle (line 751) | [Command("history title")]
    method CommandIf (line 759) | [Command("if")]
    method GetIndexExpression (line 799) | private static int GetIndexExpression(PlaylistManager playlistManager,...
    method CommandInfo (line 822) | [Command("info")]
    method CommandInfo (line 826) | [Command("info")]
    method CommandJsonMerge (line 870) | [Command("json merge")]
    method CommandJsonApi (line 891) | [Command("json api", "_undocumented")]
    method CommandJump (line 899) | [Command("jump")]
    method CommandKickme (line 906) | [Command("kickme")]
    method CommandKickmeFar (line 910) | [Command("kickme far", "cmd_kickme_help")]
    method CommandKickme (line 914) | private static async Task CommandKickme(Ts3Client ts3Client, ClientCal...
    method CommandListAdd (line 927) | [Command("list add")]
    method CommandListClear (line 942) | [Command("list clear")]
    method CommandListCreate (line 946) | [Command("list create", "_undocumented")]
    method CommandListDelete (line 950) | [Command("list delete")]
    method CommandListDelete (line 966) | [Command("list delete")]
    method PropagiateLoad (line 970) | [Command("list from", "_undocumented")]
    method CommandListImport (line 977) | [Command("list import", "cmd_list_get_help")] // TODO readjust help texts
    method ImportMerge (line 984) | private static JsonValue<PlaylistInfo> ImportMerge(PlaylistManager pla...
    method CommandListAdd (line 997) | [Command("list insert", "_undocumented")]  // TODO Doc
    method CommandListItemMove (line 1015) | [Command("list item get", "_undocumented")]
    method CommandListItemMove (line 1025) | [Command("list item move")] // TODO return modified elements
    method CommandListItemDelete (line 1045) | [Command("list item delete")] // TODO return modified elements
    method CommandListItemName (line 1060) | [Command("list item name")] // TODO return modified elements
    method CommandListList (line 1072) | [Command("list list")]
    method CommandListMerge (line 1083) | [Command("list merge")]
    method CommandListName (line 1093) | [Command("list name")]
    method CommandListPlayInternal (line 1097) | [Command("list play")]
    method CommandListQueue (line 1111) | [Command("list queue")]
    method CommandListShow (line 1118) | [Command("list show")]
    method CommandNext (line 1145) | [Command("next")]
    method CommandParam (line 1149) | [Command("param", "_undocumented")] // TODO add documentation, when na...
    method CommandPm (line 1165) | [Command("pm")]
    method CommandPmChannel (line 1172) | [Command("pm channel", "_undocumented")] // TODO
    method CommandPmServer (line 1175) | [Command("pm server", "_undocumented")] // TODO
    method CommandPmUser (line 1178) | [Command("pm user")]
    method CommandPause (line 1181) | [Command("pause")]
    method CommandPlay (line 1184) | [Command("play")]
    method CommandPlay (line 1193) | [Command("play")]
    method CommandPlay (line 1197) | [Command("play")]
    method CommandPluginList (line 1201) | [Command("plugin list")]
    method CommandPluginUnload (line 1205) | [Command("plugin unload")]
    method CommandPluginLoad (line 1213) | [Command("plugin load")]
    method CommandPrevious (line 1221) | [Command("previous")]
    method CommandPrint (line 1225) | [Command("print")]
    method CommandQuiz (line 1235) | [Command("quiz")]
    method CommandQuizOn (line 1237) | [Command("quiz on")]
    method CommandQuizOff (line 1245) | [Command("quiz off")]
    method CommandRandom (line 1256) | [Command("random")]
    method CommandRandomOn (line 1258) | [Command("random on")]
    method CommandRandomOff (line 1260) | [Command("random off")]
    method CommandRandomSeed (line 1262) | [Command("random seed", "cmd_random_seed_help")]
    method CommandRandomSeed (line 1268) | [Command("random seed", "cmd_random_seed_string_help")]
    method CommandRandomSeed (line 1275) | [Command("random seed", "cmd_random_seed_int_help")]
    method CommandRepeat (line 1278) | [Command("repeat")]
    method CommandRepeatOff (line 1287) | [Command("repeat off")]
    method CommandRepeatOne (line 1289) | [Command("repeat one")]
    method CommandRepeatAll (line 1291) | [Command("repeat all")]
    method CommandRightsCan (line 1294) | [Command("rights can")]
    method CommandRightsReload (line 1298) | [Command("rights reload")]
    method CommandRng (line 1308) | [Command("rng")]
    method CommandSeek (line 1330) | [Command("seek")]
    method GetSearchResult (line 1344) | private static IList<AudioResource> GetSearchResult(this UserSession s...
    method GetSingleSearchResult (line 1352) | private static AudioResource GetSingleSearchResult(this UserSession se...
    method FormatSearchResult (line 1362) | private static JsonArray<AudioResource> FormatSearchResult(IList<Audio...
    method CommandSearchAdd (line 1381) | [Command("search add", "_undocumented")] // TODO Doc
    method PropagiateSearch (line 1385) | [Command("search from", "_undocumented")] // TODO Doc
    method CommandSearchGet (line 1393) | [Command("search get", "_undocumented")] // TODO Doc
    method CommandSeachPlay (line 1397) | [Command("search play", "_undocumented")] // TODO Doc
    method CommandSearchShow (line 1401) | [Command("search show", "_undocumented")] // TODO Doc
    method CommandServerTree (line 1405) | [Command("server tree", "_undocumented")]
    method CommandSettings (line 1411) | [Command("settings")]
    method CommandSettingsCopy (line 1415) | [Command("settings copy")]
    method CommandSettingsCreate (line 1418) | [Command("settings create")]
    method CommandSettingsDelete (line 1421) | [Command("settings delete")]
    method CommandSettingsGet (line 1424) | [Command("settings get")]
    method CommandSettingsSet (line 1428) | [Command("settings set")]
    method CommandSettingsBotGet (line 1439) | [Command("settings bot get", "cmd_settings_get_help")]
    method CommandSettingsBotSet (line 1446) | [Command("settings bot set", "cmd_settings_set_help")]
    method CommandSettingsReload (line 1453) | [Command("settings bot reload")]
    method CommandSettingsGlobalGet (line 1462) | [Command("settings global get")]
    method CommandSettingsGlobalSet (line 1466) | [Command("settings global set")]
    method CommandSettingsGlobalReload (line 1478) | public static void CommandSettingsGlobalReload(ConfRoot config)
    method GetConf (line 1484) | private static async Task<ConfigPart> GetConf(Bot? bot, ConfRoot confi...
    method SettingsGet (line 1502) | private static ConfigPart SettingsGet(ConfigPart config, string? path ...
    method SettingsSet (line 1504) | private static void SettingsSet(ConfigPart config, string path, string...
    method SettingsGetSingle (line 1519) | private static ConfigPart SettingsGetSingle(this ConfigPart[] configPa...
    method CommandSettingsHelp (line 1539) | [Command("settings help")]
    method CommandSong (line 1546) | [Command("song")]
    method CommandStop (line 1581) | [Command("stop")]
    method CommandSubscribe (line 1584) | [Command("subscribe")]
    method CommandSubscribeTempChannel (line 1591) | [Command("subscribe tempchannel")]
    method CommandSubscribeChannel (line 1599) | [Command("subscribe channel")]
    method CommandSubscribeUser (line 1611) | [Command("subscribe client")]
    method CommandSystemInfo (line 1617) | [Command("system info", "_undocumented")]
    method CommandSystemQuit (line 1634) | [Command("system quit", "cmd_quit_help")]
    method CommandTake (line 1658) | [Command("take")]
    method CommandUnsubscribe (line 1699) | [Command("unsubscribe")]
    method CommandUnsubscribeChannel (line 1706) | [Command("unsubscribe channel")]
    method CommandUnsubscribeTemporary (line 1721) | [Command("unsubscribe temporary")]
    method CommandUnsubscribeUser (line 1724) | [Command("unsubscribe client")]
    method CommandVersion (line 1730) | [Command("version")]
    method CommandVolume (line 1733) | [Command("volume")]
    method CommandVolume (line 1737) | [Command("volume")]
    method CommandWhisperAll (line 1782) | [Command("whisper all")]
    method CommandWhisperGroup (line 1785) | [Command("whisper group")]
    method CommandWhisperList (line 1804) | [Command("whisper list")]
    method CommandWhisperOff (line 1846) | [Command("whisper off")]
    method CommandWhisperSubsription (line 1849) | [Command("whisper subscription")]
    method CommandXecute (line 1852) | [Command("xecute")]
    method HasRights (line 1860) | public static async ValueTask<bool> HasRights(this ExecutionInformatio...
    method Write (line 1870) | public static async Task Write(this ExecutionInformation info, string ...
    method UseComplexityTokens (line 1908) | public static void UseComplexityTokens(this ExecutionInformation info,...

FILE: TS3AudioBot/Playlists/LoopMode.cs
  type LoopMode (line 12) | public enum LoopMode

FILE: TS3AudioBot/Playlists/Parser/JspfContent.cs
  class JspfContent (line 20) | public class JspfContent : IPlaylistParser<XspfPlaylist>, IPlaylistWrite...
    method GetFromStream (line 22) | public XspfPlaylist GetFromStream(Stream stream)
    method GetFromString (line 30) | public XspfPlaylist GetFromString(string playlistString)
    method ToText (line 35) | public string ToText(XspfPlaylist playlist)
  class XspfPlaylist (line 41) | public class XspfPlaylist : IBasePlaylist
    method XspfPlaylist (line 54) | public XspfPlaylist()
    method GetTracksPaths (line 58) | public List<string> GetTracksPaths() => PlaylistEntries.Select(x => x....
  class XspfPlaylistEntry (line 61) | public class XspfPlaylistEntry
    method XspfPlaylistEntry (line 63) | public XspfPlaylistEntry() { }
  class XspfMeta (line 78) | public class XspfMeta
    method XspfMeta (line 83) | public XspfMeta(string key, string value)
  class JspfMetaConverter (line 90) | internal class JspfMetaConverter : JsonConverter<XspfMeta>
    method ReadJson (line 92) | public override XspfMeta ReadJson(JsonReader reader, Type objectType, ...
    method WriteJson (line 101) | public override void WriteJson(JsonWriter writer, XspfMeta? value, Jso...

FILE: TS3AudioBot/Playlists/Playlist.cs
  class Playlist (line 17) | public class Playlist : IReadOnlyPlaylist
    method Playlist (line 27) | public Playlist() :
    method Playlist (line 31) | public Playlist(List<PlaylistItem> items)
    method SetTitle (line 37) | public Playlist SetTitle(string newTitle)
    method GetMaxAdd (line 44) | private int GetMaxAdd(int amount)
    method Add (line 50) | public E<LocalStr> Add(PlaylistItem song)
    method AddRange (line 60) | public E<LocalStr> AddRange(IEnumerable<PlaylistItem> songs)
    method RemoveAt (line 71) | public void RemoveAt(int index) => items.RemoveAt(index);
    method Insert (line 73) | public E<LocalStr> Insert(int index, PlaylistItem song)
    method Clear (line 83) | public void Clear() => items.Clear();
  type IReadOnlyPlaylist (line 88) | public interface IReadOnlyPlaylist

FILE: TS3AudioBot/Playlists/PlaylistApiExtensions.cs
  class PlaylistApiExtensions (line 15) | public static class PlaylistApiExtensions
    method ToApiFormat (line 17) | public static PlaylistItemGetData ToApiFormat(this ResolveContext reso...

FILE: TS3AudioBot/Playlists/PlaylistIO.cs
  class PlaylistIO (line 26) | public class PlaylistIO : IDisposable
    method PlaylistIO (line 37) | public PlaylistIO(ConfBot confBot)
    method NameToFile (line 42) | private FileInfo? NameToFile(string listId)
    method Read (line 50) | public R<Playlist, LocalStr> Read(string listId) => ReadInternal(listI...
    method ReadInternal (line 52) | private R<Playlist, LocalStr> ReadInternal(string listId, bool hasRead...
    method ReadFromFile (line 97) | private R<Playlist, LocalStr> ReadFromFile(string listId, bool headOnl...
    method ReadHeadStream (line 172) | private R<PlaylistMeta, LocalStr> ReadHeadStream(StreamReader sr)
    method Write (line 206) | public E<LocalStr> Write(string listId, IReadOnlyPlaylist list)
    method WriteToFile (line 222) | private E<LocalStr> WriteToFile(string listId, IReadOnlyPlaylist plist)
    method Delete (line 260) | public E<LocalStr> Delete(string listId)
    method DeleteInternal (line 273) | private E<LocalStr> DeleteInternal(string listId)
    method ListPlaylists (line 294) | public R<PlaylistInfo[], LocalStr> ListPlaylists(string? pattern)
    method ReloadFolderCache (line 345) | public void ReloadFolderCache() => reloadFolderCache = true;
    method Exists (line 347) | public bool Exists(string listId)
    method ExistsInternal (line 360) | public bool ExistsInternal(string listId)
    method Flush (line 368) | public void Flush()
    method Dispose (line 388) | public void Dispose()
  class PlaylistMeta (line 396) | public class PlaylistMeta

FILE: TS3AudioBot/Playlists/PlaylistItem.cs
  class PlaylistItem (line 17) | public class PlaylistItem : IAudioResourceResult, IMetaContainer
    method PlaylistItem (line 22) | public PlaylistItem(AudioResource resource, PlayInfo? meta = null)
    method From (line 28) | public static PlaylistItem From(PlayResource playResource)
    method ToString (line 33) | public override string ToString() => AudioResource.ResourceTitle ?? $"...

FILE: TS3AudioBot/Playlists/PlaylistManager.cs
  class PlaylistManager (line 21) | public sealed class PlaylistManager
    method PlaylistManager (line 59) | public PlaylistManager(ConfPlaylists _, PlaylistIO playlistPool)
    method Next (line 65) | public PlaylistItem? Next(bool manually = true) => MoveIndex(forward: ...
    method Previous (line 67) | public PlaylistItem? Previous(bool manually = true) => MoveIndex(forwa...
    method MoveIndex (line 69) | internal PlaylistItem? MoveIndex(bool? forward, bool manually)
    method Queue (line 107) | public void Queue(PlaylistItem item)
    method Queue (line 110) | public void Queue(IEnumerable<PlaylistItem> items)
    method Clear (line 113) | public void Clear()
    method SetRandomSeed (line 116) | private void SetRandomSeed()
    method LoadPlaylist (line 121) | public R<IReadOnlyPlaylist, LocalStr> LoadPlaylist(string listId)
    method CreatePlaylist (line 141) | public E<LocalStr> CreatePlaylist(string listId, string? title = null)
    method ExistsPlaylist (line 151) | public bool ExistsPlaylist(string listId)
    method ModifyPlaylist (line 161) | public E<LocalStr> ModifyPlaylist(string listId, Action<Playlist> action)
    method DeletePlaylist (line 190) | public E<LocalStr> DeletePlaylist(string listId)
    method GetAvailablePlaylists (line 199) | public R<PlaylistInfo[], LocalStr> GetAvailablePlaylists(string? patte...
    method GetSpecialPlaylist (line 201) | private R<Playlist, LocalStr> GetSpecialPlaylist(string listId)

FILE: TS3AudioBot/Playlists/Shuffle/IShuffleAlgorithm.cs
  type IShuffleAlgorithm (line 12) | public interface IShuffleAlgorithm
    method Next (line 18) | bool Next();
    method Prev (line 20) | bool Prev();

FILE: TS3AudioBot/Playlists/Shuffle/LinearFeedbackShiftRegister.cs
  class LinearFeedbackShiftRegister (line 15) | public class LinearFeedbackShiftRegister : IShuffleAlgorithm
    method Recalc (line 45) | private void Recalc()
    method Next (line 62) | public bool Next()
    method NextOf (line 73) | private int NextOf(int val)
    method Prev (line 81) | public bool Prev()
    method PrevOf (line 92) | private int PrevOf(int val)
    method PrevOfTest (line 103) | private int PrevOfTest(int val, int lsb)
    method GenerateGaloisMask (line 109) | private static int GenerateGaloisMask(int bits, int seedOffset)
    method TestLfsr (line 129) | private static bool TestLfsr(int mask, int max)
    method NumberOfSetBits (line 144) | private static int NumberOfSetBits(int i)

FILE: TS3AudioBot/Playlists/Shuffle/ListedShuffle.cs
  class ListedShuffle (line 16) | public class ListedShuffle : IShuffleAlgorithm
    method GenList (line 51) | private void GenList()
    method Next (line 63) | public bool Next()
    method Prev (line 71) | public bool Prev()

FILE: TS3AudioBot/Playlists/Shuffle/NormalOrder.cs
  class NormalOrder (line 14) | public class NormalOrder : IShuffleAlgorithm
    method Next (line 20) | public bool Next()
    method Prev (line 26) | public bool Prev()

FILE: TS3AudioBot/Plugins/ITabPlugin.cs
  type ITabPlugin (line 14) | public interface ITabPlugin : IDisposable
    method Initialize (line 16) | void Initialize();
  type ICorePlugin (line 19) | public interface ICorePlugin : ITabPlugin { }
  type IBotPlugin (line 21) | public interface IBotPlugin : ITabPlugin { }
  type IPluginMeta (line 23) | public interface IPluginMeta
  class StaticPluginAttribute (line 32) | [AttributeUsage(AttributeTargets.Class, Inherited = false)]

FILE: TS3AudioBot/Plugins/Plugin.cs
  class Plugin (line 28) | internal class Plugin
    method Plugin (line 49) | public Plugin(CoreInjector coreInjector, ResourceResolver resourceReso...
    method CheckStatus (line 91) | public PluginStatus CheckStatus(Bot? bot)
    method Load (line 108) | public PluginResponse Load()
    method Md5EqualsCache (line 163) | private bool Md5EqualsCache()
    method PrepareBinary (line 178) | private PluginResponse PrepareBinary()
    method PrepareSource (line 195) | private PluginResponse PrepareSource()
    method InitializeAssembly (line 247) | private PluginResponse InitializeAssembly(Assembly assembly)
    method Start (line 315) | public PluginResponse Start(Bot? bot)
    method StartInternal (line 345) | private bool StartInternal(Bot? bot)
    method CreatePluginObjects (line 422) | public static PluginObjects CreatePluginObjects(IInjector injector, Ty...
    method Stop (line 448) | public PluginResponse Stop(Bot? bot)
    method DestroyPluginObjects (line 501) | private void DestroyPluginObjects(PluginObjects pluginObjs)
    method Unload (line 515) | public void Unload()
    method ToString (line 525) | public override string ToString() => Name;
  type PluginType (line 528) | public enum PluginType

FILE: TS3AudioBot/Plugins/PluginCommandBag.cs
  class PluginCommandBag (line 17) | internal class PluginCommandBag : ICommandBag
    method PluginCommandBag (line 22) | public PluginCommandBag(object? obj, Type t)

FILE: TS3AudioBot/Plugins/PluginExtensions.cs
  class PluginExtensions (line 16) | public static class PluginExtensions
    method GetLogger (line 18) | [MethodImpl(MethodImplOptions.NoInlining)]

FILE: TS3AudioBot/Plugins/PluginManager.cs
  class PluginManager (line 35) | public class PluginManager : IDisposable
    method PluginManager (line 48) | public PluginManager(ConfPlugins config, CoreInjector coreInjector, Re...
    method CheckAndClearPlugins (line 56) | private void CheckAndClearPlugins(Bot? bot)
    method CheckLocalPlugins (line 64) | private void CheckLocalPlugins(Bot? bot)
    method ClearMissingFiles (line 109) | private void ClearMissingFiles()
    method IsIgnored (line 123) | public static bool IsIgnored(FileInfo file) =>
    method TryGetPlugin (line 127) | private Plugin TryGetPlugin(string identifier)
    method GetFreeId (line 138) | private int GetFreeId()
    method StartPlugin (line 147) | public PluginResponse StartPlugin(string identifier, Bot? bot)
    method StopPlugin (line 157) | public PluginResponse StopPlugin(string identifier, Bot? bot)
    method StopPlugins (line 165) | internal void StopPlugins(Bot bot)
    method RemovePlugin (line 174) | private void RemovePlugin(Plugin plugin)
    method GetPluginOverview (line 181) | public PluginStatusInfo[] GetPluginOverview(Bot? bot)
    method FormatOverview (line 198) | public static string FormatOverview(ICollection<PluginStatusInfo> plug...
    method Dispose (line 224) | public void Dispose()
  class PluginStatusInfo (line 231) | public class PluginStatusInfo
    method PluginStatusInfo (line 238) | public PluginStatusInfo(int id, string name, PluginStatus status, Plug...

FILE: TS3AudioBot/Plugins/PluginObjects.cs
  class PluginObjects (line 14) | internal class PluginObjects
    method PluginObjects (line 20) | public PluginObjects(ITabPlugin plugin, PluginCommandBag bag, CommandM...

FILE: TS3AudioBot/Plugins/PluginResponse.cs
  type PluginResponse (line 12) | public enum PluginResponse

FILE: TS3AudioBot/Plugins/PluginStatus.cs
  type PluginStatus (line 12) | public enum PluginStatus

FILE: TS3AudioBot/ResourceFactories/AudioResource.cs
  class AudioResource (line 17) | public class AudioResource : IAudioResourceResult
    method AudioResource (line 38) | public AudioResource() { }
    method AudioResource (line 41) | public AudioResource(string resourceId, string? resourceTitle, string ...
    method Add (line 49) | public AudioResource Add(string key, string value)
    method Get (line 56) | public string? Get(string key)
    method Equals (line 63) | public override bool Equals(object? obj)
    method GetHashCode (line 72) | public override int GetHashCode() => HashCode.Combine(AudioType, Resou...
    method ToString (line 74) | public override string ToString()

FILE: TS3AudioBot/ResourceFactories/AudioTags/AudioTagReader.cs
  class AudioTagReader (line 17) | internal static class AudioTagReader
    method AudioTagReader (line 22) | static AudioTagReader()
    method Register (line 28) | private static void Register(Tag tagHeader)
    method GetData (line 33) | public static HeaderData? GetData(Stream fileStream)
    class Tag (line 54) | private abstract class Tag
      method GetData (line 57) | public abstract HeaderData GetData(BinaryReader fileStream);
    class Id3_1 (line 61) | private class Id3_1 : Tag
      method GetData (line 66) | public override HeaderData GetData(BinaryReader fileStream)
    class Id3_2 (line 79) | private class Id3_2 : Tag
      method GetData (line 91) | public override HeaderData GetData(BinaryReader fileStream)
      method ReadNullTermString (line 199) | private static int ReadNullTermString(BinaryReader fileStream, byte ...
      method GetEncoding (line 228) | private static Encoding GetEncoding(byte type)
      method DecodeString (line 240) | private static string DecodeString(byte type, byte[] textBuffer, int...
      method FrameIdV2 (line 243) | private static int FrameIdV2(string id)
      method FrameIdV3 (line 248) | private static uint FrameIdV3(string id)
  class HeaderData (line 257) | internal class HeaderData

FILE: TS3AudioBot/ResourceFactories/AudioTags/BinaryReaderBigEndianExtensions.cs
  class BinaryReaderBigEndianExtensions (line 15) | internal static class BinaryReaderBigEndianExtensions
    method ReadInt16Be (line 17) | public static short ReadInt16Be(this BinaryReader br)
    method ReadInt24Be (line 21) | public static int ReadInt24Be(this BinaryReader br)
    method ReadInt32Be (line 25) | public static int ReadInt32Be(this BinaryReader br)
    method ReadInt64Be (line 29) | public static long ReadInt64Be(this BinaryReader br)
    method ReadUInt16Be (line 34) | public static ushort ReadUInt16Be(this BinaryReader br)
    method ReadUInt32Be (line 38) | public static uint ReadUInt32Be(this BinaryReader br)
    method ReadUInt64Be (line 42) | public static ulong ReadUInt64Be(this BinaryReader br)
    method ReadId3Int (line 47) | public static int ReadId3Int(this BinaryReader br)
  class BitConverterBigEndian (line 58) | internal static class BitConverterBigEndian
    method ToInt16 (line 62) | public static short ToInt16(byte[] bytes)
    method ToInt24 (line 68) | public static int ToInt24(byte[] bytes)
    method ToInt32 (line 75) | public static int ToInt32(byte[] bytes)
    method ToInt64 (line 83) | public static long ToInt64(byte[] bytes)
    method ToUInt16 (line 100) | public static ushort ToUInt16(byte[] bytes)
    method ToUInt32 (line 106) | public static uint ToUInt32(byte[] bytes)
    method ToUInt64 (line 114) | public static ulong ToUInt64(byte[] bytes)
    type ReinterpretInt (line 131) | [StructLayout(LayoutKind.Explicit)]

FILE: TS3AudioBot/ResourceFactories/AudioTags/M3uReader.cs
  class M3uReader (line 18) | public static class M3uReader
    method TryGetData (line 27) | public static async Task<List<M3uEntry>> TryGetData(Stream stream)
  class M3uEntry (line 133) | public class M3uEntry
    method M3uEntry (line 139) | public M3uEntry(string trackUrl, string? title, string? streamMeta)

FILE: TS3AudioBot/ResourceFactories/BandcampResolver.cs
  class BandcampResolver (line 21) | public class BandcampResolver : IResourceResolver, IThumbnailResolver
    method MatchResource (line 34) | public MatchCertainty MatchResource(ResolveContext _, string uri) => B...
    method GetResource (line 36) | public async Task<PlayResource> GetResource(ResolveContext _, string url)
    method GetResourceById (line 73) | public async Task<PlayResource> GetResourceById(ResolveContext _, Audi...
    method RestoreLink (line 92) | public string RestoreLink(ResolveContext _, AudioResource resource)
    method DownloadEmbeddedSite (line 104) | private static Task<string> DownloadEmbeddedSite(string id)
    method GetThumbnail (line 107) | public async Task GetThumbnail(ResolveContext _, PlayResource playReso...
    method GetTrackArtId (line 143) | private static string? GetTrackArtId(string site)
    method Dispose (line 151) | public void Dispose() { }
  class BandcampPlayResource (line 154) | public class BandcampPlayResource : PlayResource
    method BandcampPlayResource (line 158) | public BandcampPlayResource(string uri, AudioResource baseData, string...

FILE: TS3AudioBot/ResourceFactories/IPlaylistResolver.cs
  type IPlaylistResolver (line 15) | public interface IPlaylistResolver : IResolver
    method MatchPlaylist (line 17) | MatchCertainty MatchPlaylist(ResolveContext ctx, string uri);
    method GetPlaylist (line 19) | Task<Playlist> GetPlaylist(ResolveContext ctx, string url);

FILE: TS3AudioBot/ResourceFactories/IResolver.cs
  type IResolver (line 14) | public interface IResolver : IDisposable

FILE: TS3AudioBot/ResourceFactories/IResourceResolver.cs
  type IResourceResolver (line 14) | public interface IResourceResolver : IResolver
    method MatchResource (line 19) | MatchCertainty MatchResource(ResolveContext ctx, string uri);
    method GetResource (line 23) | Task<PlayResource> GetResource(ResolveContext ctx, string uri);
    method GetResourceById (line 27) | Task<PlayResource> GetResourceById(ResolveContext ctx, AudioResource r...
    method RestoreLink (line 31) | string RestoreLink(ResolveContext ctx, AudioResource resource);

FILE: TS3AudioBot/ResourceFactories/ISearchResolver.cs
  type ISearchResolver (line 15) | public interface ISearchResolver : IResolver
    method Search (line 17) | Task<IList<AudioResource>> Search(ResolveContext ctx, string keyword);

FILE: TS3AudioBot/ResourceFactories/IThumbnailResolver.cs
  type IThumbnailResolver (line 16) | public interface IThumbnailResolver : IResolver
    method GetThumbnail (line 18) | Task GetThumbnail(ResolveContext ctx, PlayResource playResource, Func<...

FILE: TS3AudioBot/ResourceFactories/MatchCertainty.cs
  type MatchCertainty (line 12) | public enum MatchCertainty
  class MatchCertaintyExtensions (line 22) | public static class MatchCertaintyExtensions
    method ToMatchCertainty (line 24) | public static MatchCertainty ToMatchCertainty(this bool val) => val ? ...

FILE: TS3AudioBot/ResourceFactories/MediaResolver.cs
  class MediaResolver (line 24) | public sealed class MediaResolver : IResourceResolver, IPlaylistResolver...
    method MatchResource (line 30) | public MatchCertainty MatchResource(ResolveContext _, string uri) =>
    method MatchPlaylist (line 35) | public MatchCertainty MatchPlaylist(ResolveContext _, string uri) =>
    method GetResource (line 40) | public Task<PlayResource> GetResource(ResolveContext ctx, string uri)
    method GetResourceById (line 45) | public async Task<PlayResource> GetResourceById(ResolveContext ctx, Au...
    method RestoreLink (line 65) | public string RestoreLink(ResolveContext _, AudioResource resource) =>...
    method ValidateFromString (line 67) | private Task<ResData> ValidateFromString(ConfBot config, string uriStr)
    method ValidateUri (line 73) | private Task<ResData> ValidateUri(Uri uri)
    method GetStreamHeaderData (line 83) | private static HeaderData GetStreamHeaderData(Stream stream)
    method ValidateWeb (line 90) | private static async Task<ResData> ValidateWeb(Uri link)
    method ValidateFile (line 121) | private ResData ValidateFile(Uri foundPath)
    method GetUri (line 140) | private Uri GetUri(ConfBot conf, string uri)
    method TryInPath (line 161) | private static Uri? TryInPath(string pathPrefix, string file)
    method GetPlaylist (line 178) | public async Task<Playlist> GetPlaylist(ResolveContext ctx, string url)
    method GetPlaylistContentAsync (line 236) | private Task<Playlist> GetPlaylistContentAsync(Stream stream, string u...
    method GetPlaylistContent (line 239) | private Playlist GetPlaylistContent(Stream stream, string url, string?...
    method GetThumbnail (line 318) | public async Task GetThumbnail(ResolveContext _, PlayResource playReso...
    method Dispose (line 345) | public void Dispose() { }
  class ResData (line 348) | internal class ResData
    method ResData (line 356) | public ResData(string fullUri, string? title)
  class MediaExt (line 364) | internal static class MediaExt
    method IsWeb (line 366) | public static bool IsWeb(this Uri uri)
    method IsFile (line 370) | public static bool IsFile(this Uri uri)
  class MediaPlayResource (line 374) | public class MediaPlayResource : PlayResource
    method MediaPlayResource (line 379) | public MediaPlayResource(string uri, AudioResource baseData, byte[]? i...

FILE: TS3AudioBot/ResourceFactories/PlayResource.cs
  class PlayResource (line 15) | public class PlayResource : IAudioResourceResult, IMetaContainer
    method PlayResource (line 22) | public PlayResource(string uri, AudioResource baseData, PlayInfo? play...
    method ToString (line 30) | public override string ToString() => AudioResource.ToString();

FILE: TS3AudioBot/ResourceFactories/ResolveContext.cs
  class ResolveContext (line 19) | public class ResolveContext
    method ResolveContext (line 24) | public ResolveContext(ResourceResolver resolver, ConfBot config)
    method Load (line 30) | public Task<PlayResource> Load(AudioResource resource) => Resolver.Loa...
    method Load (line 31) | public Task<PlayResource> Load(string message, string? audioType = nul...
    method LoadPlaylistFrom (line 32) | public Task<Playlist> LoadPlaylistFrom(string message, string? audioTy...
    method RestoreLink (line 33) | public string? RestoreLink(AudioResource res) => Resolver.RestoreLink(...
    method GetThumbnail (line 34) | public Task GetThumbnail(PlayResource playResource, Func<Stream, Task>...
    method Search (line 35) | public Task<IList<AudioResource>> Search(string resolverName, string q...

FILE: TS3AudioBot/ResourceFactories/ResourceResolver.cs
  class ResourceResolver (line 25) | public sealed class ResourceResolver : IDisposable
    method ResourceResolver (line 34) | public ResourceResolver(ConfFactories conf)
    method GetResolverByType (line 43) | private T? GetResolverByType<T>(string audioType) where T : class, IRe...
    method GetResResolverByLink (line 49) | private IEnumerable<(IResourceResolver, MatchCertainty)> GetResResolve...
    method GetListResolverByLink (line 56) | private IEnumerable<(IPlaylistResolver, MatchCertainty)> GetListResolv...
    method FilterUsable (line 63) | private static IEnumerable<T> FilterUsable<T>(IEnumerable<(T, MatchCer...
    method Load (line 83) | public async Task<PlayResource> Load(ResolveContext ctx, AudioResource...
    method Load (line 118) | public async Task<PlayResource> Load(ResolveContext ctx, string messag...
    method LoadPlaylistFrom (line 155) | public async Task<Playlist> LoadPlaylistFrom(ResolveContext ctx, strin...
    method RestoreLink (line 190) | public string? RestoreLink(ResolveContext ctx, AudioResource res)
    method GetThumbnail (line 209) | public async Task GetThumbnail(ResolveContext ctx, PlayResource playRe...
    method Search (line 220) | public async Task<IList<AudioResource>> Search(ResolveContext ctx, str...
    method AddResolver (line 228) | public void AddResolver(IResolver resolver)
    method RemoveResolver (line 251) | public void RemoveResolver(IResolver Resolver)
    method CouldNotLoad (line 264) | private static AudioBotException CouldNotLoad(string? reason = null)
    method ToErrorString (line 273) | private static AudioBotException ToErrorString(List<(string rsv, Audio...
    method Dispose (line 282) | public void Dispose()

FILE: TS3AudioBot/ResourceFactories/SongInfo.cs
  class SongInfo (line 5) | public class SongInfo

FILE: TS3AudioBot/ResourceFactories/SoundcloudResolver.cs
  class SoundcloudResolver (line 22) | public sealed class SoundcloudResolver : IResourceResolver, IPlaylistRes...
    method MatchResource (line 33) | public MatchCertainty MatchResource(ResolveContext? _, string uri) => ...
    method MatchPlaylist (line 35) | public MatchCertainty MatchPlaylist(ResolveContext? _, string uri) => ...
    method GetResource (line 37) | public async Task<PlayResource> GetResource(ResolveContext? _, string ...
    method GetResourceById (line 60) | public Task<PlayResource> GetResourceById(ResolveContext _, AudioResou...
    method GetResourceById (line 62) | private async Task<PlayResource> GetResourceById(AudioResource resourc...
    method RestoreLink (line 79) | public string RestoreLink(ResolveContext? _, AudioResource resource)
    method CheckAndGet (line 90) | private AudioResource? CheckAndGet(JsonTrackInfo track)
    method YoutubeDlWrappedAsync (line 107) | private async Task<PlayResource> YoutubeDlWrappedAsync(string link)
    method GetPlaylist (line 124) | public async Task<Playlist> GetPlaylist(ResolveContext _, string url)
    method GetThumbnail (line 151) | public async Task GetThumbnail(ResolveContext _, PlayResource playReso...
    method Dispose (line 168) | public void Dispose() { }
    class JsonTrackInfo (line 172) | private class JsonTrackInfo
    class JsonTrackUser (line 179) | private class JsonTrackUser
    class JsonPlaylist (line 183) | private class JsonPlaylist
    class JsonTumbnailMinimal (line 188) | private class JsonTumbnailMinimal

FILE: TS3AudioBot/ResourceFactories/TwitchResolver.cs
  class TwitchResolver (line 20) | public sealed class TwitchResolver : IResourceResolver
    method MatchResource (line 30) | public MatchCertainty MatchResource(ResolveContext _, string uri) => T...
    method GetResource (line 32) | public async Task<PlayResource> GetResource(ResolveContext _, string uri)
    method GetResourceById (line 40) | public async Task<PlayResource> GetResourceById(ResolveContext? _, Aud...
    method SelectStream (line 134) | private static int SelectStream(List<StreamData> list) => list.FindInd...
    method RestoreLink (line 136) | public string RestoreLink(ResolveContext _, AudioResource resource) =>...
    method Dispose (line 138) | public void Dispose() { }
    class JsonAccessToken (line 141) | private class JsonAccessToken
  class StreamData (line 150) | public sealed class StreamData
  type StreamQuality (line 158) | public enum StreamQuality

FILE: TS3AudioBot/ResourceFactories/Youtube/Json.cs
  class JsonVideoListResponse (line 5) | public class JsonVideoListResponse // # youtube#videoListResponse
  class JsonVideo (line 10) | public class JsonVideo // youtube#video
  class JsonSearchListResponse (line 15) | public class JsonSearchListResponse // youtube#searchListResponse
  class JsonSearchResult (line 19) | public class JsonSearchResult // youtube#searchResult
  class JsonContentDetails (line 24) | public class JsonContentDetails
  class JsonSnippet (line 28) | public class JsonSnippet
  class JsonThumbnailList (line 33) | public class JsonThumbnailList
  class JsonThumbnail (line 41) | public class JsonThumbnail
  class JsonPlayerResponse (line 48) | public class JsonPlayerResponse
  class JsonStreamingData (line 53) | public class JsonStreamingData
  class JsonVideoDetails (line 58) | public class JsonVideoDetails
  class JsonPlayFormat (line 65) | public class JsonPlayFormat

FILE: TS3AudioBot/ResourceFactories/Youtube/LoaderPriority.cs
  type LoaderPriority (line 12) | public enum LoaderPriority

FILE: TS3AudioBot/ResourceFactories/Youtube/VideoCodec.cs
  type VideoCodec (line 12) | public enum VideoCodec

FILE: TS3AudioBot/ResourceFactories/Youtube/VideoData.cs
  class VideoData (line 12) | public sealed class VideoData
    method VideoData (line 14) | public VideoData(string link, string qualitydesciption, VideoCodec cod...
    method ToString (line 29) | public override string ToString() => $"{Qualitydesciption} @ {Codec} -...

FILE: TS3AudioBot/ResourceFactories/Youtube/YoutubeResolver.cs
  class YoutubeResolver (line 27) | public sealed class YoutubeResolver : IResourceResolver, IPlaylistResolv...
    method YoutubeResolver (line 39) | public YoutubeResolver(ConfResolverYoutube conf)
    method MatchResource (line 46) | public MatchCertainty MatchResource(ResolveContext? _, string uri) =>
    method MatchPlaylist (line 51) | public MatchCertainty MatchPlaylist(ResolveContext? _, string uri) => ...
    method GetResource (line 53) | public async Task<PlayResource> GetResource(ResolveContext? _, string ...
    method GetResourceById (line 69) | public async Task<PlayResource> GetResourceById(ResolveContext? _, Aud...
    method ResolveResourceInternal (line 86) | private async Task<PlayResource> ResolveResourceInternal(AudioResource...
    method ParseLiveData (line 138) | private static async Task<PlayResource> ParseLiveData(AudioResource re...
    method ParsePlayerData (line 166) | private static void ParsePlayerData(JsonPlayerResponse data, List<Vide...
    method ParseEncodedFmt (line 171) | private static void ParseEncodedFmt(List<string> videoDataUnsplit, Lis...
    method ParseAdaptiveFmt (line 195) | private static void ParseAdaptiveFmt(List<string> videoDataUnsplit, Li...
    method RestoreLink (line 224) | public string RestoreLink(ResolveContext _, AudioResource resource) =>...
    method SelectStream (line 226) | private static int SelectStream(List<VideoData> list)
    method ValidateMedia (line 245) | private static Task ValidateMedia(VideoData media) => WebWrapper.Reque...
    method GetCodec (line 247) | private static VideoCodec GetCodec(string type)
    method GetPlaylist (line 283) | public async Task<Playlist> GetPlaylist(ResolveContext _, string url)
    method GetPlaylistYoutubeApi (line 296) | private async Task<Playlist> GetPlaylistYoutubeApi(string id)
    method GetPlaylistYoutubeDl (line 330) | private async Task<Playlist> GetPlaylistYoutubeDl(string id, string url)
    method YoutubeDlWrapped (line 347) | private static async Task<PlayResource> YoutubeDlWrapped(AudioResource...
    method ParseQueryString (line 364) | public static Dictionary<string, List<string>> ParseQueryString(string...
    method GetThumbnail (line 381) | public Task GetThumbnail(ResolveContext _, PlayResource playResource, ...
    method Search (line 393) | public async Task<IList<AudioResource>> Search(ResolveContext _, strin...
    method SearchYoutubeApi (line 401) | public async Task<IList<AudioResource>> SearchYoutubeApi(string keyword)
    method SearchYoutubeDlAsync (line 420) | public async Task<IList<AudioResource>> SearchYoutubeDlAsync(string ke...
    method Dispose (line 433) | public void Dispose() { }

FILE: TS3AudioBot/ResourceFactories/YoutubeDlHelper.cs
  class YoutubeDlHelper (line 25) | public static class YoutubeDlHelper
    method GetSingleVideo (line 35) | public static async Task<JsonYtdlDump> GetSingleVideo(string id)
    method GetPlaylistAsync (line 45) | public static async Task<JsonYtdlPlaylistDump> GetPlaylistAsync(string...
    method GetSearchAsync (line 55) | public static async Task<JsonYtdlPlaylistDump> GetSearchAsync(string t...
    method FindYoutubeDl (line 65) | public static (string ytdlpath, string param)? FindYoutubeDl()
    method RunYoutubeDl (line 108) | public static async Task<T> RunYoutubeDl<T>(string path, string args) ...
    method ParseResponse (line 169) | public static T ParseResponse<T>(string? json) where T : notnull
    method FilterBest (line 186) | public static JsonYtdlFormat? FilterBest(IEnumerable<JsonYtdlFormat>? ...
    method MapToSongInfo (line 210) | public static SongInfo MapToSongInfo(JsonYtdlDump dump)
    method WaitForExitAsync (line 230) | public static async Task WaitForExitAsync(this Process process, TimeSp...
  class JsonYtdlBase (line 264) | public abstract class JsonYtdlBase
  class JsonYtdlDump (line 270) | public class JsonYtdlDump : JsonYtdlBase
  class JsonYtdlFormat (line 284) | public class JsonYtdlFormat
  class JsonYtdlPlaylistDump (line 301) | public class JsonYtdlPlaylistDump : JsonYtdlBase
  class JsonYtdlPlaylistEntry (line 308) | public class JsonYtdlPlaylistEntry

FILE: TS3AudioBot/Rights/CreateFileSettings.cs
  class CreateFileSettings (line 12) | public class CreateFileSettings

FILE: TS3AudioBot/Rights/ExecuteContext.cs
  class ExecuteContext (line 18) | internal class ExecuteContext

FILE: TS3AudioBot/Rights/Matchers/MatchApiCallerIp.cs
  class MatchApiCallerIp (line 15) | internal class MatchApiCallerIp : Matcher
    method MatchApiCallerIp (line 19) | public MatchApiCallerIp(IEnumerable<IPAddress> requestIps) => this.req...
    method Matches (line 21) | public override bool Matches(ExecuteContext ctx) => ctx.ApiCallerIp !=...

FILE: TS3AudioBot/Rights/Matchers/MatchBot.cs
  class MatchBot (line 14) | internal class MatchBot : Matcher
    method MatchBot (line 18) | public MatchBot(IEnumerable<string> bots) => this.bots = new HashSet<s...
    method Matches (line 20) | public override bool Matches(ExecuteContext ctx) => ctx.Bot != null &&...

FILE: TS3AudioBot/Rights/Matchers/MatchChannelGroupId.cs
  class MatchChannelGroupId (line 15) | internal class MatchChannelGroupId : Matcher
    method MatchChannelGroupId (line 19) | public MatchChannelGroupId(IEnumerable<ChannelGroupId> channelGroupIds...
    method Matches (line 21) | public override bool Matches(ExecuteContext ctx) => ctx.ChannelGroupId...

FILE: TS3AudioBot/Rights/Matchers/MatchClientGroupId.cs
  class MatchServerGroupId (line 15) | internal class MatchServerGroupId : Matcher
    method MatchServerGroupId (line 19) | public MatchServerGroupId(IEnumerable<ServerGroupId> serverGroupIds) =...
    method Matches (line 21) | public override bool Matches(ExecuteContext ctx) => ctx.ServerGroups?....

FILE: TS3AudioBot/Rights/Matchers/MatchClientUid.cs
  class MatchClientUid (line 15) | internal class MatchClientUid : Matcher
    method MatchClientUid (line 19) | public MatchClientUid(IEnumerable<Uid> clientUids) => this.clientUids ...
    method Matches (line 21) | public override bool Matches(ExecuteContext ctx) => ctx.ClientUid != n...

FILE: TS3AudioBot/Rights/Matchers/MatchHost.cs
  class MatchHost (line 14) | internal class MatchHost : Matcher
    method MatchHost (line 18) | public MatchHost(IEnumerable<string> hosts) => this.hosts = new HashSe...
    method Matches (line 20) | public override bool Matches(ExecuteContext ctx) => ctx.Host != null &...

FILE: TS3AudioBot/Rights/Matchers/MatchIsApi.cs
  class MatchIsApi (line 12) | internal class MatchIsApi : Matcher
    method MatchIsApi (line 16) | public MatchIsApi(bool isApi) => this.isApi = isApi;
    method Matches (line 18) | public override bool Matches(ExecuteContext ctx) => ctx.IsApi == isApi;

FILE: TS3AudioBot/Rights/Matchers/MatchPermission.cs
  class MatchPermission (line 19) | internal class MatchPermission : Matcher
    method MatchPermission (line 24) | public MatchPermission(string[] permissions, ParseContext ctx)
    method ComparingPermissions (line 76) | public IReadOnlyCollection<TsPermission> ComparingPermissions() => per...
    method Matches (line 78) | public override bool Matches(ExecuteContext ctx)

FILE: TS3AudioBot/Rights/Matchers/MatchToken.cs
  class MatchToken (line 14) | internal class MatchToken : Matcher
    method MatchToken (line 18) | public MatchToken(IEnumerable<string> tokens) => this.tokens = new Has...
    method Matches (line 20) | public override bool Matches(ExecuteContext ctx) => ctx.ApiToken != nu...

FILE: TS3AudioBot/Rights/Matchers/MatchVisibility.cs
  class MatchVisibility (line 15) | internal class MatchVisibility : Matcher
    method MatchVisibility (line 19) | public MatchVisibility(TextMessageTargetMode[] visibility) => this.vis...
    method Matches (line 21) | public override bool Matches(ExecuteContext ctx) => ctx.Visibiliy != n...

FILE: TS3AudioBot/Rights/Matchers/Matcher.cs
  class Matcher (line 12) | internal abstract class Matcher
    method Matches (line 14) | public abstract bool Matches(ExecuteContext ctx);

FILE: TS3AudioBot/Rights/Matchers/PermCompare.cs
  type PermCompare (line 12) | internal enum PermCompare

FILE: TS3AudioBot/Rights/ParseContext.cs
  class ParseContext (line 18) | internal class ParseContext
    method ParseContext (line 32) | public ParseContext(ISet<string> registeredRights)
    method SplitDeclarations (line 43) | public void SplitDeclarations()
    method AsResult (line 49) | public (bool hasErrors, string info) AsResult()

FILE: TS3AudioBot/Rights/RightsDecl.cs
  class RightsDecl (line 18) | internal abstract class RightsDecl
    method ParseKey (line 30) | public virtual bool ParseKey(string key, TomlObject tomlObj, ParseCont...
    method ParseChilden (line 63) | public bool ParseChilden(TomlTable tomlObj, ParseContext ctx)
    method ResolveGroup (line 80) | public abstract RightsGroup? ResolveGroup(string groupName, ParseConte...
    method ResolveIncludes (line 86) | public bool ResolveIncludes(ParseContext ctx)
    method MergeGroups (line 105) | public void MergeGroups(IEnumerable<RightsDecl> merge)
    method MergeGroups (line 113) | public void MergeGroups(RightsDecl include)

FILE: TS3AudioBot/Rights/RightsGroup.cs
  class RightsGroup (line 12) | internal class RightsGroup : RightsDecl
    method RightsGroup (line 16) | public RightsGroup(string name)
    method ResolveGroup (line 21) | public override RightsGroup? ResolveGroup(string groupName, ParseConte...

FILE: TS3AudioBot/Rights/RightsManager.cs
  class RightsManager (line 30) | public class RightsManager
    method RightsManager (line 49) | public RightsManager(ConfRights config)
    method SetRightsList (line 55) | public void SetRightsList(IEnumerable<string> rights)
    method HasAllRights (line 66) | public async ValueTask<bool> HasAllRights(ExecutionInformation info, p...
    method GetRightsSubset (line 73) | public async ValueTask<string[]> GetRightsSubset(ExecutionInformation ...
    method GetRightsContext (line 80) | private async ValueTask<ExecuteContext> GetRightsContext(ExecutionInfo...
    method TryGetRootSafe (line 198) | private RightsRule? TryGetRootSafe()
    method ProcessNode (line 214) | private static bool ProcessNode(RightsRule rule, ExecuteContext ctx)
    method Reload (line 230) | public bool Reload()
    method ReadFile (line 238) | private RightsRule? ReadFile()
    method CreateDefaultConfigIfNotExists (line 268) | public void CreateDefaultConfigIfNotExists()
    method CreateConfig (line 273) | public void CreateConfig(CreateFileSettings settings)
    method CreateConfigIfNotExists (line 299) | public void CreateConfigIfNotExists(bool interactive = false)
    method RecalculateRights (line 335) | private static void RecalculateRights(TomlTable table, ParseContext pa...
    method ExpandRights (line 364) | private static HashSet<string> ExpandRights(IEnumerable<string> rights...
    method NormalizeRules (line 401) | private static void NormalizeRules(ParseContext ctx)
    method ValidateUniqueGroupNames (line 424) | private static bool ValidateUniqueGroupNames(ParseContext ctx)
    method ResolveIncludes (line 454) | private static bool ResolveIncludes(ParseContext ctx)
    method CheckCyclicGroupDependencies (line 468) | private static bool CheckCyclicGroupDependencies(ParseContext ctx)
    method BuildLevel (line 506) | private static void BuildLevel(RightsDecl root, int level = 0)
    method LintDeclarations (line 523) | private static void LintDeclarations(ParseContext ctx)
    method FlattenGroups (line 571) | private static void FlattenGroups(ParseContext ctx)
    method FlattenRules (line 598) | private static void FlattenRules(RightsRule root)
    method CheckRequiredCalls (line 614) | private static void CheckRequiredCalls(ParseContext ctx)

FILE: TS3AudioBot/Rights/RightsRule.cs
  class RightsRule (line 29) | internal class RightsRule : RightsDecl
    method RightsRule (line 37) | public RightsRule()
    method HasMatcher (line 43) | public bool HasMatcher() => Matcher.Count > 0;
    method Matches (line 45) | public bool Matches(ExecuteContext ctx)
    method ParseKey (line 59) | public override bool ParseKey(string key, TomlObject tomlObj, ParseCon...
    method ResolveGroup (line 155) | public override RightsGroup? ResolveGroup(string groupName, ParseConte...
    method ToString (line 165) | public override string ToString()

FILE: TS3AudioBot/Sessions/AnonymousSession.cs
  class AnonymousSession (line 12) | internal class AnonymousSession : UserSession
    method AnonymousSession (line 14) | public AnonymousSession()

FILE: TS3AudioBot/Sessions/ApiToken.cs
  class ApiToken (line 15) | internal class ApiToken
    method ApiToken (line 24) | public ApiToken(string value, DateTime timeout)

FILE: TS3AudioBot/Sessions/SessionManager.cs
  class SessionManager (line 16) | public class SessionManager
    method GetOrCreateSession (line 23) | public UserSession GetOrCreateSession(ClientId clientId)
    method GetSession (line 37) | public UserSession? GetSession(ClientId id)
    method RemoveSession (line 48) | public void RemoveSession(ClientId id)

FILE: TS3AudioBot/Sessions/TokenManager.cs
  class TokenManager (line 18) | public class TokenManager
    method TokenManager (line 26) | public TokenManager(DbStore database)
    method GenerateToken (line 33) | public string GenerateToken(string authId, TimeSpan? timeout = null)
    method AddTimeSpanSafe (line 54) | private static DateTime AddTimeSpanSafe(DateTime dateTime, TimeSpan ad...
    method GetToken (line 73) | internal ApiToken? GetToken(string authId)
    class DbApiToken (line 95) | private class DbApiToken

FILE: TS3AudioBot/Sessions/UserSession.cs
  class UserSession (line 17) | public class UserSession
    method UserSession (line 26) | public UserSession() { }
    method SetResponseInstance (line 28) | public void SetResponseInstance(Response responseProcessor) => Set(Res...
    method ClearResponse (line 30) | public void ClearResponse() => Set<Response?>(ResponseKey, null);
    method Get (line 32) | public bool Get<TData>(string key, [MaybeNullWhen(false)] out TData va...
    method Set (line 49) | public void Set<TData>(string key, TData data)
  class UserSessionExtensions (line 61) | public static class UserSessionExtensions
    method SetResponse (line 63) | public static void SetResponse(this UserSession? session, Response res...

FILE: TS3AudioBot/Setup.cs
  class Setup (line 26) | internal static class Setup
    method Main (line 34) | public static int Main(string[] args)
    method StartBot (line 87) | private static async void StartBot(ParameterData setup)
    method SetupLog (line 95) | public static void SetupLog()
    method SetupLibopus (line 115) | public static bool SetupLibopus()
    method EnableLlgc (line 123) | public static void EnableLlgc()
    method LogHeader (line 128) | public static void LogHeader()
  class ParameterData (line 143) | public class ParameterData

FILE: TS3AudioBot/Ts3Client.cs
  class Ts3Client (line 29) | public sealed class Ts3Client
    method Ts3Client (line 70) | public Ts3Client(ConfBot config, TsFullClient ts3FullClient, Id id)
    method Connect (line 100) | public E<string> Connect()
    method ConnectClient (line 139) | private async Task ConnectClient()
    method Disconnect (line 190) | public async Task Disconnect()
    method UpdateIndentityToSecurityLevel (line 197) | private void UpdateIndentityToSecurityLevel(int targetLevel)
    method SendMessage (line 210) | public Task SendMessage(string message, ClientId clientId) => ts3FullC...
    method SendChannelMessage (line 211) | public Task SendChannelMessage(string message) => ts3FullClient.SendCh...
    method SendServerMessage (line 212) | public Task SendServerMessage(string message) => ts3FullClient.SendSer...
    method KickClientFromServer (line 214) | public Task KickClientFromServer(params ClientId[] clientId) => ts3Ful...
    method KickClientFromChannel (line 215) | public Task KickClientFromChannel(params ClientId[] clientId) => ts3Fu...
    method ChangeDescription (line 217) | public Task ChangeDescription(string description)
    method ChangeBadges (line 220) | public Task ChangeBadges(string badgesString)
    method ChangeName (line 227) | public Task ChangeName(string name)
    method GetCachedClientById (line 232) | public Task<ClientList> GetCachedClientById(ClientId id) => ClientBuff...
    method GetFallbackedClientById (line 234) | public async Task<ClientList> GetFallbackedClientById(ClientId id)
    method GetClientByName (line 247) | public async Task<ClientList> GetClientByName(string name)
    method ClientBufferRequest (line 257) | private async Task<ClientList> ClientBufferRequest(Predicate<ClientLis...
    method RefreshClientBuffer (line 266) | public async ValueTask RefreshClientBuffer(bool force)
    method GetClientServerGroups (line 281) | public async Task<ServerGroupId[]> GetClientServerGroups(ClientDbId dbId)
    method GetDbClientByDbId (line 287) | public async Task<ClientDbInfo> GetDbClientByDbId(ClientDbId clientDbId)
    method GetClientInfoById (line 297) | public Task<ClientInfo> GetClientInfoById(ClientId id) => ts3FullClien...
    method GetClientDbIdByUid (line 299) | public async Task<ClientDbId> GetClientDbIdByUid(Uid uid)
    method SetupRights (line 310) | public async Task SetupRights(string? key)
    method UploadAvatar (line 454) | public Task UploadAvatar(System.IO.Stream stream)
    method DeleteAvatar (line 459) | public Task DeleteAvatar() => ts3FullClient.DeleteAvatar().UnwrapThrow();
    method MoveTo (line 461) | public Task MoveTo(ChannelId channelId, string? password = null)
    method SetChannelCommander (line 464) | public Task SetChannelCommander(bool isCommander)
    method IsChannelCommander (line 467) | public async Task<bool> IsChannelCommander()
    method InvalidateClientBuffer (line 470) | public void InvalidateClientBuffer() => clientbufferOutdated = true;
    method ClearAllCaches (line 472) | private void ClearAllCaches()
    method TsFullClient_OnErrorEvent (line 485) | private void TsFullClient_OnErrorEvent(object? sender, CommandError er...
    method TsFullClient_OnDisconnected (line 499) | private async void TsFullClient_OnDisconnected(object? sender, Disconn...
    method TryReconnect (line 561) | private async Task<bool> TryReconnect(ReconnectType type)
    method ExtendedTextMessage (line 604) | private async void ExtendedTextMessage(object? sender, TextMessage tex...
    method UpdateReconnectChannel (line 612) | private void UpdateReconnectChannel(ClientId clientId, ChannelId chann...
    method AloneRecheckRequired (line 618) | private bool AloneRecheckRequired(ClientId clientId, ChannelId channelId)
    method IsAloneRecheck (line 621) | private async ValueTask IsAloneRecheck()
    type ReconnectType (line 638) | private enum ReconnectType
  class AloneChanged (line 649) | public class AloneChanged : EventArgs
    method AloneChanged (line 653) | public AloneChanged(bool alone)
  class CommandErrorExtentions (line 659) | internal static class CommandErrorExtentions
    method UnwrapThrow (line 661) | public static async Task<T> UnwrapThrow<T>(this Task<R<T, CommandError...
    method UnwrapThrow (line 670) | public static async Task UnwrapThrow(this Task<E<CommandError>> task, ...
    method FormatLocal (line 677) | public static async Task<R<T, LocalStr>> FormatLocal<T>(this Task<R<T,...
    method FormatLocal (line 680) | public static R<T, LocalStr> FormatLocal<T>(this R<T, CommandError> cm...
    method FormatLocal (line 687) | public static async CmdE FormatLocal(this Task<E<CommandError>> task, ...
    method FormatLocal (line 690) | public static E<LocalStr> FormatLocal(this E<CommandError> cmdErr, Fun...
    method FormatLocal (line 697) | public static LocalStr FormatLocal(this CommandError err, Func<TsError...

FILE: TS3AudioBot/Upgrader.cs
  class Upgrader (line 14) | internal static class Upgrader
    method PerformUpgrades (line 20) | public static void PerformUpgrades(CoreInjector injector)

FILE: TS3AudioBot/Web/Api/ApiCall.cs
  class ApiCall (line 16) | public class ApiCall : InvokerData
    method CreateAnonymous (line 23) | public static ApiCall CreateAnonymous() => new ApiCall(Uid.Anonymous);
    method ApiCall (line 25) | public ApiCall(Uid clientUid, IPAddress? ipAddress = null, Uri? reques...

FILE: TS3AudioBot/Web/Api/DataStream.cs
  class DataStream (line 16) | public class DataStream
    method DataStream (line 20) | public DataStream(Func<HttpResponse, Task> writeFunc)
    method WriteOut (line 25) | public Task WriteOut(HttpResponse response) => writeFunc(response);
    method ToString (line 27) | public override string? ToString() => null;

FILE: TS3AudioBot/Web/Api/JsonArray.cs
  class JsonArray (line 15) | public class JsonArray<T> : JsonValue<IList<T>>
    method JsonArray (line 17) | public JsonArray(IList<T> value, string msg) : base(value, msg) { }
    method JsonArray (line 18) | public JsonArray(IList<T> value, Func<IList<T>, string>? asString = null)

FILE: TS3AudioBot/Web/Api/JsonEmpty.cs
  class JsonEmpty (line 12) | public class JsonEmpty : JsonObject
    method JsonEmpty (line 15) | public JsonEmpty(string msg) { message = msg; }
    method GetSerializeObject (line 17) | public override object GetSerializeObject() => string.Empty;
    method Serialize (line 18) | public override string Serialize() => string.Empty;
    method ToString (line 19) | public override string ToString() => message;

FILE: TS3AudioBot/Web/Api/JsonError.cs
  class JsonError (line 15) | public class JsonError : JsonObject
    method JsonError (line 29) | public JsonError(string msg, CommandExceptionReason reason)
    method Serialize (line 35) | public override string Serialize() => JsonConvert.SerializeObject(GetS...
    method ToString (line 36) | public override string ToString() => ErrorMessage;

FILE: TS3AudioBot/Web/Api/JsonObject.cs
  class JsonObject (line 16) | public abstract class JsonObject : IWrappedResult
    method JsonObject (line 20) | static JsonObject()
    method JsonObject (line 26) | protected JsonObject() { }
    method GetSerializeObject (line 29) | public virtual object GetSerializeObject() => this;
    method Serialize (line 30) | public virtual string Serialize() => JsonConvert.SerializeObject(GetSe...
    method ToString (line 31) | public override abstract string ToString();

FILE: TS3AudioBot/Web/Api/JsonValue.cs
  class JsonValue (line 16) | public class JsonValue<T> : JsonValue where T : notnull
    method JsonValue (line 22) | public JsonValue(T value) : base(value) { }
    method JsonValue (line 23) | public JsonValue(T value, string msg) : base(value, msg) { }
    method JsonValue (line 24) | public JsonValue(T value, Func<T, string>? asString) : base(value)
    method ToString (line 29) | public override string ToString()
    method JsonValue (line 47) | protected JsonValue(object value) { Value = value; AsStringResult = nu...
    method JsonValue (line 48) | protected JsonValue(object value, string msg) { Value = value; AsStrin...
    method GetSerializeObject (line 50) | public override object GetSerializeObject() => Value;
    method Serialize (line 52) | public override string Serialize()
    method ToString (line 60) | public override string ToString()
    method Create (line 69) | public static JsonValue<T> Create<T>(T anon) where T : notnull => new ...
    method Create (line 70) | public static JsonValue<T> Create<T>(T anon, string msg) where T : not...
    method Create (line 71) | public static JsonValue<T> Create<T>(T anon, Func<T, string>? asString...
  class JsonValue (line 42) | public abstract class JsonValue : JsonObject
    method JsonValue (line 22) | public JsonValue(T value) : base(value) { }
    method JsonValue (line 23) | public JsonValue(T value, string msg) : base(value, msg) { }
    method JsonValue (line 24) | public JsonValue(T value, Func<T, string>? asString) : base(value)
    method ToString (line 29) | public override string ToString()
    method JsonValue (line 47) | protected JsonValue(object value) { Value = value; AsStringResult = nu...
    method JsonValue (line 48) | protected JsonValue(object value, string msg) { Value = value; AsStrin...
    method GetSerializeObject (line 50) | public override object GetSerializeObject() => Value;
    method Serialize (line 52) | public override string Serialize()
    method ToString (line 60) | public override string ToString()
    method Create (line 69) | public static JsonValue<T> Create<T>(T anon) where T : notnull => new ...
    method Create (line 70) | public static JsonValue<T> Create<T>(T anon, string msg) where T : not...
    method Create (line 71) | public static JsonValue<T> Create<T>(T anon, Func<T, string>? asString...

FILE: TS3AudioBot/Web/Api/OpenApiGenerator.cs
  class OpenApiGenerator (line 21) | public static class OpenApiGenerator
    method OpenApiGenerator (line 25) | static OpenApiGenerator()
    method Generate (line 30) | public static JObject Generate(CommandManager commandManager, BotInfo[...
    method GenerateCommand (line 91) | private static JToken? GenerateCommand(CommandManager commandManager, ...
    method Chain (line 197) | private static T Chain<T>(this T token, Action<T> func) where T : JToken
    method JPropObj (line 203) | private static JProperty JPropObj(string name, params object[] token)
    method NormalToSchema (line 208) | private static OApiSchema? NormalToSchema(Type type)
    class OApiSchema (line 243) | private class OApiSchema
      method OApiSchema (line 254) | public OApiSchema(string type)
      method FromBasic (line 259) | public static OApiSchema FromBasic(string type, string? format = nul...
      method ObjWrap (line 261) | public OApiSchema ObjWrap() => new OApiSchema("object") { Additional...

FILE: TS3AudioBot/Web/Api/TimeSpanConverter.cs
  class TimeSpanConverter (line 15) | internal class TimeSpanConverter : JsonConverter<TimeSpan>
    method WriteJson (line 17) | public override void WriteJson(JsonWriter writer, TimeSpan value, Json...
    method ReadJson (line 22) | public override TimeSpan ReadJson(JsonReader reader, Type objectType, ...

FILE: TS3AudioBot/Web/Api/WebApi.cs
  class WebApi (line 34) | public sealed class WebApi
    method WebApi (line 51) | public WebApi(ConfWebApi config, CoreInjector coreInjector, TokenManag...
    method ProcessApiV1Call (line 59) | public async Task ProcessApiV1Call(HttpContext context)
    method BuildCommand (line 146) | private ICommand BuildCommand(Uri requestUrl)
    method BuildContext (line 155) | private ExecutionInformation BuildContext(ApiCall apiCallData)
    method ProcessBodyData (line 170) | private async Task<E<Exception>> ProcessBodyData(HttpRequest request, ...
    method ReturnError (line 188) | private static async Task ReturnError(Exception ex, HttpResponse respo...
    method ReturnCommandError (line 227) | private static JsonError ReturnCommandError(CommandException ex, HttpR...
    method UnescapeAstTree (line 294) | private static void UnescapeAstTree(AstNode node)
    method Authenticate (line 313) | private R<ApiCall, string> Authenticate(HttpRequest request)

FILE: TS3AudioBot/Web/Model/CurrentSongInfo.cs
  class CurrentSongInfo (line 15) | public class CurrentSongInfo : PlaylistItemGetData

FILE: TS3AudioBot/Web/Model/PlaylistInfo.cs
  class PlaylistInfo (line 14) | public class PlaylistInfo
    method PlaylistInfo (line 34) | public PlaylistInfo(string id, string title)

FILE: TS3AudioBot/Web/Model/PlaylistItemGetData.cs
  class PlaylistItemGetData (line 12) | public class PlaylistItemGetData

FILE: TS3AudioBot/Web/Model/QueueInfo.cs
  class QueueInfo (line 14) | public class QueueInfo : PlaylistInfo
    method QueueInfo (line 19) | public QueueInfo(string id, string title) : base(id, title)

FILE: TS3AudioBot/Web/WebServer.cs
  class WebServer (line 25) | public sealed class WebServer : IDisposable
    method WebServer (line 34) | public WebServer(ConfWeb config, CoreInjector coreInjector)
    method StartWebServer (line 41) | public void StartWebServer()
    method FindWebFolder (line 63) | public string? FindWebFolder()
    method StartWebServerInternal (line 89) | private void StartWebServerInternal()
    method OnShutdown (line 182) | public void OnShutdown()
    method Dispose (line 187) | public void Dispose()

FILE: TSLib/Audio/AudioInterfaces.cs
  type IAudioStream (line 14) | public interface IAudioStream { }
  type IAudioPassiveProducer (line 17) | public interface IAudioPassiveProducer : IAudioStream, IDisposable
    method Read (line 19) | int Read(byte[] buffer, int offset, int length, out Meta? meta);
  type IAudioActiveProducer (line 22) | public interface IAudioActiveProducer : IAudioStream
  type IAudioPassiveConsumer (line 27) | public interface IAudioPassiveConsumer : IAudioStream
    method Write (line 30) | void Write(Span<byte> data, Meta? meta);
  type IAudioActiveConsumer (line 33) | public interface IAudioActiveConsumer : IAudioStream
  type IAudioPipe (line 41) | public interface IAudioPipe : IAudioPassiveConsumer, IAudioActiveProduce...
  type ISampleInfo (line 43) | public interface ISampleInfo
  class SampleInfo (line 50) | public sealed class SampleInfo : ISampleInfo
    method SampleInfo (line 56) | public SampleInfo(int sampleRate, int channels, int bitsPerSample)

FILE: TSLib/Audio/AudioMeta.cs
  class Meta (line 14) | public class Meta
  type MetaIn (line 22) | public struct MetaIn
  class MetaOut (line 28) | public class MetaOut
  type TargetSendMode (line 38) | public enum TargetSendMode
  type PipeControl (line 46) | public enum PipeControl

FILE: TSLib/Audio/AudioPacketReader.cs
  class AudioPacketReader (line 15) | public class AudioPacketReader : IAudioPipe
    method Write (line 20) | public void Write(Span<byte> data, Meta? meta)

FILE: TSLib/Audio/AudioPipeExtensions.cs
  class AudioPipeExtensions (line 14) | public static class AudioPipeExtensions
    method Chain (line 16) | public static T Chain<T>(this IAudioActiveProducer producer, T addCons...
    method Chain (line 36) | public static T Chain<T>(this IAudioActiveProducer producer, Action<T>...
    method Into (line 43) | public static T Into<T>(this IAudioPassiveProducer producer, T reader)...
    method Into (line 49) | public static T Into<T>(this IAudioPassiveProducer producer, Action<T>...

FILE: TSLib/Audio/AudioTools.cs
  class AudioTools (line 14) | public static class AudioTools
    method TryMonoToStereo (line 16) | public static bool TryMonoToStereo(byte[] pcm, ref int length)

FILE: TSLib/Audio/CheckActivePipe.cs
  class CheckActivePipe (line 14) | public class CheckActivePipe : IAudioPipe
    method Write (line 19) | public void Write(Span<byte> data, Meta? meta)

FILE: TSLib/Audio/ClientMixdown.cs
  class ClientMixdown (line 15) | public class ClientMixdown : PassiveMergePipe, IAudioPassiveConsumer
    method Write (line 23) | public void Write(Span<byte> data, Meta? meta)
    class ClientMix (line 57) | public class ClientMix : IAudioPassiveProducer
      method ClientMix (line 65) | public ClientMix(int bufferSize)
      method Write (line 70) | public void Write(Span<byte> data, Meta meta)
      method Read (line 81) | public int Read(byte[] buffer, int offset, int length, out Meta? meta)
      method Dispose (line 96) | public void Dispose() { }

FILE: TSLib/Audio/DecoderPipe.cs
  class DecoderPipe (line 16) | public class DecoderPipe : IAudioPipe, IDisposable, ISampleInfo
    method DecoderPipe (line 33) | public DecoderPipe()
    method Write (line 38) | public void Write(Span<byte> data, Meta? meta)
    method GetDecoder (line 70) | private OpusDecoder GetDecoder(ClientId sender, Codec codec)
    method CreateDecoder (line 85) | private OpusDecoder CreateDecoder(Codec codec)
    method Dispose (line 95) | public void Dispose()

FILE: TSLib/Audio/EncoderPipe.cs
  class EncoderPipe (line 15) | public class EncoderPipe : IAudioPipe, IDisposable, ISampleInfo
    method EncoderPipe (line 39) | public EncoderPipe(Codec codec)
    method Write (line 78) | public void Write(Span<byte> data, Meta? meta)
    method GetPlayLength (line 109) | public TimeSpan GetPlayLength(int bytes)
    method Dispose (line 114) | public void Dispose()

FILE: TSLib/Audio/Opus/NativeMethods.cs
  class NativeMethods (line 31) | public static class NativeMethods
    method NativeMethods (line 36) | static NativeMethods()
    method PreloadLibrary (line 41) | public static bool PreloadLibrary()
    method opus_encoder_create (line 63) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_encoder_destroy (line 66) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_encode (line 69) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_decoder_create (line 72) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_decoder_destroy (line 75) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_decode (line 78) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_encoder_ctl (line 81) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_encoder_ctl (line 84) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
    method opus_get_version_string (line 87) | [DllImport("libopus", CallingConvention = CallingConvention.Cdecl)]
  type Ctl (line 92) | public enum Ctl : int
  type Application (line 103) | public enum Application : int
  type Errors (line 119) | public enum Errors : int

FILE: TSLib/Audio/Opus/OpusDecoder.cs
  class OpusDecoder (line 30) | public sealed class OpusDecoder : IDisposable
    method Create (line 38) | public static OpusDecoder Create(int outputSampleRate, int outputChann...
    method OpusDecoder (line 59) | private OpusDecoder(IntPtr decoder, int outputSamplingRate, int output...
    method Decode (line 72) | public Span<byte> Decode(Span<byte> inputOpusData, Span<byte> outputDe...
    method FrameCount (line 101) | public int FrameCount(int bufferSize)
    method Dispose (line 130) | public void Dispose()

FILE: TSLib/Audio/Opus/OpusEncoder.cs
  class OpusEncoder (line 30) | public sealed class OpusEncoder : IDisposable
    method Create (line 39) | public static OpusEncoder Create(int inputSamplingRate, int inputChann...
    method OpusEncoder (line 60) | private OpusEncoder(IntPtr encoder, int inputSamplingRate, int inputCh...
    method Encode (line 75) | public Span<byte> Encode(ReadOnlySpan<byte> inputPcmSamples, int sampl...
    method FrameCount (line 94) | public int FrameCount(int bufferSize)
    method FrameByteCount (line 107) | public int FrameByteCount(int frameCount)
    method Dispose (line 187) | public void Dispose()

FILE: TSLib/Audio/PassiveMergePipe.cs
  class PassiveMergePipe (line 18) | public class PassiveMergePipe : IAudioPassiveProducer, ICollection<IAudi...
    method Add (line 29) | public void Add(IAudioPassiveProducer addProducer)
    method Remove (line 44) | public bool Remove(IAudioPassiveProducer removeProducer)
    method Clear (line 58) | public void Clear()
    method Read (line 67) | public int Read(byte[] buffer, int offset, int length, out Meta? meta)
    method GetEnumerator (line 112) | IEnumerator<IAudioPassiveProducer> IEnumerable<IAudioPassiveProducer>....
    method GetEnumerator (line 114) | IEnumerator IEnumerable.GetEnumerator() => safeProducerList.GetEnumera...
    method Contains (line 116) | public bool Contains(IAudioPassiveProducer item) => safeProducerList.C...
    method CopyTo (line 118) | public void CopyTo(IAudioPassiveProducer[] array, int arrayIndex) => A...
    method Dispose (line 120) | public void Dispose()

FILE: TSLib/Audio/PassiveSplitterPipe.cs
  class PassiveSplitterPipe (line 16) | public class PassiveSplitterPipe : IAudioPipe
    method Add (line 39) | public void Add(IAudioPassiveConsumer consumer)
    method Remove (line 51) | public void Remove(IAudioPassiveConsumer consumer)
    method Clear (line 59) | public void Clear()
    method Write (line 68) | public void Write(Span<byte> data, Meta? meta)

FILE: TSLib/Audio/PreciseAudioTimer.cs
  class PreciseAudioTimer (line 17) | public class PreciseAudioTimer : ISampleInfo
    method PreciseAudioTimer (line 41) | public PreciseAudioTimer(ISampleInfo sampleInfo)
    method PreciseAudioTimer (line 44) | public PreciseAudioTimer(int sampleRate, int bits, int channel)
    method Start (line 56) | public void Start()
    method Stop (line 62) | public void Stop() => stopwatch.Stop();
    method Reset (line 64) | public void Reset()
    method PushBytes (line 70) | public void PushBytes(int count) => AbsoluteBufferLength += count;
    method ResetRemoteBuffer (line 72) | public void ResetRemoteBuffer()

FILE: TSLib/Audio/PreciseTimedPipe.cs
  class PreciseTimedPipe (line 16) | public class PreciseTimedPipe : IAudioActiveConsumer, IAudioActiveProduc...
    method PreciseTimedPipe (line 50) | public PreciseTimedPipe(ISampleInfo info, Id id)
    method PreciseTimedPipe (line 62) | public PreciseTimedPipe(ISampleInfo info, Id id, IAudioPassiveProducer...
    method PreciseTimedPipe (line 67) | public PreciseTimedPipe(ISampleInfo info, Id id, IAudioPassiveConsumer...
    method PreciseTimedPipe (line 72) | public PreciseTimedPipe(ISampleInfo info, Id id, IAudioPassiveProducer...
    method ReadLoop (line 78) | private void ReadLoop()
    method ReadTick (line 88) | private void ReadTick()
    method Dispose (line 112) | public void Dispose()

FILE: TSLib/Audio/StaticMetaPipe.cs
  class StaticMetaPipe (line 16) | public class StaticMetaPipe : IAudioPipe
    method ClearData (line 24) | private void ClearData()
    method SetNone (line 30) | public void SetNone()
    method SetVoice (line 36) | public void SetVoice()
    method SetWhisper (line 42) | public void SetWhisper(IReadOnlyList<ChannelId> channelIds, IReadOnlyL...
    method SetWhisperGroup (line 50) | public void SetWhisperGroup(GroupWhisperType type, GroupWhisperTarget ...
    method Write (line 59) | public void Write(Span<byte> data, Meta? meta)

FILE: TSLib/Audio/StreamAudioProducer.cs
  class StreamAudioProducer (line 14) | public class StreamAudioProducer : IAudioPassiveProducer
    method StreamAudioProducer (line 18) | public StreamAudioProducer(Stream stream) { this.stream = stream; }
    method Read (line 20) | public int Read(byte[] buffer, int offset, int length, out Meta? meta)
    method Dispose (line 26) | public void Dispose() => stream.Dispose();

FILE: TSLib/Audio/VolumePipe.cs
  class VolumePipe (line 15) | public class VolumePipe : IAudioPipe
    method AdjustVolume (line 33) | public static void AdjustVolume(Span<byte> audioSamples, float volume)
    method IsAbout (line 54) | private static bool IsAbout(float value, float compare) => Math.Abs(va...
    method Write (line 56) | public void Write(Span<byte> data, Meta? meta)

FILE: TSLib/Commands/CommandMultiParameter.cs
  class CommandMultiParameter (line 14) | public sealed partial class CommandMultiParameter : ICommandPart

FILE: TSLib/Commands/CommandOption.cs
  class CommandOption (line 18) | public class CommandOption : ICommandPart
    method CommandOption (line 23) | public CommandOption(string name) { Value = string.Concat(" -", name); }
    method CommandOption (line 28) | public CommandOption(Enum values)

FILE: TSLib/Commands/CommandParameter.cs
  class CommandParameter (line 18) | public sealed partial class CommandParameter : ICommandPart
    method Serialize (line 24) | [DebuggerStepThrough] public static string Serialize(bool value) => va...
    method Serialize (line 25) | [DebuggerStepThrough] public static string Serialize(sbyte value) => v...
    method Serialize (line 26) | [DebuggerStepThrough] public static string Serialize(byte value) => va...
    method Serialize (line 27) | [DebuggerStepThrough] public static string Serialize(short value) => v...
    method Serialize (line 28) | [DebuggerStepThrough] public static string Serialize(ushort value) => ...
    method Serialize (line 29) | [DebuggerStepThrough] public static string Serialize(int value) => val...
    method Serialize (line 30) | [DebuggerStepThrough] public static string Serialize(uint value) => va...
    method Serialize (line 31) | [DebuggerStepThrough] public static string Serialize(long value) => va...
    method Serialize (line 32) | [DebuggerStepThrough] public static string Serialize(ulong value) => v...
    method Serialize (line 33) | [DebuggerStepThrough] public static string Serialize(float value) => v...
    method Serialize (line 34) | [DebuggerStepThrough] public static string Serialize(double value) => ...
    method Serialize (line 35) | [DebuggerStepThrough] public static string Serialize(string value) => ...
    method Serialize (line 36) | [DebuggerStepThrough] public static string Serialize(DateTime value) =...

FILE: TSLib/Commands/ICommandPart.cs
  type ICommandPart (line 12) | public interface ICommandPart
  type CommandPartType (line 17) | public enum CommandPartType

FILE: TSLib/Commands/TsCommand.cs
  class TsCommand (line 22) | public partial class TsCommand : IEnumerable<ICommandPart>
    method TsCommand (line 33) | [DebuggerStepThrough]
    method TsCommand (line 44) | [DebuggerStepThrough]
    method Add (line 50) | [DebuggerStepThrough]
    method ExpectsResponse (line 72) | [DebuggerStepThrough]
    method ToString (line 81) | public override string ToString() => raw ??= BuildToString(Command, Ge...
    method BuildToString (line 89) | public static string BuildToString(string command, IEnumerable<IComman...
    method GetParameter (line 150) | private IEnumerable<ICommandPart> GetParameter() => parameter ?? Enume...
    method GetEnumerator (line 152) | public IEnumerator GetEnumerator() => GetParameter().GetEnumerator();
    method GetEnumerator (line 153) | IEnumerator<ICommandPart> IEnumerable<ICommandPart>.GetEnumerator() =>...
  class TsRawCommand (line 156) | public class TsRawCommand : TsCommand
    method TsRawCommand (line 158) | public TsRawCommand(string raw) : base(null!)
    method Add (line 163) | public override TsCommand Add(ICommandPart? addParameter)
    method ToString (line 168) | public override string ToString()

FILE: TSLib/Commands/TsCommand.gen.cs
  class TsCommand (line 26) | partial class TsCommand
    method Add (line 29) | [DebuggerStepThrough] public TsCommand Add(string key, bool? value) { ...
    method Add (line 30) | [DebuggerStepThrough] public TsCommand Add(string key, bool value) => ...
    method Add (line 32) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<boo...
    method Add (line 34) | [DebuggerStepThrough] public TsCommand Add(string key, sbyte? value) {...
    method Add (line 35) | [DebuggerStepThrough] public TsCommand Add(string key, sbyte value) =>...
    method Add (line 37) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<sby...
    method Add (line 39) | [DebuggerStepThrough] public TsCommand Add(string key, byte? value) { ...
    method Add (line 40) | [DebuggerStepThrough] public TsCommand Add(string key, byte value) => ...
    method Add (line 42) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<byt...
    method Add (line 44) | [DebuggerStepThrough] public TsCommand Add(string key, short? value) {...
    method Add (line 45) | [DebuggerStepThrough] public TsCommand Add(string key, short value) =>...
    method Add (line 47) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<sho...
    method Add (line 49) | [DebuggerStepThrough] public TsCommand Add(string key, ushort? value) ...
    method Add (line 50) | [DebuggerStepThrough] public TsCommand Add(string key, ushort value) =...
    method Add (line 52) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<ush...
    method Add (line 54) | [DebuggerStepThrough] public TsCommand Add(string key, int? value) { i...
    method Add (line 55) | [DebuggerStepThrough] public TsCommand Add(string key, int value) => A...
    method Add (line 57) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<int...
    method Add (line 59) | [DebuggerStepThrough] public TsCommand Add(string key, uint? value) { ...
    method Add (line 60) | [DebuggerStepThrough] public TsCommand Add(string key, uint value) => ...
    method Add (line 62) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<uin...
    method Add (line 64) | [DebuggerStepThrough] public TsCommand Add(string key, long? value) { ...
    method Add (line 65) | [DebuggerStepThrough] public TsCommand Add(string key, long value) => ...
    method Add (line 67) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<lon...
    method Add (line 69) | [DebuggerStepThrough] public TsCommand Add(string key, ulong? value) {...
    method Add (line 70) | [DebuggerStepThrough] public TsCommand Add(string key, ulong value) =>...
    method Add (line 72) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<ulo...
    method Add (line 74) | [DebuggerStepThrough] public TsCommand Add(string key, float? value) {...
    method Add (line 75) | [DebuggerStepThrough] public TsCommand Add(string key, float value) =>...
    method Add (line 77) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<flo...
    method Add (line 79) | [DebuggerStepThrough] public TsCommand Add(string key, double? value) ...
    method Add (line 80) | [DebuggerStepThrough] public TsCommand Add(string key, double value) =...
    method Add (line 82) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<dou...
    method Add (line 84) | [DebuggerStepThrough] public TsCommand Add(string key, string value) {...
    method Add (line 86) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<str...
    method Add (line 88) | [DebuggerStepThrough] public TsCommand Add(string key, DateTime? value...
    method Add (line 89) | [DebuggerStepThrough] public TsCommand Add(string key, DateTime value)...
    method Add (line 91) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Dat...
    method Add (line 93) | [DebuggerStepThrough] public TsCommand Add(string key, Uid? value) { i...
    method Add (line 94) | [DebuggerStepThrough] public TsCommand Add(string key, Uid value) => A...
    method Add (line 96) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Uid...
    method Add (line 98) | [DebuggerStepThrough] public TsCommand Add(string key, ClientDbId? val...
    method Add (line 99) | [DebuggerStepThrough] public TsCommand Add(string key, ClientDbId valu...
    method Add (line 101) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Cli...
    method Add (line 103) | [DebuggerStepThrough] public TsCommand Add(string key, ClientId? value...
    method Add (line 104) | [DebuggerStepThrough] public TsCommand Add(string key, ClientId value)...
    method Add (line 106) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Cli...
    method Add (line 108) | [DebuggerStepThrough] public TsCommand Add(string key, ChannelId? valu...
    method Add (line 109) | [DebuggerStepThrough] public TsCommand Add(string key, ChannelId value...
    method Add (line 111) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Cha...
    method Add (line 113) | [DebuggerStepThrough] public TsCommand Add(string key, ServerGroupId? ...
    method Add (line 114) | [DebuggerStepThrough] public TsCommand Add(string key, ServerGroupId v...
    method Add (line 116) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Ser...
    method Add (line 118) | [DebuggerStepThrough] public TsCommand Add(string key, ChannelGroupId?...
    method Add (line 119) | [DebuggerStepThrough] public TsCommand Add(string key, ChannelGroupId ...
    method Add (line 121) | [DebuggerStepThrough] public TsCommand Add(string key, IEnumerable<Cha...
  class CommandParameter (line 125) | partial class CommandParameter
    method CommandParameter (line 128) | [DebuggerStepThrough] public CommandParameter(string key, bool value) ...
    method CommandParameter (line 130) | [DebuggerStepThrough] public CommandParameter(string key, sbyte value)...
    method CommandParameter (line 132) | [DebuggerStepThrough] public CommandParameter(string key, byte value) ...
    method CommandParameter (line 134) | [DebuggerStepThrough] public CommandParameter(string key, short value)...
    method CommandParameter (line 136) | [DebuggerStepThrough] public CommandParameter(string key, ushort value...
    method CommandParameter (line 138) | [DebuggerStepThrough] public CommandParameter(string key, int value) {...
    method CommandParameter (line 140) | [DebuggerStepThrough] public CommandParameter(string key, uint value) ...
    method CommandParameter (line 142) | [DebuggerStepThrough] public CommandParameter(string key, long value) ...
    method CommandParameter (line 144) | [DebuggerStepThrough] public CommandParameter(string key, ulong value)...
    method CommandParameter (line 146) | [DebuggerStepThrough] public CommandParameter(string key, float value)...
    method CommandParameter (line 148) | [DebuggerStepThrough] public CommandParameter(string key, double value...
    method CommandParameter (line 150) | [DebuggerStepThrough] public CommandParameter(string key, string value...
    method CommandParameter (line 152) | [DebuggerStepThrough] public CommandParameter(string key, DateTime val...
    method CommandParameter (line 154) | [DebuggerStepThrough] public CommandParameter(string key, Uid value) {...
    method CommandParameter (line 156) | [DebuggerStepThrough] public CommandParameter(string key, ClientDbId v...
    method CommandParameter (line 158) | [DebuggerStepThrough] public CommandParameter(string key, ClientId val...
    method CommandParameter (line 160) | [DebuggerStepThrough] public CommandParameter(string key, ChannelId va...
    method CommandParameter (line 162) | [DebuggerStepThrough] public CommandParameter(string key, ServerGroupI...
    method CommandParameter (line 164) | [DebuggerStepThrough] public CommandParameter(string key, ChannelGroup...
  class CommandMultiParameter (line 168) | partial class CommandMultiParameter
    method CommandMultiParameter (line 171) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 173) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 175) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 177) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 179) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 181) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 183) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 185) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 187) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 189) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 191) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 193) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 195) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 197) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 199) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 201) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 203) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 205) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...
    method CommandMultiParameter (line 207) | [DebuggerStepThrough] public CommandMultiParameter(string key, IEnumer...

FILE: TSLib/Commands/TsConst.cs
  class TsConst (line 12) | public class TsConst
    method GetByServerBuildNum (line 21) | public static TsConst GetByServerBuildNum(ulong buildNum)
    method TsConst (line 64) | public TsConst() { }

FILE: TSLib/Commands/TsString.cs
  class TsString (line 21) | public static class TsString
    method Escape (line 23) | public static string Escape(string stringToEscape) => Escape(stringToE...
    method Escape (line 25) | public static string Escape(ReadOnlySpan<char> stringToEscape)
    method Unescape (line 47) | public static string Unescape(string stringToUnescape) => Unescape(str...
    method Unescape (line 49) | public static string Unescape(ReadOnlySpan<char> stringToUnescape)
    method Unescape (line 77) | public static string Unescape(ReadOnlySpan<byte> stringToUnescape)
    method TokenLength (line 107) | public static int TokenLength(string str) => Tools.Utf8Encoder.GetByte...
    method IsDoubleChar (line 109) | public static bool IsDoubleChar(char c) => unchecked(c == (byte)c) && ...
    method IsDoubleChar (line 115) | public static bool IsDoubleChar(byte c)

FILE: TSLib/ConnectionData.cs
  class ConnectionData (line 16) | public class ConnectionData
    method ConnectionData (line 24) | public ConnectionData(string address, Id? logId = null)
  class ConnectionDataFull (line 32) | public class ConnectionDataFull : ConnectionData
    method ConnectionDataFull (line 58) | public ConnectionDataFull(
  type Password (line 78) | public readonly struct Password
    method Password (line 84) | private Password(string hashed) { HashedPassword = hashed; }
    method FromHash (line 85) | public static Password FromHash(string hash) => new Password(hash);
    method FromPlain (line 86) | public static Password FromPlain(string pass) => new Password(TsCrypt....

FILE: TSLib/DisconnectEventArgs.cs
  class DisconnectEventArgs (line 15) | public class DisconnectEventArgs : EventArgs
    method DisconnectEventArgs (line 20) | public DisconnectEventArgs(Reason exitReason, CommandError? error = null)

FILE: TSLib/EventDispatcher.cs
  class EventDispatcherHelper (line 17) | internal static class EventDispatcherHelper
    method CreateLogThreadName (line 22) | internal static string CreateLogThreadName(string threadName, Id id) =...
    method CreateDispatcherTitle (line 24) | internal static string CreateDispatcherTitle(Id id) => CreateLogThread...
  type IEventDispatcher (line 30) | internal interface IEventDispatcher : IDisposable
    method Init (line 37) | void Init(Action<LazyNotification> dispatcher, Id id);
    method Invoke (line 40) | void Invoke(LazyNotification lazyNotification);
    method DoWork (line 41) | void DoWork();
  class ExtraThreadEventDispatcher (line 44) | internal sealed class ExtraThreadEventDispatcher : IEventDispatcher
    method ExtraThreadEventDispatcher (line 53) | public ExtraThreadEventDispatcher() { }
    method Init (line 56) | public void Init(Action<LazyNotification> dispatcher, Id id)
    method Invoke (line 70) | public void Invoke(LazyNotification lazyNotification)
    method DispatchLoop (line 76) | private void DispatchLoop()
    method DoWork (line 89) | public void DoWork()
    method Dispose (line 97) | public void Dispose()

FILE: TSLib/Full/Book/Book.cs
  class Connection (line 18) | public partial class Connection
    method Self (line 22) | public Client? Self() => GetClient(OwnClient);
    method CurrentChannel (line 23) | public Channel? CurrentChannel()
    method SetServer (line 31) | private void SetServer(Server server)
    method GetChannel (line 36) | private Channel? GetChannel(ChannelId id)
    method SetChannel (line 43) | private void SetChannel(Channel channel, ChannelId id)
    method RemoveChannel (line 49) | private void RemoveChannel(ChannelId id)
    method GetClient (line 56) | private Client? GetClient(ClientId id)
    method SetClient (line 63) | private void SetClient(Client client, ClientId id)
    method RemoveClient (line 69) | private void RemoveClient(ClientId id)
    method SetConnectionClientData (line 74) | private void SetConnectionClientData(ConnectionClientData connectionCl...
    method SetServerGroup (line 81) | private void SetServerGroup(ServerGroup serverGroup, ServerGroupId id)
    method GetServer (line 86) | private Server GetServer()
    method Reset (line 91) | public void Reset()
    method PostClientEnterView (line 102) | partial void PostClientEnterView(ClientEnterView msg) => SetOwnChannel...
    method PostClientMoved (line 103) | partial void PostClientMoved(ClientMoved msg) => SetOwnChannelSubscrib...
    method SetOwnChannelSubscribed (line 104) | private void SetOwnChannelSubscribed(ClientId clientId)
    method MaxClientsCcFun (line 118) | private static (MaxClients?, MaxClients?) MaxClientsCcFun(ChannelCreat...
    method MaxClientsCeFun (line 119) | private static (MaxClients?, MaxClients?) MaxClientsCeFun(ChannelEdite...
    method MaxClientsClFun (line 120) | private static (MaxClients?, MaxClients?) MaxClientsClFun(ChannelList ...
    method MaxClientsFun (line 121) | private static (MaxClients?, MaxClients?) MaxClientsFun(int? MaxClient...
    method ChannelTypeCcFun (line 142) | private static ChannelType ChannelTypeCcFun(ChannelCreated msg) => Cha...
    method ChannelTypeCeFun (line 143) | private static ChannelType ChannelTypeCeFun(ChannelEdited msg) => Chan...
    method ChannelTypeClFun (line 144) | private static ChannelType ChannelTypeClFun(ChannelList msg) => Channe...
    method ChannelTypeFun (line 145) | private static ChannelType ChannelTypeFun(bool? semi, bool? perma)
    method AwayCevFun (line 152) | private static string? AwayCevFun(ClientEnterView msg) => AwayFun(msg....
    method AwayCuFun (line 153) | private static string? AwayCuFun(ClientUpdated msg) => AwayFun(msg.IsA...
    method AwayFun (line 154) | private static string? AwayFun(bool? away, string? msg)
    method TalkPowerCevFun (line 161) | private static TalkPowerRequest? TalkPowerCevFun(ClientEnterView msg)
    method TalkPowerCuFun (line 167) | private static TalkPowerRequest? TalkPowerCuFun(ClientUpdated msg) => ...
    method TalkPowerFun (line 168) | private static TalkPowerRequest? TalkPowerFun(DateTime? time, string? ...
    method ClientTypeCevFun (line 175) | private static ClientType ClientTypeCevFun(ClientEnterView msg) => msg...
    method ChannelOrderCcFun (line 177) | private ChannelId ChannelOrderCcFun(ChannelCreated msg)
    method ChannelOrderCmFun (line 182) | private ChannelId ChannelOrderCmFun(ChannelMoved msg) => ChannelOrderM...
    method ChannelOrderCeFun (line 183) | private ChannelId? ChannelOrderCeFun(ChannelEdited msg)
    method ChannelOrderMoveFun (line 190) | private ChannelId ChannelOrderMoveFun(ChannelId id, ChannelId newOrder...
    method ChannelOrderRemove (line 207) | private void ChannelOrderRemove(ChannelId id, ChannelId oldOrder)
    method ChannelOrderInsert (line 217) | private void ChannelOrderInsert(ChannelId id, ChannelId newOrder, Chan...
    method AddressFun (line 235) | private static SocketAddr AddressFun(ClientConnectionInfo msg) => msg.Ip;
    method SetClientDataFun (line 237) | private void SetClientDataFun(InitServer initServer)
    method ChannelSubscribeFun (line 242) | private bool ChannelSubscribeFun(ChannelSubscribed _) => true;
    method ChannelUnsubscribeFun (line 244) | private bool ChannelUnsubscribeFun(ChannelUnsubscribed msg)
    method ReturnFalse (line 252) | private static bool ReturnFalse<T>(T _) => false;

FILE: TSLib/Full/Book/SpecialTypes.cs
  type MaxClients (line 14) | public struct MaxClients
  type MaxClientsKind (line 20) | public enum MaxClientsKind
  type ChannelType (line 27) | public enum ChannelType
  type TalkPowerRequest (line 34) | public struct TalkPowerRequest

FILE: TSLib/Full/GenerationWindow.cs
  class GenerationWindow (line 14) | public sealed class GenerationWindow
    method GenerationWindow (line 21) | public GenerationWindow(int mod) : this(mod, mod / 2) { }
    method GenerationWindow (line 23) | public GenerationWindow(int mod, int windowSize)
    method SetAndDrag (line 29) | public bool SetAndDrag(int mappedValue)
    method Advance (line 37) | public void Advance(int amount)
    method AdvanceToExcluded (line 54) | public void AdvanceToExcluded(int mappedValue)
    method IsInWindow (line 62) | public bool IsInWindow(int mappedValue)
    method IsNextGen (line 75) | public bool IsNextGen(int mappedValue) =>
    method GetGeneration (line 79) | public uint GetGeneration(int mappedValue) => (uint)(Generation + (IsN...
    method MappedToIndex (line 81) | public int MappedToIndex(int mappedValue)
    method Reset (line 100) | public void Reset()

FILE: TSLib/Full/IdentityData.cs
  class IdentityData (line 19) | public class IdentityData
    method IdentityData (line 45) | public IdentityData(BigInteger privateKey, ECPoint? publicKey = null)

FILE: TSLib/Full/License.cs
  class Licenses (line 18) | public class Licenses
    method Parse (line 28) | public static R<Licenses, string> Parse(ReadOnlySpan<byte> data)
    method DeriveKey (line 55) | public byte[] DeriveKey()
  class LicenseBlock (line 64) | public abstract class LicenseBlock
    method Parse (line 76) | public static R<(LicenseBlock block, int read), string> Parse(ReadOnly...
    method ReadNullString (line 132) | private static R<(string str, int read), string> ReadNullString(ReadOn...
    method DeriveKey (line 147) | public byte[] DeriveKey(ReadOnlySpan<byte> parent)
  class IntermediateLicenseBlock (line 169) | public class IntermediateLicenseBlock : LicenseBlock
    method IntermediateLicenseBlock (line 174) | public IntermediateLicenseBlock(string issuer)
  class WebsiteLicenseBlock (line 180) | public class WebsiteLicenseBlock : LicenseBlock
    method WebsiteLicenseBlock (line 185) | public WebsiteLicenseBlock(string issuer)
  class CodeLicenseBlock (line 191) | public class CodeLicenseBlock : LicenseBlock
    method CodeLicenseBlock (line 196) | public CodeLicenseBlock(string issuer)
  class ServerLicenseBlock (line 202) | public class ServerLicenseBlock : LicenseBlock
    method ServerLicenseBlock (line 208) | public ServerLicenseBlock(string issuer, ServerLicenseType licenseType)
  class EphemeralLicenseBlock (line 215) | public class EphemeralLicenseBlock : LicenseBlock
  type ChainBlockType (line 220) | public enum ChainBlockType : byte
  type ServerLicenseType (line 235) | public enum ServerLicenseType : byte

FILE: TSLib/Full/NetworkStats.cs
  class NetworkStats (line 20) | public sealed class NetworkStats
    method LogOutPacket (line 33) | internal void LogOutPacket<TDir>(ref Packet<TDir> packet)
    method LogInPacket (line 45) | internal void LogInPacket<TDir>(ref Packet<TDir> packet)
    method LogLostPings (line 57) | public void LogLostPings(int count)
    method AddPing (line 62) | public void AddPing(TimeSpan ping)
    method TypeToKind (line 72) | private static PacketKind TypeToKind(PacketType type)
    method GetWithin (line 93) | private static void GetWithin(Queue<PacketData> queue, TimeSpan time, ...
    method DropOver (line 115) | private static void DropOver(Queue<PacketData> queue, TimeSpan time)
    method GenerateStatusAnswer (line 122) | public TsCommand GenerateStatusAnswer()
    method StdDev (line 181) | private static double StdDev(IEnumerable<double> values)
    method Reset (line 196) | public void Reset()
    type PacketKind (line 210) | private enum PacketKind : ushort
    type PacketData (line 217) | private readonly struct PacketData
      method PacketData (line 223) | public PacketData(ushort size, DateTime sendPoint, PacketKind kind) ...
    type DataCatergory (line 226) | struct DataCatergory

FILE: TSLib/Full/Packet.cs
  type Packet (line 18) | internal struct Packet<TDir>
    method Packet (line 81) | public Packet(ReadOnlySpan<byte> data, PacketType packetType, ushort p...
    method FromRaw (line 91) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method ToString (line 105) | public override string ToString()
    method BuildHeader (line 116) | public void BuildHeader() => BuildHeader(Header);
    method BuildHeader (line 117) | public void BuildHeader(Span<byte> into)
    method FromHeader (line 141) | public void FromHeader()
  class ResendPacket (line 165) | internal class ResendPacket<T>
    method ResendPacket (line 171) | public ResendPacket(Packet<T> packet)
    method ToString (line 179) | public override string ToString() => $"RS(first:{FirstSendTime},last:{...
  type C2S (line 182) | internal struct C2S
  type S2C (line 189) | internal struct S2C

FILE: TSLib/Full/PacketHandler.cs
  class PacketHandler (line 23) | internal sealed class PacketHandler<TIn, TOut>
    method PacketHandler (line 70) | public PacketHandler(TsCrypt ts3Crypt, Id id)
    method Connect (line 85) | public E<string> Connect(IPEndPoint address)
    method Listen (line 99) | public void Listen(IPEndPoint address)
    method Initialize (line 113) | private E<Exception> Initialize(IPEndPoint address, bool connect)
    method Stop (line 172) | public void Stop() => Stop(null);
    method Stop (line 173) | private void Stop(Reason? closeReason)
    method AddOutgoingPacket (line 189) | public E<string> AddOutgoingPacket(ReadOnlySpan<byte> packet, PacketTy...
    method AddOutgoingSplitData (line 218) | private E<string> AddOutgoingSplitData(ReadOnlySpan<byte> rawData, Pac...
    method SendOutgoingData (line 253) | private E<string> SendOutgoingData(ReadOnlySpan<byte> data, PacketType...
    method GetPacketCounter (line 319) | private (ushort Id, uint Generation) GetPacketCounter(PacketType packe...
    method IncPacketCounter (line 324) | private void IncPacketCounter(PacketType packetType)
    method NeedsSplitting (line 331) | private static bool NeedsSplitting(int dataSize) => dataSize + OutHead...
    method FetchPacketEvent (line 333) | private static void FetchPacketEvent(object? selfObj, SocketAsyncEvent...
    method FetchPackets (line 371) | private void FetchPackets(Span<byte> buffer)
    method FindIncommingGenerationId (line 444) | private void FindIncommingGenerationId(ref Packet<TIn> packet)
    method ReceiveVoice (line 459) | private bool ReceiveVoice(ref Packet<TIn> packet, GenerationWindow win...
    method ReceiveCommand (line 462) | private bool ReceiveCommand(ref Packet<TIn> packet, RingQueue<Packet<T...
    method TryGetCommand (line 483) | private static bool TryGetCommand(RingQueue<Packet<TIn>> packetQueue, ...
    method SendAck (line 555) | private void SendAck(ushort ackId, PacketType ackType)
    method ReceiveAck (line 563) | private bool ReceiveAck(ref Packet<TIn> packet)
    method SendPing (line 579) | private void SendPing()
    method ReceivePing (line 585) | private void ReceivePing(ref Packet<TIn> packet)
    method ReceivePong (line 597) | private bool ReceivePong(ref Packet<TIn> packet)
    method ReceivedFinalInitAck (line 611) | public void ReceivedFinalInitAck()
    method ReceiveInitAck (line 616) | private bool ReceiveInitAck(ref Packet<TIn> packet)
    method UpdateRto (line 639) | private void UpdateRto(TimeSpan sampleRtt)
    method ResendLoop (line 658) | private void ResendLoop()
    method ResendPackets (line 713) | private bool ResendPackets(IEnumerable<ResendPacket<TOut>> packetList)
    method ResendPacket (line 721) | private bool ResendPacket(ResendPacket<TOut> packet)
    method SendRaw (line 745) | private E<string> SendRaw(ref Packet<TOut> packet)
  class PacketHandlerConst (line 776) | internal static class PacketHandlerConst

FILE: TSLib/Full/PacketType.cs
  type PacketType (line 14) | public enum PacketType : byte
  type PacketFlags (line 27) | [Flags]

FILE: TSLib/Full/QuickerLz.cs
  class QuickerLz (line 19) | public static class QuickerLz
    method GetCompressedSize (line 24) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method GetDecompressedSize (line 27) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method Compress (line 37) | public static Span<byte> Compress(ReadOnlySpan<byte> data, int level)
    method Decompress (line 148) | public static byte[] Decompress(ReadOnlySpan<byte> data, int maxSize)
    method WriteHeader (line 237) | private static void WriteHeader(Span<byte> dest, int srcLen, int level...
    method Write24 (line 265) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method Read24 (line 273) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method Hash (line 280) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method Is6Same (line 283) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method CopyBufferBytes (line 293) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
    method UpdateHashtable (line 304) | [MethodImpl(MethodImplOptions.AggressiveInlining)]

FILE: TSLib/Full/RingQueue.cs
  class RingQueue (line 17) | public sealed class RingQueue<T> where T : notnull
    method RingQueue (line 29) | public RingQueue(int maxBufferSize, int mod)
    method BufferSet (line 43) | private void BufferSet(int index, T value)
    method BufferGet (line 53) | private ref T BufferGet(int index)
    method StateGet (line 60) | private bool StateGet(int index)
    method BufferPop (line 67) | private void BufferPop()
    method BufferExtend (line 75) | private void BufferExtend(int index)
    method IndexToLocal (line 95) | private int IndexToLocal(int index) => (currentStart + index) % ringBu...
    method Set (line 99) | public void Set(int mappedValue, T value)
    method IsSet (line 108) | public ItemSetStatus IsSet(int mappedValue)
    method IsSetIndex (line 114) | private ItemSetStatus IsSetIndex(int index)
    method TryDequeue (line 125) | public bool TryDequeue([MaybeNullWhen(false)] out T value)
    method TryPeekStart (line 133) | public bool TryPeekStart(int index, [MaybeNullWhen(false)] out T value)
    method Clear (line 150) | public void Clear()
  type ItemSetStatus (line 166) | [Flags]

FILE: TSLib/Full/TsCrypt.cs
  class TsCrypt (line 33) | public sealed class TsCrypt
    method TsCrypt (line 55) | public TsCrypt(IdentityData identity)
    method Reset (line 61) | internal void Reset()
    method LoadIdentityDynamic (line 85) | public static R<IdentityData, string> LoadIdentityDynamic(string key, ...
    method LoadIdentity (line 99) | public static R<IdentityData, string> LoadIdentity(string key, ulong k...
    method LoadIdentity (line 113) | private static IdentityData LoadIdentity(ECPoint? publicKey, BigIntege...
    method ImportPublicKey (line 124) | private static R<ECPoint, string> ImportPublicKey(byte[] asnByteArray)
    method ImportKeyDynamic (line 138) | private static R<(ECPoint? publicKey, BigInteger? privateKey), string>...
    method ExportPublicKey (line 166) | internal static string ExportPublicKey(ECPoint publicKey)
    method ExportPrivateKey (line 176) | internal static string ExportPrivateKey(BigInteger privateKey)
    method ExportPublicAndPrivateKey (line 185) | internal static string ExportPublicAndPrivateKey(ECPoint publicKey, Bi...
    method GetUidFromPublicKey (line 196) | internal static string GetUidFromPublicKey(string publicKey)
    method RestorePublicFromPrivateKey (line 203) | internal static ECPoint RestorePublicFromPrivateKey(BigInteger private...
    method DeobfuscateAndImportTsIdentity (line 212) | public static R<IdentityData, string> DeobfuscateAndImportTsIdentity(s...
    method CryptoInit (line 255) | internal E<string> CryptoInit(string alpha, string beta, string omega)
    method GetSharedSecret (line 273) | private byte[] GetSharedSecret(ECPoint publicKeyPoint)
    method SetSharedSecret (line 291) | private E<string> SetSharedSecret(ReadOnlySpan<byte> alpha, ReadOnlySp...
    method CryptoInit2 (line 312) | internal E<string> CryptoInit2(string license, string omega, string pr...
    method GetSharedSecret2 (line 347) | private static byte[] GetSharedSecret2(ReadOnlySpan<byte> publicKey, R...
    method ProcessInit1 (line 362) | internal R<byte[], string> ProcessInit1<TDir>(byte[]? data)
    method SolveRsaChallange (line 495) | private static R<byte[], string> SolveRsaChallange(byte[] data, int of...
    method GenerateTemporaryKey (line 506) | internal static (byte[] publicKey, byte[] privateKey) GenerateTemporar...
    method Encrypt (line 524) | internal void Encrypt<TDir>(ref Packet<TDir> packet)
    method FakeEncrypt (line 573) | private static void FakeEncrypt<TDir>(ref Packet<TDir> packet, byte[] ...
    method Decrypt (line 585) | internal bool Decrypt<TDir>(ref Packet<TDir> packet)
    method DecryptData (line 617) | private bool DecryptData<TDir>(ref Packet<TDir> packet, bool dummyEncr...
    method FakeDecrypt (line 646) | private static bool FakeDecrypt<TDir>(ref Packet<TDir> packet, byte[] ...
    method GetKeyNonce (line 662) | private (byte[] key, byte[] nonce) GetKeyNonce(bool fromServer, ushort...
    method CheckEqual (line 706) | private static bool CheckEqual(ReadOnlySpan<byte> a1, ReadOnlySpan<byt...
    method XorBinary (line 717) | private static void XorBinary(ReadOnlySpan<byte> a, ReadOnlySpan<byte>...
    method Hash1It (line 727) | internal static byte[] Hash1It(byte[] data, int offset = 0, int len = ...
    method Hash256It (line 728) | internal static byte[] Hash256It(byte[] data, int offset = 0, int len ...
    method Hash512It (line 729) | internal static byte[] Hash512It(byte[] data, int offset = 0, int len ...
    method HashItInternal (line 730) | private static byte[] HashItInternal(System.Security.Cryptography.Hash...
    method HashPassword (line 744) | public static string HashPassword(string password)
    method Sign (line 753) | public static byte[] Sign(BigInteger privateKey, byte[] data)
    method VerifySign (line 762) | public static bool VerifySign(ECPoint publicKey, byte[] data, byte[] p...
    method EdCheck (line 773) | public static bool EdCheck(TsVersionSigned sign)
    method VersionSelfCheck (line 782) | public static void VersionSelfCheck()
    method Base64Decode (line 793) | internal static byte[]? Base64Decode(string str)
    method ImproveSecurity (line 812) | public static void ImproveSecurity(IdentityData identity, int toLevel)
    method GetSecurityLevel (line 834) | public static int GetSecurityLevel(IdentityData identity)
    method GenerateNewIdentity (line 845) | public static IdentityData GenerateNewIdentity(int securityLevel = 8)
    method GetSecurityLevel (line 862) | private static int GetSecurityLevel(byte[] hashBuffer, int pubKeyLen, ...
    method GetLeadingZeroBits (line 879) | private static int GetLeadingZeroBits(byte[] data)

FILE: TSLib/Full/TsFullClient.cs
  class TsFullClient (line 27) | public sealed partial class TsFullClient : TsBaseFunctions, IAudioActive...
    method TsFullClient (line 79) | public TsFullClient(DedicatedTaskScheduler? scheduler = null)
    method Connect (line 92) | public override async CmdR Connect(ConnectionData conData)
    method Disconnect (line 141) | public override async Task Disconnect()
    method ChangeState (line 162) | private void ChangeState(ConnectionContext ctx, TsClientStatus setStat...
    method PacketEvent (line 211) | private void PacketEvent(ConnectionContext ctx, ref Packet<S2C> packet)
    method ProcessEachInitIvExpand (line 259) | async partial void ProcessEachInitIvExpand(InitIvExpand initIvExpand)
    method ProcessEachInitIvExpand2 (line 276) | async partial void ProcessEachInitIvExpand2(InitIvExpand2 initIvExpand2)
    method ProcessEachInitServer (line 304) | partial void ProcessEachInitServer(InitServer initServer)
    method ProcessEachPluginCommand (line 318) | async partial void ProcessEachPluginCommand(PluginCommand cmd)
    method ProcessEachCommandError (line 324) | partial void ProcessEachCommandError(CommandError error)
    method ProcessEachClientLeftView (line 335) | partial void ProcessEachClientLeftView(ClientLeftView clientLeftView)
    method ProcessEachChannelListFinished (line 347) | async partial void ProcessEachChannelListFinished(ChannelListFinished _)
    method ProcessEachClientConnectionInfoUpdateRequest (line 353) | async partial void ProcessEachClientConnectionInfoUpdateRequest(Client...
    method ProcessPermList (line 360) | partial voi
Condensed preview — 507 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,479K chars).
[
  {
    "path": "ClassLibrary4.csproj",
    "chars": 385,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp3.1</TargetFramework>\n    <AssemblyN"
  },
  {
    "path": "ClassLibrary4.sln",
    "chars": 2106,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3392"
  },
  {
    "path": "LICENSE",
    "chars": 10296,
    "preview": "Open Software License (\"OSL\") v 3.0\n\nThis Open Software License (the \"License\") applies to any original work of\nauthorsh"
  },
  {
    "path": "README.md",
    "chars": 3603,
    "preview": "# ⚠️ 本项目已停止更新和维护\n\n**请使用最新的项目:[teamspeak-music-bot](https://github.com/ZHANGTIANYAO1/teamspeak-music-bot)**\n\n本仓库将不再进行任何更新"
  },
  {
    "path": "TS3AudioBot/Algorithm/IFilterAlgorithm.cs",
    "chars": 3503,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Algorithm/LruCache.cs",
    "chars": 1826,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Algorithm/TimedCache.cs",
    "chars": 1649,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/AudioValues.cs",
    "chars": 1544,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/CustomTargetPipe.cs",
    "chars": 5181,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/FfmpegProducer.cs",
    "chars": 13563,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/IPlayerSource.cs",
    "chars": 721,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/IVoiceTarget.cs",
    "chars": 1861,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/PlayInfo.cs",
    "chars": 1721,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/PlayInfoEventArgs.cs",
    "chars": 953,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/PlayManager.cs",
    "chars": 10684,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/Player.cs",
    "chars": 4655,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/SongEndEventArgs.cs",
    "chars": 624,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/SongInfoChanged.cs",
    "chars": 518,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/StallCheckPipe.cs",
    "chars": 1390,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Audio/StreamAudioPlayerSource.cs",
    "chars": 1544,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Bot.cs",
    "chars": 19672,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/BotManager.cs",
    "chars": 7495,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CallerInfo.cs",
    "chars": 1115,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ClientCall.cs",
    "chars": 1452,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/AstCommand.cs",
    "chars": 1166,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/AstError.cs",
    "chars": 1204,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/AstNode.cs",
    "chars": 1106,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/AstType.cs",
    "chars": 486,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/AstValue.cs",
    "chars": 1300,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Ast/StringType.cs",
    "chars": 492,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/BotCommand.cs",
    "chars": 4844,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandAttribute.cs",
    "chars": 1464,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandException.cs",
    "chars": 2719,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandManager.cs",
    "chars": 13737,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandParser.cs",
    "chars": 6603,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandResults/IAudioResourceResult.cs",
    "chars": 563,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandResults/IWrappedResult.cs",
    "chars": 507,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandResults/PickObjectCommand.cs",
    "chars": 1312,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandResults/TailString.cs",
    "chars": 754,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandSystemExtensions.cs",
    "chars": 1182,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/CommandSystemTypes.cs",
    "chars": 1392,
    "preview": "// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program is free software: you can redistribute it and/or modi"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/AliasCommand.cs",
    "chars": 1582,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/AppliedCommand.cs",
    "chars": 1239,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/CommandGroup.cs",
    "chars": 3160,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/FunctionCommand.cs",
    "chars": 16518,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/ICommand.cs",
    "chars": 1486,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/LazyCommand.cs",
    "chars": 1134,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/OverloadedFunctionCommand.cs",
    "chars": 3449,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/ResultCommand.cs",
    "chars": 1127,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Commands/RootCommand.cs",
    "chars": 1534,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/ExecutionInformation.cs",
    "chars": 714,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/ICommandBag.cs",
    "chars": 608,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/StaticList.cs",
    "chars": 1996,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/AppliedTextMod.cs",
    "chars": 1304,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/Color.cs",
    "chars": 4987,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/LongTextBehaviour.cs",
    "chars": 685,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/LongTextTransform.cs",
    "chars": 3075,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/TextMod.cs",
    "chars": 2104,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/TextModBuilder.cs",
    "chars": 3915,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/TextModFlag.cs",
    "chars": 593,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/CommandSystem/Text/TextModHelper.cs",
    "chars": 532,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/Config.cs",
    "chars": 6877,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigArray.cs",
    "chars": 1684,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigDynamicTable.cs",
    "chars": 2243,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigEnumerable.cs",
    "chars": 4140,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigHelper.cs",
    "chars": 2330,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigPart.cs",
    "chars": 6844,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigStructs.cs",
    "chars": 19009,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigTable.cs",
    "chars": 1786,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigUpgrade2.cs",
    "chars": 1756,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Config/ConfigValue.cs",
    "chars": 3822,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Core.cs",
    "chars": 4563,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/DbStore.cs",
    "chars": 1675,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/BasicInjector.cs",
    "chars": 906,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/ChainedInjector.cs",
    "chars": 1034,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/DependencyBuilder.cs",
    "chars": 3504,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/IInjector.cs",
    "chars": 708,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/InjectorExtensions.cs",
    "chars": 2632,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/Module.cs",
    "chars": 1029,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Dependency/NullInjector.cs",
    "chars": 683,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Environment/Stats.cs",
    "chars": 10368,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Environment/SystemData.cs",
    "chars": 6766,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Environment/SystemMonitor.cs",
    "chars": 2794,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Error.cs",
    "chars": 1248,
    "preview": "using System;\nusing System.Runtime.Serialization;\n\nnamespace TS3AudioBot\n{\n\tpublic static class Error\n\t{\n\t\tpublic static"
  },
  {
    "path": "TS3AudioBot/Helper/AttributeStrings.cs",
    "chars": 551,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/Const.cs",
    "chars": 824,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/Diagnose/SelfDiagnoseLevel.cs",
    "chars": 492,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/Diagnose/SelfDiagnoseMessage.cs",
    "chars": 860,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/IJsonConfig.cs",
    "chars": 1975,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/ImageUtil.cs",
    "chars": 2741,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/Interactive.cs",
    "chars": 1373,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/LimitStream.cs",
    "chars": 2204,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/TextUtil.cs",
    "chars": 4089,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/TomlTools.cs",
    "chars": 16938,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/Util.cs",
    "chars": 5249,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Helper/WebWrapper.cs",
    "chars": 7533,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/AudioLogEntry.cs",
    "chars": 1825,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/HistoryManager.cs",
    "chars": 11059,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/HistorySaveData.cs",
    "chars": 776,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/IHistoryFormatter.cs",
    "chars": 785,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/SearchQuery.cs",
    "chars": 753,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/History/SmartHistoryFormatter.cs",
    "chars": 5585,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/InvokerData.cs",
    "chars": 700,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Limits.cs",
    "chars": 714,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Localization/DynamicResourceManager.cs",
    "chars": 1882,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Localization/LocalStr.cs",
    "chars": 739,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Localization/LocalizationManager.cs",
    "chars": 6271,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Localization/strings.Designer.cs",
    "chars": 101626,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code w"
  },
  {
    "path": "TS3AudioBot/Localization/strings.resx",
    "chars": 43225,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The prima"
  },
  {
    "path": "TS3AudioBot/MainCommands.cs",
    "chars": 76913,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/LoopMode.cs",
    "chars": 471,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Parser/JspfContent.cs",
    "chars": 3179,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Playlist.cs",
    "chars": 2298,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/PlaylistApiExtensions.cs",
    "chars": 855,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/PlaylistIO.cs",
    "chars": 9275,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/PlaylistItem.cs",
    "chars": 1165,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/PlaylistManager.cs",
    "chars": 5697,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Shuffle/IShuffleAlgorithm.cs",
    "chars": 939,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Shuffle/LinearFeedbackShiftRegister.cs",
    "chars": 3603,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Shuffle/ListedShuffle.cs",
    "chars": 1791,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Playlists/Shuffle/NormalOrder.cs",
    "chars": 805,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/ITabPlugin.cs",
    "chars": 962,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/Plugin.cs",
    "chars": 15797,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginCommandBag.cs",
    "chars": 838,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginExtensions.cs",
    "chars": 783,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginManager.cs",
    "chars": 6603,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginObjects.cs",
    "chars": 802,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginResponse.cs",
    "chars": 636,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Plugins/PluginStatus.cs",
    "chars": 980,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Properties/PublishProfiles/FolderProfile.pubxml",
    "chars": 583,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project>\n  <PropertyGr"
  },
  {
    "path": "TS3AudioBot/Properties/PublishProfiles/FolderProfile.pubxml.user",
    "chars": 512,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project>\n  <PropertyGr"
  },
  {
    "path": "TS3AudioBot/Properties.cs",
    "chars": 91,
    "preview": "using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"TS3ABotUnitTests\")]\n"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/AudioResource.cs",
    "chars": 2650,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/AudioTags/AudioTagReader.cs",
    "chars": 8083,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/AudioTags/BinaryReaderBigEndianExtensions.cs",
    "chars": 3734,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/AudioTags/M3uReader.cs",
    "chars": 3979,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/BandcampResolver.cs",
    "chars": 5800,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/IPlaylistResolver.cs",
    "chars": 671,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/IResolver.cs",
    "chars": 523,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/IResourceResolver.cs",
    "chars": 2017,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/ISearchResolver.cs",
    "chars": 620,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/IThumbnailResolver.cs",
    "chars": 642,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/MatchCertainty.cs",
    "chars": 950,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/MediaResolver.cs",
    "chars": 11228,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/PlayResource.cs",
    "chars": 1021,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/ResolveContext.cs",
    "chars": 1507,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/ResourceResolver.cs",
    "chars": 10306,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/SongInfo.cs",
    "chars": 240,
    "preview": "using System;\n\nnamespace TS3AudioBot.ResourceFactories\n{\n\tpublic class SongInfo\n\t{\n\t\tpublic string? Title { get; set; }\n"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/SoundcloudResolver.cs",
    "chars": 6655,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/TwitchResolver.cs",
    "chars": 5794,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/Youtube/Json.cs",
    "chars": 2141,
    "preview": "namespace TS3AudioBot.ResourceFactories.Youtube\n{\n#pragma warning disable CS0649, CS0169, IDE1006\n\t// ReSharper disable "
  },
  {
    "path": "TS3AudioBot/ResourceFactories/Youtube/LoaderPriority.cs",
    "chars": 496,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/Youtube/VideoCodec.cs",
    "chars": 519,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/Youtube/VideoData.cs",
    "chars": 993,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/Youtube/YoutubeResolver.cs",
    "chars": 14279,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/ResourceFactories/YoutubeDlHelper.cs",
    "chars": 9546,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Resources/DefaultRights.toml",
    "chars": 1434,
    "preview": "# Rights declaration file\n# For more information about syntax and structure see here:\n# https://github.com/Splamy/TS3Aud"
  },
  {
    "path": "TS3AudioBot/Resources/NLog.config",
    "chars": 1472,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<nlog xmlns=\"http://www.nlog-project.org/schemas/NLog.xsd\"\n      xmlns:xsi=\"http"
  },
  {
    "path": "TS3AudioBot/Rights/CreateFileSettings.cs",
    "chars": 582,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/ExecuteContext.cs",
    "chars": 1176,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchApiCallerIp.cs",
    "chars": 819,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchBot.cs",
    "chars": 730,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchChannelGroupId.cs",
    "chars": 872,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchClientGroupId.cs",
    "chars": 856,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchClientUid.cs",
    "chars": 788,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchHost.cs",
    "chars": 739,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchIsApi.cs",
    "chars": 633,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchPermission.cs",
    "chars": 3529,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchToken.cs",
    "chars": 754,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/MatchVisibility.cs",
    "chars": 781,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/Matcher.cs",
    "chars": 519,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/Matchers/PermCompare.cs",
    "chars": 525,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/ParseContext.cs",
    "chars": 2097,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/RightsDecl.cs",
    "chars": 3166,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/RightsGroup.cs",
    "chars": 735,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/RightsManager.cs",
    "chars": 18621,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Rights/RightsRule.cs",
    "chars": 5273,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Sessions/AnonymousSession.cs",
    "chars": 510,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Sessions/ApiToken.cs",
    "chars": 816,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Sessions/SessionManager.cs",
    "chars": 1447,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Sessions/TokenManager.cs",
    "chars": 2722,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Sessions/UserSession.cs",
    "chars": 1919,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Setup.cs",
    "chars": 5987,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/TS3AudioBot.csproj",
    "chars": 4410,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <RootNamespace>TS3AudioBot</RootNamespace>\n    <AssemblyName>TS"
  },
  {
    "path": "TS3AudioBot/TS3AudioBot.csproj.user",
    "chars": 346,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/ms"
  },
  {
    "path": "TS3AudioBot/Ts3Client.cs",
    "chars": 25634,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Upgrader.cs",
    "chars": 1579,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/ApiCall.cs",
    "chars": 981,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/DataStream.cs",
    "chars": 798,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/JsonArray.cs",
    "chars": 703,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/JsonEmpty.cs",
    "chars": 714,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/JsonError.cs",
    "chars": 1226,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/JsonObject.cs",
    "chars": 1124,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/JsonValue.cs",
    "chars": 2258,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/OpenApiGenerator.cs",
    "chars": 7735,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/TimeSpanConverter.cs",
    "chars": 905,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Api/WebApi.cs",
    "chars": 10901,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Model/CurrentSongInfo.cs",
    "chars": 761,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Model/PlaylistInfo.cs",
    "chars": 1355,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  },
  {
    "path": "TS3AudioBot/Web/Model/PlaylistItemGetData.cs",
    "chars": 700,
    "preview": "// TS3AudioBot - An advanced Musicbot for Teamspeak 3\n// Copyright (C) 2017  TS3AudioBot contributors\n//\n// This program"
  }
]

// ... and 307 more files (download for full content)

About this extraction

This page contains the full source code of the ZHANGTIANYAO1/TS3AudioBot-NetEaseCloudmusic-plugin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 507 files (3.9 MB), approximately 1.0M tokens, and a symbol index with 3673 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!