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; } } }
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)); }
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()); }
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); }
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); }
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); }
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); }
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()); }
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)); } }
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); }
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); }
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); }
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)); }
[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); }
public (DiscordRequest request, Contexts contexts) CreateRequestAndContexts(string content) { var request = new DiscordRequest { OriginalMessage = content }; var contexts = GetDefaultContexts(); return(request, contexts); }
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] == ' '); })); }
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); }
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];
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); }
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); }
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(); }
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)); }
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)); } }
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"))); }
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); }
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); }
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); }
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); }
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); } }