/// <summary> /// Initializes a new instance of the <see cref="IrcProvider"/> class. /// </summary> /// <param name="jobManager">The <see cref="IJobManager"/> for the provider.</param> /// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> to get the <see cref="IAssemblyInformationProvider.VersionString"/> from.</param> /// <param name="asyncDelayer">The value of <see cref="asyncDelayer"/>.</param> /// <param name="logger">The <see cref="ILogger"/> for the <see cref="Provider"/>.</param> /// <param name="chatBot">The <see cref="Models.ChatBot"/> for the <see cref="Provider"/>.</param> public IrcProvider( IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, IAsyncDelayer asyncDelayer, ILogger <IrcProvider> logger, Models.ChatBot chatBot) : base(jobManager, logger, chatBot) { if (assemblyInformationProvider == null) { throw new ArgumentNullException(nameof(assemblyInformationProvider)); } this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer)); var builder = chatBot.CreateConnectionStringBuilder(); if (builder == null || !builder.Valid || !(builder is IrcConnectionStringBuilder ircBuilder)) { throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!"); } address = ircBuilder.Address; port = ircBuilder.Port.Value; nickname = ircBuilder.Nickname; password = ircBuilder.Password; passwordType = ircBuilder.PasswordType; client = new IrcFeatures { SupportNonRfc = true, CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...", AutoRejoin = true, AutoRejoinOnKick = true, AutoRelogin = true, AutoRetry = true, AutoRetryLimit = TimeoutSeconds, AutoRetryDelay = TimeoutSeconds, ActiveChannelSyncing = true, AutoNickHandling = true, CtcpVersion = assemblyInformationProvider.VersionString, UseSsl = ircBuilder.UseSsl.Value, }; if (ircBuilder.UseSsl.Value) { client.ValidateServerCertificate = true; // dunno if it defaults to that or what } client.OnChannelMessage += Client_OnChannelMessage; client.OnQueryMessage += Client_OnQueryMessage; channelIdMap = new Dictionary <ulong, string>(); queryChannelIdMap = new Dictionary <ulong, string>(); channelIdCounter = 1; }
/// <summary> /// Construct a <see cref="DiscordProvider"/> /// </summary> /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="Provider"/>.</param> /// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param> /// <param name="logger">The <see cref="ILogger"/> for the <see cref="Provider"/>.</param> /// <param name="chatBot">The <see cref="ChatBot"/> for the <see cref="Provider"/>.</param> public DiscordProvider( IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, ILogger <DiscordProvider> logger, Models.ChatBot chatBot) : base(jobManager, logger, chatBot) { this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); client = new DiscordSocketClient(); client.MessageReceived += Client_MessageReceived; mappedChannels = new List <ulong>(); }
/// <summary> /// Construct a <see cref="DiscordProvider"/> /// </summary> /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="Provider"/>.</param> /// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param> /// <param name="logger">The <see cref="ILogger"/> for the <see cref="Provider"/>.</param> /// <param name="chatBot">The <see cref="ChatBot"/> for the <see cref="Provider"/>.</param> public DiscordProvider( IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, ILogger <DiscordProvider> logger, Models.ChatBot chatBot) : base(jobManager, logger, chatBot) { this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); var csb = new DiscordConnectionStringBuilder(chatBot.ConnectionString); botToken = csb.BotToken; basedMeme = csb.BasedMeme; outputDisplayType = csb.DMOutputDisplay; client = new DiscordSocketClient(); client.MessageReceived += Client_MessageReceived; mappedChannels = new List <ulong>(); }
/// <inheritdoc /> public IProvider CreateProvider(Models.ChatBot settings) { if (settings == null) { throw new ArgumentNullException(nameof(settings)); } return(settings.Provider switch { ChatProvider.Irc => new IrcProvider( jobManager, assemblyInformationProvider, asyncDelayer, loggerFactory.CreateLogger <IrcProvider>(), settings), ChatProvider.Discord => new DiscordProvider( jobManager, assemblyInformationProvider, loggerFactory.CreateLogger <DiscordProvider>(), settings), _ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider)), });
public async Task <IActionResult> Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (String.IsNullOrWhiteSpace(model.Name)) { return(BadRequest(new ErrorMessage { Message = "name cannot be null or whitespace!" })); } if (String.IsNullOrWhiteSpace(model.ConnectionString)) { return(BadRequest(new ErrorMessage { Message = "connection_string cannot be null or whitespace!" })); } if (!model.Provider.HasValue) { return(BadRequest(new ErrorMessage { Message = "provider cannot be null!" })); } switch (model.Provider) { case ChatProvider.Discord: case ChatProvider.Irc: break; default: return(BadRequest(new ErrorMessage { Message = "Invalid provider!" })); } if (model.ReconnectionInterval == 0) { return(BadRequest(new ErrorMessage { Message = "ReconnectionInterval must not be zero!" })); } if (!model.ValidateProviderChannelTypes()) { return(BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" })); } model.Enabled = model.Enabled ?? false; model.ReconnectionInterval = model.ReconnectionInterval ?? 1; // try to update das db first var dbModel = new Models.ChatBot { Name = model.Name, ConnectionString = model.ConnectionString, Enabled = model.Enabled, Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List <Models.ChatChannel>(), // important that this isn't null InstanceId = Instance.Id, Provider = model.Provider, ReconnectionInterval = model.ReconnectionInterval }; DatabaseContext.ChatBots.Add(dbModel); await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); try { try { // try to create it var instance = instanceManager.GetInstance(Instance); await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false); if (dbModel.Channels.Count > 0) { await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false); } } catch { // undo the add DatabaseContext.ChatBots.Remove(dbModel); await DatabaseContext.Save(default).ConfigureAwait(false);
public async Task <IActionResult> Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken) { if (model == null) { throw new ArgumentNullException(nameof(model)); } var earlyOut = StandardModelChecks(model, true); if (earlyOut != null) { return(earlyOut); } var countOfExistingBotsInInstance = await DatabaseContext .ChatBots .AsQueryable() .Where(x => x.InstanceId == Instance.Id) .CountAsync(cancellationToken) .ConfigureAwait(false); if (countOfExistingBotsInInstance >= Instance.ChatBotLimit.Value) { return(Conflict(new ErrorMessage(ErrorCode.ChatBotMax))); } model.Enabled ??= false; model.ReconnectionInterval ??= 1; // try to update das db first var dbModel = new Models.ChatBot { Name = model.Name, ConnectionString = model.ConnectionString, Enabled = model.Enabled, Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List <Models.ChatChannel>(), // important that this isn't null InstanceId = Instance.Id, Provider = model.Provider, ReconnectionInterval = model.ReconnectionInterval, ChannelLimit = model.ChannelLimit }; DatabaseContext.ChatBots.Add(dbModel); await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); return(await WithComponentInstance( async instance => { try { // try to create it await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false); if (dbModel.Channels.Count > 0) { await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false); } } catch { // undo the add DatabaseContext.ChatBots.Remove(dbModel); // DCTx2: Operations must always run await DatabaseContext.Save(default).ConfigureAwait(false);
/// <inheritdoc /> public async Task ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken) { if (newSettings == null) { throw new ArgumentNullException(nameof(newSettings)); } logger.LogTrace("ChangeSettings..."); IProvider provider; async Task DisconnectProvider(IProvider p) { try { await p.Disconnect(cancellationToken).ConfigureAwait(false); } finally { await p.DisposeAsync().ConfigureAwait(false); } } Task disconnectTask; lock (providers) { // raw settings changes forces a rebuild of the provider if (providers.TryGetValue(newSettings.Id.Value, out provider)) { providers.Remove(newSettings.Id.Value); disconnectTask = DisconnectProvider(provider); } else { disconnectTask = Task.CompletedTask; } if (newSettings.Enabled.Value) { provider = providerFactory.CreateProvider(newSettings); providers.Add(newSettings.Id.Value, provider); } } lock (mappedChannels) foreach (var oldMappedChannelId in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList()) { mappedChannels.Remove(oldMappedChannelId); } await disconnectTask.ConfigureAwait(false); lock (synchronizationLock) { // same thread shennanigans var oldOne = connectionsUpdated; connectionsUpdated = new TaskCompletionSource <object>(); oldOne.SetResult(null); } var reconnectionUpdateTask = provider?.SetReconnectInterval( newSettings.ReconnectionInterval.Value, newSettings.Enabled.Value) ?? Task.CompletedTask; lock (activeChatBots) { var originalChatBot = activeChatBots.FirstOrDefault(bot => bot.Id == newSettings.Id); if (originalChatBot != null) { activeChatBots.Remove(originalChatBot); } activeChatBots.Add(new Models.ChatBot { Id = newSettings.Id, ConnectionString = newSettings.ConnectionString, Enabled = newSettings.Enabled, Name = newSettings.Name, ReconnectionInterval = newSettings.ReconnectionInterval, Provider = newSettings.Provider, }); } await reconnectionUpdateTask.ConfigureAwait(false); }