示例#1
0
        /// <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;
        }
示例#2
0
 /// <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>();
        }
示例#4
0
 /// <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);
示例#6
0
        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);
示例#7
0
        /// <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);
        }