示例#1
0
        public async Task Run(DiscordRequest request, Contexts contexts)
        {
            if (this._controllersContainer == null)
            {
                this.LoadControllers();
            }

            using (LogContext.PushProperty("MessageId", Guid.NewGuid()))
                using (LogContext.PushProperty("Request", request))
                    using (LogContext.PushProperty("Contexts", contexts))
                    {
                        var readAlwaysMethods = this._controllersContainer.WithReadAlways;
                        var readAlwaysTask    = Task.Run(() => RunMethods(request, contexts, readAlwaysMethods, true));

                        Task commandsTask = null;
                        if (request.IsCommandForBot)
                        {
                            var discordCommandMethods = this._controllersContainer.WithDiscordCommand;
                            commandsTask = Task.Run(() => RunMethods(request, contexts, discordCommandMethods, false));
                        }

                        // ReadAlwaysMethods should be first in throwing exception, bcs every ReadAlways exception is Error
                        await readAlwaysTask;
                        if (commandsTask != null)
                        {
                            await commandsTask;
                        }
                    }
        }
示例#2
0
        public MuteEvent GetMuteEvent(ulong userId, Contexts contexts, DiscordRequest request)
        {
            var reason    = _request.Arguments.FirstOrDefault(x => x.Name == "reason" || x.Name == "r")?.Value;
            var timeRange = request.GetFutureTimeRange(defaultTime: TimeSpan.FromHours(1));

            return(new MuteEvent(userId, timeRange, reason, contexts.Server.Id));
        }
示例#3
0
        private void RunMethods(DiscordRequest request, Contexts contexts, IEnumerable <ControllerInfo> controllers, bool isReadAlways)
        {
            var tasks = new List <Task>();

            foreach (var controllerInfo in controllers)
            {
                using (LogContext.PushProperty("Controller", controllerInfo.Controller.GetType().Name))
                {
                    foreach (var method in controllerInfo.Methods)
                    {
                        if (isReadAlways)
                        {
                            var task = InvokeMethod(request, contexts, controllerInfo, method);
                            tasks.Add(task);
                            continue;
                        }

                        var command = method.GetAttributeInstances <DiscordCommand>();
                        if (IsMatchedCommand(command, request) && IsValid(contexts, method))
                        {
                            var task = InvokeMethod(request, contexts, controllerInfo, method);
                            tasks.Add(task);
                        }
                    }
                }
            }
            Task.WaitAll(tasks.ToArray());
        }
示例#4
0
        public async Task UnmuteUserAsync(DiscordRequest request, Contexts contexts)
        {
            var requestParser = new MuteRequestParser(request, this._usersService, contexts);
            var userToUnmute  = requestParser.GetUser();

            await this._unmutingService.UnmuteNow(contexts, userToUnmute);
        }
示例#5
0
        public object GetValueByName(string key, bool isList, DiscordRequest request, BotCommandTemplate template)
        {
            var result = request.Arguments.FirstOrDefault(a => a.Name.ToLowerInvariant() == key.ToLowerInvariant());

            if (result == null)
            {
                return(null);
            }
            var argType = template.Properties.FirstOrDefault(x => x.Name.ToLowerInvariant() == key.ToLowerInvariant())
                          ?.Type;

            if (argType.HasValue && argType.Value == BotCommandPropertyType.Bool)
            {
                return(result.Value ?? bool.TrueString);
            }
            if (!isList)
            {
                return(result.Value);
            }
            var argumentsList = request.Arguments.ToList();
            var indexOf       = argumentsList.IndexOf(result);
            var nextResults   = argumentsList.Skip(indexOf + 1).TakeWhile(x => x.Name == null);
            var list          = new List <string> {
                result.Value
            };

            list.AddRange(nextResults.Select(x => x.Value));
            return(list);
        }
示例#6
0
        public object GetValueByName(string key, bool isList, DiscordRequest request, BotCommandTemplate template)
        {
            var argType = template.Properties.First(x => x.Name.ToLowerInvariant() == key.ToLowerInvariant()).Type;
            var result  = request.Arguments.FirstOrDefault(a => a.Name?.ToLowerInvariant() == key.ToLowerInvariant());

            if (argType == BotCommandPropertyType.Bool)
            {
                return(result == null ? bool.FalseString : bool.TrueString);
            }
            if (result == null)
            {
                return(null);
            }
            if (argType == BotCommandPropertyType.Text)
            {
                return(result.Value.Trim('\"'));
            }
            if (!isList)
            {
                return(result.Value);
            }
            var indexOf = request.Arguments.ToList().IndexOf(result);
            var list    = request.Arguments
                          .Skip(indexOf)
                          .TakeWhile(x => x.Name == result.Name || x.Name == null)
                          .Select(x => x.Value)
                          .ToList();

            return(list);
        }
示例#7
0
        public async Task AddResponse(DiscordRequest request, Contexts contexts)
        {
            var messageService = _messagesServiceFactory.Create(contexts);
            var onEvent        = request.Arguments.FirstOrDefault(x => x.Name?.ToLowerInvariant() == "onevent")?.Value;
            var message        = request.Arguments.FirstOrDefault(x => x.Name?.ToLowerInvariant() == "message")?.Value;

            if (onEvent == null || message == null)
            {
                await messageService.SendResponse(x => x.NotEnoughArguments(), contexts);

                return;
            }
            var response = await _responsesService.GetResponseByOnEvent(onEvent);

            if (response == null)
            {
                await messageService.SendResponse(x => x.ResponseNotFound(contexts, onEvent), contexts);

                return;
            }
            var responseForThisServer = await _responsesService.GetResponseByOnEvent(onEvent, contexts.Server.Id);

            if (responseForThisServer != null)
            {
                await messageService.SendResponse(x => x.ResponseAlreadyExists(contexts, onEvent), contexts);

                return;
            }
            await _responsesService.AddResponse(onEvent, message, contexts.Server.Id);

            await messageService.SendResponse(x => x.ResponseHasBeenAdded(contexts, onEvent), contexts);
        }
示例#8
0
        private void RunMethodsIBotCommand(DiscordRequest request, Contexts contexts, IEnumerable <ControllerInfo> controllers, bool isReadAlways)
        {
            var tasks = new List <Task>();

            foreach (var controllerInfo in controllers)
            {
                using (LogContext.PushProperty("Controller", controllerInfo.Controller.GetType().Name))
                {
                    foreach (var method in controllerInfo.Methods)
                    {
                        if (!IsValid(contexts, method))
                        {
                            continue;
                        }

                        var commandInParameterType = method.GetParameters().First(x => typeof(IBotCommand).IsAssignableFrom(x.ParameterType)).ParameterType;
                        var template = this._botCommandsService.GetCommandTemplate(commandInParameterType);
                        if (!this._botCommandsService.IsMatchedWithCommand(request, template))
                        {
                            continue;
                        }
                        var command = this._botCommandsService.ParseRequestToCommand(commandInParameterType, request, template);
                        var task    = InvokeMethod(command, contexts, controllerInfo, method);
                        tasks.Add(task);
                    }
                }
            }
            Task.WaitAll(tasks.ToArray());
        }
示例#9
0
        public async IAsyncEnumerable <Message> GetMessages(DiscordServerContext server, ChannelContext channel, int limit, ulong fromMessageId = 0, bool goBefore = true)
        {
            var textChannel = (ITextChannel)this.GetChannel(channel.Id, server.Id);

            if (!await this.CanBotReadTheChannelAsync(textChannel))
            {
                yield break;
            }
            var channelMessages = fromMessageId == 0
                ? textChannel.GetMessagesAsync(limit)
                : textChannel.GetMessagesAsync(fromMessageId, goBefore ? Direction.Before : Direction.After, limit);

            await foreach (var message in channelMessages.Flatten())
            {
                var user     = this._userContextsFactory.Create(message.Author);
                var contexts = new Contexts(server, channel, user);

                DiscordRequest request;
                try
                {
                    request = this._commandParser.Parse(message.Content, message.Timestamp.UtcDateTime);
                }
                catch // should almost never go to catch block, but in rare cases Parse() can throw an exception
                {
                    request = new DiscordRequest
                    {
                        OriginalMessage = message.Content,
                        SentAt          = message.Timestamp.UtcDateTime
                    };
                }
                yield return(new Message(message.Id, request, contexts));
            }
        }
示例#10
0
        public async Task SetRoleAsSafe(DiscordRequest request, Contexts contexts)
        {
            var args = request.Arguments.Skip(1).ToArray(); // 1 args is string "role", so it's not needed

            if (args.Length < 2)
            {
                throw new NotEnoughArgumentsException();
            }

            var roleName    = args[0].Value;
            var toSetAsSafe = args[1].Value == "safe";

            var serverRole = _usersRolesService.GetRoleByName(roleName, contexts.Server);

            if (serverRole == null)
            {
                throw new RoleNotFoundException(roleName);
            }

            if (toSetAsSafe)
            {
                var command = new SetRoleAsSafeCommand(roleName, contexts.Server.Id);
                await _commandBus.ExecuteAsync(command);
            }
            else
            {
                var command = new SetRoleAsUnsafeCommand(roleName, contexts.Server.Id);
                await _commandBus.ExecuteAsync(command);
            }

            var messagesService = _messagesServiceFactory.Create(contexts);
            await messagesService.SendResponse(x => x.RoleSettingsChanged(roleName), contexts);
        }
示例#11
0
        public async Task GetStatisticsPerPeriod(DiscordRequest request, Contexts contexts)
        {
            var period = _reportsService.SelectPeriod(request.Arguments.FirstOrDefault()?.Value);

            if (implementedBySplitter.Contains(period))
            {
                var query  = new GetMessagesStatisticsQuery(period);
                var result = await this._queryBus.ExecuteAsync(query);

                var periodStats = result.PeriodStatistics.Where(x => x.Count > 0); // todo
                return;
            }

            var getMessages = new GetMessagesQuery(contexts.Server.Id);
            var messages    = this._queryBus.Execute(getMessages).Messages.ToList();
            var report      = _reportsService.CreateReport(messages, period);

            Log.Information("Generated statistics for time range {start} {end}", report.TimeRange.Start, report.TimeRange.End);
#if DEBUG
            PrintDebugStats(report);
#endif
            var path            = _chartsService.GetImageStatisticsPerPeriod(report);
            var messagesService = _messagesServiceFactory.Create(contexts);
            await messagesService.SendFile(path);
        }
示例#12
0
        public SpamProbability GetOverallSpamProbability(DiscordRequest request, Contexts contexts)
        {
            var probabilities = this._spamDetectors
                                .Select(x => x.GetSpamProbability(this._serverMessagesCacheService, request, contexts))
                                .Where(x => x != SpamProbability.None)
                                .ToList();

            if (probabilities.Count == 0)
            {
                return(SpamProbability.None);
            }
            if (probabilities.Contains(SpamProbability.Sure))
            {
                return(SpamProbability.Sure);
            }
            if (probabilities.Count(x => x == SpamProbability.Medium) > 1)
            {
                return(SpamProbability.Sure);
            }
            if (probabilities.Contains(SpamProbability.Medium))
            {
                return(SpamProbability.Medium);
            }
            return(SpamProbability.Low);
        }
示例#13
0
        private async Task RunMethodsIBotCommand(DiscordRequest request, Contexts contexts, IEnumerable <ControllerInfo> controllers)
        {
            if (!request.IsCommandForBot)
            {
                return;
            }

            foreach (var controllerInfo in controllers)
            {
                using (LogContext.PushProperty("Controller", controllerInfo.Controller.GetType().Name))
                {
                    foreach (var method in controllerInfo.Methods)
                    {
                        var commandInParameterType = method.GetParameters().First(x => typeof(IBotCommand).IsAssignableFrom(x.ParameterType)).ParameterType;
                        //TODO zoptymalizować, spokojnie można to pobierać wcześniej i używać raz, zamiast wszystko obliczać przy każdym odpaleniu
                        var template      = this._botCommandsService.GetCommandTemplate(commandInParameterType);
                        var customCommand = await this._commandsContainer.GetCommand(request, commandInParameterType, contexts.Server.Id);

                        var isCommandMatchedWithCustom         = customCommand != null;
                        var isThereDefaultCommandWithGivenName = request.Name.ToLowerInvariant() == template.NormalizedCommandName;
                        if (!isCommandMatchedWithCustom && !isThereDefaultCommandWithGivenName)
                        {
                            continue;
                        }
                        if (!this.IsValid(contexts, method))
                        {
                            return;
                        }
                        var command = this.CreateBotCommand(isThereDefaultCommandWithGivenName, template, commandInParameterType, request, customCommand?.Template, isCommandMatchedWithCustom);
                        await InvokeMethod(command, contexts, controllerInfo, method);
                    }
                }
            }
        }
 private bool ShouldCheckThisMessage(ulong userId, DiscordRequest request)
 {
     if (_isNowChecking || request.IsCommandForBot) // interactions with bot shouldn't be considered as spam
     {
         return(false);
     }
     return(!_lastUserPunishmentDate.TryGetValue(userId, out var time) || time < request.SentAt.AddSeconds(-5));
 }
示例#15
0
        [DiscordCommand("remove role")] //todo
        public void RemoveRole(DiscordRequest request, Contexts contexts)
        {
            var commandRole     = request.OriginalMessage.ToLowerInvariant().Replace("-remove role ", string.Empty); //TODO use DiscordRequest properties
            var safeRoles       = this._queryBus.Execute(new GetDiscordServerSafeRolesQuery(contexts.Server.Id)).SafeRoles;
            var messagesService = _messagesServiceFactory.Create(contexts);

            _rolesService.DeleteRoleFromUser(safeRoles, messagesService, contexts, commandRole);
        }
示例#16
0
        public (DiscordRequest request, Contexts contexts) CreateRequestAndContexts(string content)
        {
            var request = new DiscordRequest {
                OriginalMessage = content
            };
            var contexts = GetDefaultContexts();

            return(request, contexts);
        }
示例#17
0
 private bool IsMatchedCommand(IEnumerable <DiscordCommand> commands, DiscordRequest request)
 {
     return(commands.Any(x =>
     {
         var withoutPrefix = request.OriginalMessage.CutStart(request.Prefix);
         return withoutPrefix.StartsWith(x.Command) &&
         (withoutPrefix.Length == x.Command.Length || withoutPrefix[x.Command.Length] == ' ');
     }));
 }
示例#18
0
        public async Task MuteUser(DiscordRequest request, Contexts contexts)
        {
            var requestParser = new MuteRequestParser(request, _usersService, contexts);
            var userToMute    = requestParser.GetUser();
            var muteEvent     = requestParser.GetMuteEvent(userToMute.Id, contexts, request);

            await _muteService.MuteUserOrOverwrite(contexts, muteEvent, userToMute);

            _muteService.UnmuteInFuture(contexts, muteEvent, userToMute);
        }
示例#19
0
        private static TimeSpan ParseToTimeSpan(DiscordRequest discordRequest, TimeSpan defaultTime)
        {
            var timeAsString = discordRequest.Arguments.FirstOrDefault(x => x.Name == "t" || x.Name == "time")?.Value;

            if (string.IsNullOrWhiteSpace(timeAsString))
            {
                return(defaultTime);
            }

            var lastChar = timeAsString[^ 1];
示例#20
0
        public static string GetMention(this DiscordRequest discordRequest)
        {
            var mention = discordRequest.Arguments.FirstOrDefault(x => x.Value.StartsWith('<') && x.Value.EndsWith('>'))?.Value;

            if (mention == null)
            {
                throw new UserDidntMentionAnyUser();
            }
            return(mention);
        }
示例#21
0
        public async Task Responses(DiscordRequest request, Contexts contexts)
        {
            var argument = request.Arguments?.FirstOrDefault()?.Name?.ToLowerInvariant();

            if (!this._possibleArguments.Contains(argument))
            {
                argument = "all";
            }
            await this._responsesMessageService.PrintResponses(argument, contexts);
        }
示例#22
0
        private async void MessageReceived(SocketMessage socketMessage)
        {
            if (this.ShouldIgnoreMessage(socketMessage))
            {
                return;
            }

            Contexts contexts;

            try
            {
                contexts = this.GetContexts(socketMessage);
            }
            catch (Exception e)
            {
                Log.Error(e, e.StackTrace);
                return;
            }

            DiscordRequest request;

            try
            {
                request = this.ParseRequest(socketMessage);
            }
            catch (Exception e)
            {
                Log.Error(e, e.StackTrace);
                this.OnWorkflowException.ForEach(x => x.Invoke(e, contexts));
                request = new DiscordRequest
                {
                    OriginalMessage = socketMessage.Content,
                    SentAt          = socketMessage.Timestamp.UtcDateTime
                };
            }
            try
            {
                Log.Information("Starting controllers");
                await this._controllersService.Run(socketMessage.Id, request, contexts);
            }
            catch (Exception e)
            {
                Log.Error(e, e.StackTrace);
                this.OnWorkflowException.ForEach(x => x.Invoke(e, contexts));
            }
            var elapsedRun         = this._stopWatch.ElapsedTicks;
            var elapsedMiliseconds = this._stopWatch.ElapsedMilliseconds;

            Log.Information("_controllersService.Run time {elapsedRun}ticks (ms: {miliseconds})", elapsedRun, elapsedMiliseconds);
#if DEBUG
            await socketMessage.Channel.SendMessageAsync($"```Run time: {elapsedRun}ticks (ms: {elapsedMiliseconds})```");
#endif
            this._stopWatch.Stop();
            this._stopWatch.Reset();
        }
示例#23
0
        public MuteEvent GetMuteEvent(ulong userId, Contexts contexts, DiscordRequest request)
        {
            var reason = this._request.Arguments.FirstOrDefault(x => x.Name == "reason" || x.Name == "r")?.Value;

            if (string.IsNullOrWhiteSpace(reason))
            {
                throw new NotEnoughArgumentsException();
            }
            var timeRange = request.GetFutureTimeRange(defaultTime: TimeSpan.FromHours(1));

            return(new MuteEvent(userId, timeRange, reason, contexts.Server.Id, contexts.Channel.Id));
        }
示例#24
0
 private async Task TryToAwaitTask(Task task, DiscordRequest request = null, Contexts sendExceptionsContexts = null)
 {
     try
     {
         await task;
     }
     catch (Exception e)
     {
         Log.Error(e, e.StackTrace);
         this.OnWorkflowException.ForEach(x => x.Invoke(e, request, sendExceptionsContexts));
     }
 }
示例#25
0
        public Task SaveMessageAsync(DiscordRequest request, Contexts contexts)
        {
            Log.Information("Started saving the message");
            var command = new AddMessageCommand(request.OriginalMessage,
                                                contexts.User.Id, contexts.User.Name,
                                                contexts.Channel.Id, contexts.Channel.Name,
                                                contexts.Server.Id, contexts.Server.Name,
                                                request.SentAt);

            Log.Information("Command created");
            return(this._commandBus.ExecuteAsync(command).ContinueWith(x => Log.Information("Message saved")));
        }
示例#26
0
        public void GetAvatar(DiscordRequest request, Contexts contexts)
        {
            var messageService = _messagesServiceFactory.Create(contexts);

            if (string.IsNullOrEmpty(contexts.User.AvatarUrl))
            {
                messageService.SendResponse(x => x.UserDoesntHaveAvatar(contexts.User), contexts);
                return;
            }

            messageService.SendMessage(contexts.User.AvatarUrl);
        }
示例#27
0
        public IBotCommand ParseRequestToCommand(Type commandType, DiscordRequest request, BotCommandTemplate template)
        {
            var instance = Activator.CreateInstance(commandType);

            foreach (var property in commandType.GetProperties())
            {
                var value         = request.Arguments.First(x => x.Name.ToLowerInvariant() == property.Name.ToLowerInvariant()).Value;
                var propertyType  = template.Properties.First(x => x.Name == property.Name).Type;
                var convertedType = botCommandPropertyConversionService.ConvertType(value, propertyType);
                property.SetValue(instance, convertedType);
            }
            return((IBotCommand)instance);
        }
示例#28
0
        public async Task <CustomCommand> GetCommand(DiscordRequest request, Type botCommand, ulong serverId)
        {
            await this.TryRefresh();

            if (!this._customCommandsGroupedByBotCommand.ContainsKey(serverId))
            {
                return(null);
            }
            var serverCommands = this._customCommandsGroupedByBotCommand[serverId];
            var command        = serverCommands.FirstOrDefault(x => x.ExpectedBotCommandName == botCommand.FullName && x.Template.IsMatch(request.OriginalMessage));

            return(command);
        }
示例#29
0
        private static Task InvokeMethod(DiscordRequest request, Contexts contexts, ControllerInfo controllerInfo, MethodInfo method)
        {
            Log.Information("Invoke in controller {controller} method {method}", controllerInfo.Controller.GetType().Name, method.Name);

            using (LogContext.PushProperty("Method", method.Name))
            {
                var runningMethod = method.Invoke(controllerInfo.Controller, new object[] { request, contexts });
                if (runningMethod is Task task)
                {
                    return(task);
                }
            }
            return(Task.CompletedTask);
        }
示例#30
0
        public void PrintHelp(DiscordRequest request, Contexts contexts)
        {
            var messagesService = _messagesServiceFactory.Create(contexts);

            if (request.HasArgument(null, "json"))
            {
                var helpMessage = this._helpMessageGenerator.GenerateJsonHelp(contexts);
                messagesService.SendMessage(helpMessage, MessageType.Json);
            }
            else
            {
                var helpMessage = this._helpMessageGenerator.GenerateHelp(contexts);
                messagesService.SendResponse(x => x.PrintHelp(helpMessage), contexts);
            }
        }