/// <summary> /// Method for configuring <see cref="LavalinkExtension"/>, accessing each configuration individually. /// </summary> /// <param name="botBase"></param> /// <param name="hostname">Sets the hostname associated with the Lavalink.</param> /// <param name="port">Sets the port associated with the Lavalink.</param> /// <param name="password">Sets the password associated with the Lavalink.</param> /// <param name="secured">Sets the secured status associated with the Lavalink.</param> /// <param name="region">Sets the voice region ID for the Lavalink connection. This should be used if nodes should be filtered by region with <see cref="LavalinkExtension.GetIdealNodeConnection(DiscordVoiceRegion)"/>.</param> /// <param name="resumeKey">Sets the resume key for the Lavalink connection. This will allow existing voice sessions to continue for a certain time after the client is disconnected.</param> /// <param name="resumeTimeout">Sets the time in seconds when all voice sessions are closed after the client disconnects. Defaults to 1 minute.</param> /// <param name="webSocketCloseTimeout">Sets the time in miliseconds to wait for Lavalink's voice WebSocket to close after leaving a voice channel. This will be the delay before the guild connection is removed. Defaults to 3 minutes.</param> /// <param name="socketAutoReconnect">Sets whether the connection wrapper should attempt automatic reconnects should the connection drop.</param> public static void LavalinkSetup(this TarsBase botBase, string hostname, int port, string password, bool secured = false, DiscordVoiceRegion region = null, string resumeKey = null, TimeSpan?resumeTimeout = null, TimeSpan?webSocketCloseTimeout = null, bool socketAutoReconnect = true) { _botBase = botBase; var connectionEndpoint = new ConnectionEndpoint(hostname, port, secured); _lavalinkConfiguration = new LavalinkConfiguration { SocketEndpoint = connectionEndpoint, RestEndpoint = connectionEndpoint, Password = password, Region = region, ResumeKey = resumeKey, ResumeTimeout = (int)(resumeTimeout?.TotalSeconds ?? TimeSpan.FromMinutes(1).TotalSeconds), WebSocketCloseTimeout = (int)(webSocketCloseTimeout?.TotalMilliseconds ?? TimeSpan.FromSeconds(3).TotalMilliseconds), SocketAutoReconnect = _socketAutoReconnect = socketAutoReconnect }; _lavalink = botBase.Discord.UseLavalink(); botBase.Discord.Heartbeated += DiscordHeartbeated; RegisterExitEvent(); RegisterLavalinkAsService(botBase); HttpClientSetup(); }
private async Task <LavalinkNodeConnection> ConnectLavaNodeAsync() { ConnectionEndpoint endpoint = new ConnectionEndpoint { Hostname = "server.local", Port = 2333 }; LavalinkConfiguration lavalinkConfig = new LavalinkConfiguration { Password = ReadConfig.Config.LavaLinkPass, RestEndpoint = endpoint, SocketEndpoint = endpoint, SocketAutoReconnect = false }; LavalinkExtension lavalink = Client.UseLavalink(); LavalinkNodeConnection res = null; try { res = await lavalink.ConnectAsync(lavalinkConfig); } catch (WebSocketException e) { SystemService.Instance.Logger.Log("Failed to start connection with Lavalink server:\n" + e.Message); } return(res); }
public LavaPlayer() { LavalinkConfiguration lavaConfig = global.botConfig.GetLavalinkConfiguration(); LavalinkExtension lavalink = global.bot.UseLavalink(); lavalink.ConnectAsync(lavaConfig).GetAwaiter().GetResult(); }
//Finished downloading guild information private async Task Client_Ready(DiscordClient client, ReadyEventArgs e) { //Setup lavalink var LavaConfig = new LavalinkConfiguration { RestEndpoint = new ConnectionEndpoint { Hostname = "localhost", Port = 2333 }, SocketEndpoint = new ConnectionEndpoint { Hostname = "localhost", Port = 2333 }, Password = this.LavaPass }; this.LavalinkNode = await this.Lavalink.ConnectAsync(LavaConfig); //Check if global config is empty, this shouldn't happen normally if (await this.Config.IsEmpty()) { //Populate the config with default settings for all guilds. List <DiscordGuild> Yes = new List <DiscordGuild>(); Yes.AddRange(client.Guilds.Values); this.Config = await this.SettingsInstance.CreateAllDefaultSettings(client, this.SQLConn); await this.Config.SaveSettings(SQLConn); } await client.UpdateStatusAsync(this.Config.StartupActivity, UserStatus.Online); client.Logger.Log(LogLevel.Information, "Connected successfully"); }
public MusicModule() { this.endpoint = new ConnectionEndpoint("127.0.0.1", 2333); this.configuration = new LavalinkConfiguration { Password = "******", RestEndpoint = this.endpoint, SocketEndpoint = this.endpoint, ResumeKey = "tutorial-bot" }; }
static async Task MainAsync() { Discord = new DiscordClient(new DiscordConfiguration() { Token = Environment.GetEnvironmentVariable("TOKEN"), TokenType = TokenType.Bot, MinimumLogLevel = LogLevel.Debug, AutoReconnect = true, }); var commands = Discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { ">" } }); commands.RegisterCommands <MyFirstModule>(); commands.RegisterCommands <MusicModule>(); commands.RegisterCommands <BobaModule>(); commands.RegisterCommands <EggModule>(); commands.RegisterCommands <DameModule>(); var endpoint = new ConnectionEndpoint { Hostname = "newmydiscordbot-lavalink.herokuapp.com", // From your server configuration. //Port = Convert.ToInt32(Environment.GetEnvironmentVariable("PORT") ?? "80") // From your server configuration //Hostname = "127.0.0.1", Port = 80 }; Console.WriteLine(endpoint.Port); var lavalinkConfig = new LavalinkConfiguration { Password = "******", // From your server configuration. //Password= "******", RestEndpoint = endpoint, SocketEndpoint = endpoint, ResumeTimeout = 50 }; var lavalink = Discord.UseLavalink(); await Discord.ConnectAsync(new DSharpPlus.Entities.DiscordActivity("Anime", DSharpPlus.Entities.ActivityType.Watching)); await lavalink.ConnectAsync(lavalinkConfig); await Task.Delay(-1); }
private LavalinkConfiguration CreateLavalinkConfig() { var endpoint = new ConnectionEndpoint { Hostname = jsonSettings.LavaHost, Port = jsonSettings.LavaPort }; var lavalinkConfig = new LavalinkConfiguration { Password = jsonSettings.LavaPassword, RestEndpoint = endpoint, SocketEndpoint = endpoint }; return(lavalinkConfig); }
public MusicService(LaveyBot bot) { this._bot = bot; this._log = LogManager.GetLogger("MusicService"); this._players = new ConcurrentDictionary <ulong, MusicPlayer>(); this._endpoint = new ConnectionEndpoint(this._bot.Config.Lavalink.Endpoint.Hostname, this._bot.Config.Lavalink.Endpoint.Port); this._config = new LavalinkConfiguration { Password = bot.Config.Lavalink.Password, RestEndpoint = this._endpoint, SocketEndpoint = this._endpoint }; this._bot.Discord.Ready += this.OnReady; this._bot.Discord.Heartbeated += this.OnHeartbeat; }
public LavalinkConfiguration GetLavalinkConfiguration() { ConnectionEndpoint endpoint = new ConnectionEndpoint { Hostname = global.botConfig.lavalinkServerIp, Port = global.botConfig.lavalinkServerPort }; LavalinkConfiguration config = new LavalinkConfiguration { Password = global.botConfig.lavalinkPassword, RestEndpoint = endpoint, SocketEndpoint = endpoint, }; return(config); }
public MusicService(DiscordClient discord) { configuration = new LavalinkConfiguration { Password = "******", RestEndpoint = new ConnectionEndpoint { Hostname = "ubge.ddns.net", //ubge.ddns.net ou 54.207.51.138 Port = 65201 }, SocketEndpoint = new ConnectionEndpoint { Hostname = "ubge.ddns.net", Port = 65201 } }; Players = new ConcurrentDictionary <ulong, MusicPlayer>(); Discord = discord; Discord.Ready += Discord_Ready; }
static Configuration() { if (!File.Exists("appsettings.json")) { throw new FileNotFoundException("Settings file has not been found", "appsettings.json"); } var json = JObject.Parse(File.ReadAllText("appsettings.json")); Discord = new DiscordConfiguration( (string)json["DiscordTokens"]?["Development"], (string)json["DiscordTokens"]?["Production"], (bool)json["DiscordTokens"]?["UseProduction"]); Lavalink = new LavalinkConfiguration( (string)json[nameof(Lavalink)]?["Host"], (int)json[nameof(Lavalink)]?["Port"], (string)json[nameof(Lavalink)]?["Password"]); }
public async Task RodandoBot(string[] args) { var discord = new DiscordClient(new DiscordConfiguration { Token = Parameters.token, TokenType = TokenType.Bot, MinimumLogLevel = LogLevel.Debug }); discord.UseInteractivity(new InteractivityConfiguration() { Timeout = System.TimeSpan.FromSeconds(30), PollBehaviour = PollBehaviour.KeepEmojis, PaginationBehaviour = PaginationBehaviour.Ignore, PaginationDeletion = PaginationDeletion.KeepEmojis }); this.Database = new DataContext(); this.StartsRepository = new StartsRepository(this.Database); this.ItemRepository = new ItemRepository(this.Database); this.CharactersRepository = new CharactersRepository(this.Database); this.AtributesRepository = new AtributesRepository(this.Database); this.MonsterRepository = new MonsterRepository(this.Database); this.BattleService = new BattleService(this.Database); var services = new ServiceCollection() .AddSingleton(this.Database) .AddSingleton(this.StartsRepository) .AddSingleton(this.ItemRepository) .AddSingleton(this.CharactersRepository) .AddSingleton(this.AtributesRepository) .AddSingleton(this.MonsterRepository) .AddSingleton(this.BattleService) .BuildServiceProvider(); var commands = discord.UseCommandsNext(new CommandsNextConfiguration { Services = services, StringPrefixes = Parameters.Prefix }); commands.RegisterCommands <LavaLinkCommands>(); commands.RegisterCommands <StartCommands>(); commands.RegisterCommands <AssignAtributtesCharacter>(); commands.RegisterCommands <StatusCommands>(); commands.RegisterCommands <ItemCommands>(); commands.RegisterCommands <MonsterCommands>(); commands.RegisterCommands <BattleCommands>(); commands.RegisterCommands <B3Commands>(); //B3Api.B3Api.B3(args); var endPoint = new ConnectionEndpoint { Hostname = "127.0.0.1", Port = 2333 }; var lavaLinkConfig = new LavalinkConfiguration { Password = "******", RestEndpoint = endPoint, SocketEndpoint = endPoint }; var lavalink = discord.UseLavalink(); await discord.ConnectAsync(); await lavalink.ConnectAsync(lavaLinkConfig); //espera infinita, para o bot ficar online continuamente. await Task.Delay(-1); }
private static async Task MainAsync() { _client = new DiscordClient(new DiscordConfiguration { Token = Configuration.Discord.UseProduction ? Configuration.Discord.Production : Configuration.Discord.Development, TokenType = TokenType.Bot, #if DEBUG MinimumLogLevel = LogLevel.Debug, #else MinimumLogLevel = LogLevel.Information, #endif Intents = DiscordIntents.All }); _client.GuildMemberAdded += OnGuildMemberAdded; _client.GuildMemberRemoved += OnGuildMemberRemoved; _client.GuildCreated += OnGuildCreated; _client.GuildDeleted += OnGuildDeleted; _client.GuildBanAdded += OnGuildBanAdded; _client.GuildBanRemoved += OnGuildBanRemoved; _client.MessageCreated += OnMessageCreated; _client.UseInteractivity(new InteractivityConfiguration { PollBehaviour = PollBehaviour.DeleteEmojis, Timeout = TimeSpan.FromSeconds(30) }); var commands = _client.UseCommandsNext(new CommandsNextConfiguration { StringPrefixes = new[] {"wyrobot!"}, IgnoreExtraArguments = true, EnableMentionPrefix = false, PrefixResolver = ResolvePrefixAsync }); commands.RegisterEvents(); commands.RegisterConverter(new BoolConverter()); commands.SetHelpFormatter<CustomHelpFormatter>(); commands.RegisterCommands<SettingsCommands>(); commands.RegisterCommands<LevelingCommands>(); commands.RegisterCommands<LevelingSettingsCommands>(); commands.RegisterCommands<LevelRewardsSettingsCommands>(); commands.RegisterCommands<ModerationCommands>(); commands.RegisterCommands<ModerationSettingsCommands>(); commands.RegisterCommands<WelcomeSettingsCommands>(); commands.RegisterCommands<MusicCommands>(); var endpoint = new ConnectionEndpoint { Hostname = Configuration.Lavalink.Host, Port = Configuration.Lavalink.Port }; var lavalinkConfig = new LavalinkConfiguration { Password = Configuration.Lavalink.Password, RestEndpoint = endpoint, SocketEndpoint = endpoint }; var lavalink = _client.UseLavalink(); SanctionHandler.InitializeAndStart(_client); await _client.ConnectAsync(new DiscordActivity { Name = "with code", ActivityType = ActivityType.Playing }); await lavalink.ConnectAsync(lavalinkConfig); await Task.Delay(-1); }
public async Task RunBotAsync() { // read config string json; await using (FileStream fs = File.OpenRead(Path.Combine(Directory.GetCurrentDirectory(), "config.json"))) using (StreamReader sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync(); Configuration = JsonConvert.DeserializeObject <ConfigJson>(json); // setup client DiscordConfiguration cfg = new DiscordConfiguration { Token = Configuration.Token, TokenType = TokenType.Bot, Intents = DiscordIntents.GuildMessages | DiscordIntents.Guilds | DiscordIntents.GuildMembers | DiscordIntents.GuildBans | DiscordIntents.GuildVoiceStates, AutoReconnect = true, MinimumLogLevel = LogLevel.Debug, LogTimestampFormat = "dd MMM yyy - hh:mm:ss" }; Client = new DiscordShardedClient(cfg); Client.Ready += new Ready(Client).Client_Ready; Client.GuildAvailable += new GuildAvailable(Client).Client_GuildAvailable; Client.ClientErrored += new ClientErrored(Client).Client_ClientErrored; // load custom commands List <Type> typesToRegister = new List <Type>(); if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "CustomCommands"))) { Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "CustomCommands"); } else { string[] assemblyList = Directory.GetFiles( Path.Combine(Directory.GetCurrentDirectory(), "CustomCommands"), "*.dll", SearchOption.AllDirectories); foreach (string assemblyPath in assemblyList) { Assembly assembly = Assembly.LoadFile(assemblyPath); Type type = assembly.GetType("SecretariaEletronica.CustomCommands.Main"); typesToRegister.Add(type); } } // setup commandsnext CommandsNextConfiguration commandCfg = new CommandsNextConfiguration { StringPrefixes = Configuration.CommandPrefix, EnableDms = true, EnableMentionPrefix = true }; Commands = await Client.UseCommandsNextAsync(commandCfg); foreach (CommandsNextExtension cmdNext in Commands.Values) { cmdNext.CommandExecuted += new CommandExecuted().Commands_CommandExecuted; cmdNext.CommandErrored += new CommandErrored().Commands_CommandErrored; cmdNext.RegisterCommands <CustomCommands>(); cmdNext.RegisterCommands <DrawningCommands>(); cmdNext.RegisterCommands <LavaLinkCommands>(); cmdNext.RegisterCommands <MiscCommands>(); cmdNext.RegisterCommands <ModeratorCommands>(); cmdNext.RegisterCommands <VoiceCommands>(); cmdNext.RegisterCommands <WaxCommands>(); foreach (Type type in typesToRegister) { cmdNext.RegisterCommands(type); } } // setup lavalink ConnectionEndpoint endpoint = new ConnectionEndpoint { Hostname = Configuration.LavaLinkIp, Port = Configuration.LavaLinkPort }; LavalinkConfiguration lavalinkConfig = new LavalinkConfiguration { Password = Configuration.LavaLinkPass, RestEndpoint = endpoint, SocketEndpoint = endpoint }; Voice = await Client.UseVoiceNextAsync(new VoiceNextConfiguration()); LavaLink = await Client.UseLavalinkAsync(); await Client.StartAsync(); foreach (LavalinkExtension lava in LavaLink.Values) { await lava.ConnectAsync(lavalinkConfig); } // setup mongodb _mongoClient = new MongoClient(Configuration.MongoUrl); Database = _mongoClient.GetDatabase("SecretariaEletronica"); await Task.Delay(-1); }
public async Task RunAsync() { var json = string.Empty; using (var fs = File.OpenRead(Path.GetFullPath(@"C:\Users\niedz\Desktop\C#\CustomBot\CustomBot\bin\Debug\net5.0\config.json"))) using (var sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync(); var configJson = JsonConvert.DeserializeObject <ConfigJson>(json); var config = new DiscordConfiguration { Token = configJson.Token, TokenType = TokenType.Bot, AutoReconnect = true, MinimumLogLevel = LogLevel.Debug }; var endpoint = new ConnectionEndpoint { Hostname = "127.0.0.1", Port = 2333 }; var lavalinkConfig = new LavalinkConfiguration { Password = "******", RestEndpoint = endpoint, SocketEndpoint = endpoint }; Client = new DiscordClient(config); Client.Ready += OnClientReady; Client.UseInteractivity(new InteractivityConfiguration { Timeout = TimeSpan.FromMinutes(2) }); var commandsConfig = new CommandsNextConfiguration { StringPrefixes = new string[] { configJson.Prefix }, EnableDms = false, EnableMentionPrefix = true, DmHelp = false, Services = _serviceProvider }; var lavalink = Client.UseLavalink(); Commands = Client.UseCommandsNext(commandsConfig); Commands.RegisterCommands <PlaylistCommands>(); Commands.RegisterCommands <FounderCommands>(); Commands.RegisterCommands <JavalinkCommands>(); Voice = Client.UseVoiceNext(); await Client.ConnectAsync(); await lavalink.ConnectAsync(lavalinkConfig); await Task.Delay(-1); }
private static async Task MainAsync() { var json = string.Empty; using (var fs = File.OpenRead("../../../config.json")) //a hacky way to arrive at the config directory using (var sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync().ConfigureAwait(false); var configJson = JsonConvert.DeserializeObject <ConfigJson>(json); //json config reading complete discord = new DiscordClient(new DiscordConfiguration() { Token = configJson.Token, TokenType = TokenType.Bot, AutoReconnect = true, MinimumLogLevel = LogLevel.Debug, Intents = DiscordIntents.GuildEmojis | DiscordIntents.GuildVoiceStates | DiscordIntents.GuildMessageReactions | DiscordIntents.GuildMessages | DiscordIntents.Guilds }); DiscordChannel channel = null; var commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { configJson.Prefix } }); commands.RegisterCommands <FirstModule>(); var endpoint = new ConnectionEndpoint { Hostname = "127.0.0.1", Port = 5688 }; var lavalinkConfig = new LavalinkConfiguration { Password = "******", RestEndpoint = endpoint, SocketEndpoint = endpoint }; var lavalink = discord.UseLavalink(); //below are the event listeners discord.VoiceStateUpdated += async(s, e) => //when someone joins leaves or moves voice channel { DiscordGuild guild = e.Guild; DiscordChannel currentChannel = e.Channel; string user = e.User.Username; if (user.Equals("User")) { await discord.SendMessageAsync(await discord.GetChannelAsync(803584072572076055), $"{user }alert ", true); } }; discord.MessageCreated += async(s, e) => { String user = e.Author.Username; //await e.Message.ModifyAsync("ahhhh another one."); if (e.Message.Content.ToLower().Contains("fiddlesticks")) { await e.Message.DeleteAsync(); await e.Message.RespondAsync($"Wuh-oh {user}, we don't use that kinda language 'round these parts."); } if (e.Author.Username.Equals("User") && e.Message.Content.ToLowerInvariant().Contains("lol")) { await e.Message.RespondAsync($"It probably wasn't that funny {user}, chill out"); } }; await discord.ConnectAsync(); await lavalink.ConnectAsync(lavalinkConfig); await Task.Delay(-1); }
public async Task EnqueueTrack(CommandContext ctx, [RemainingText] string input) { _Cmdctx = ctx; if (ctx.Member?.VoiceState?.Channel == null) { await ctx.RespondAsync("You must be connected to a voice channel to use this command."); return; } var lavaconfig = new LavalinkConfiguration { Password = "" }; if (_LavalinkNode == null || !_LavalinkNode.IsConnected) { _LavalinkNode = await _Lavalink.ConnectAsync(lavaconfig); } if ((_LavalinkGuild == null || !_LavalinkGuild.IsConnected)) { _LavalinkGuild = await _LavalinkNode.ConnectAsync(ctx.Member.VoiceState.Channel); _LavalinkGuild.PlaybackFinished += PlayTrack; } var tracks = new LavalinkLoadResult(); Uri uri; if (Uri.TryCreate(input, UriKind.Absolute, out uri)) { tracks = await _LavalinkNode.GetTracksAsync(uri); } else { tracks = await _LavalinkNode.GetTracksAsync(input); } switch (tracks.LoadResultType) { case LavalinkLoadResultType.SearchResult: case LavalinkLoadResultType.TrackLoaded: _Playlist.Enqueue(tracks.Tracks.First()); await ctx.RespondAsync($"**[Enqueued]** {tracks.Tracks.First().Title}"); break; case LavalinkLoadResultType.PlaylistLoaded: foreach (LavalinkTrack track in tracks.Tracks) { _Playlist.Enqueue(track); } await ctx.RespondAsync($"Playlist Loaded."); break; case LavalinkLoadResultType.LoadFailed: await ctx.RespondAsync($"Track could not be loaded."); break; case LavalinkLoadResultType.NoMatches: await ctx.RespondAsync($"Track Not Found."); break; default: await ctx.RespondAsync($"Error."); return; } if (string.IsNullOrEmpty(_LavalinkGuild.CurrentState.CurrentTrack.Title)) { _LavalinkGuild.Play(_Playlist.Peek()); } }
public async Task RunAsync() { #region Abrindo o arquivo de configuração do bot. string projectPath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\")); string configFilePath = $"{projectPath}{ConfigurationManager.AppSettings["ClientConfigJson"]}"; string json = string.Empty; FileStream fileStream = null; try { fileStream = File.OpenRead(configFilePath); using (StreamReader streamReader = new StreamReader(fileStream, new UTF8Encoding(false))) { fileStream = null; json = await streamReader.ReadToEndAsync().ConfigureAwait(false); } } finally { if (fileStream != null) { fileStream.Dispose(); } } configJson = JsonConvert.DeserializeObject <ConfigJson>(json); #endregion #region Configurando o bot com o Token do arquivo de configuração. DiscordConfiguration config = new DiscordConfiguration { Token = configJson.Token, TokenType = TokenType.Bot, AutoReconnect = true, MinimumLogLevel = LogLevel.Debug }; #endregion #region Instanciando o bot(_client) e configurando a extensão de interactividade. Client = new DiscordClient(config); Client.Ready += OnClientReady; Client.UseInteractivity(new InteractivityConfiguration { Timeout = TimeSpan.FromMinutes(2), }); await Client.ConnectAsync(); #endregion #region Inicializando o serviço de audio do _client ConnectionEndpoint endpoint = new ConnectionEndpoint { Hostname = configJson.EndpointHostname, Port = configJson.EndpointPort }; LavalinkConfiguration lavalinkConfig = new LavalinkConfiguration { Password = configJson.LavalinkPassword, RestEndpoint = endpoint, SocketEndpoint = endpoint, }; Lavalink = Client.UseLavalink(); LavalinkNode = await Lavalink.ConnectAsync(lavalinkConfig); #endregion #region Configuração e registro dos comandos do bot. CommandsNextConfiguration commandsConfiguration = new CommandsNextConfiguration() { StringPrefixes = new string[] { configJson.CommandPrefix }, EnableDms = false, EnableMentionPrefix = true, DmHelp = false }; Commands = Client.UseCommandsNext(commandsConfiguration); Commands.RegisterCommands <ModeratorCommands>(); Commands.RegisterCommands <UserCommands>(); #endregion LavalinkNode.PlaybackFinished += Lavalink_PlaybackFinished; await Task.Delay(-1); }