public virtual void Run() { IMessage?message = null; try { _dispatching.Set(true); message = Dequeue(); if (message != null) { _listener?.HandleMessage(message); } } catch (Exception e) { // TODO: Log if (message != null && _deadLettersQueue != null) { _deadLettersQueue.Enqueue(message); } Console.WriteLine($"AsyncMessageQueue: Dispatch to listener has failed because: {e.Message}"); Console.WriteLine(e.StackTrace); } finally { _dispatching.Set(false); _endDispatchingEvent.Set(); } }
public TResponse SendRequest <TRequest, TResponse>(TRequest request) where TRequest : IRequest where TResponse : IResponse { try { IObservable <IMessage> responseObservable = this.Observable.Where(( IMessage message ) => { return(message.Id.Equals(request.Id)); }); IObservable <IMessage> timeoutObservable = responseObservable.Timeout(this.MessageRoundTripTimeout); IObservable <IMessage> waitObservable = timeoutObservable.FirstOrDefaultAsync(); this.SendMessage(request); IMessage?response = waitObservable.Wait(); if (response is null) { throw Assert.Exception(new MessageTransmissionException($"Sending of '{ request.Name }' with id '{ request.Id }' received no response.")); } return(( TResponse )(response)); }catch (TimeoutException ex) { throw Assert.Exception(new MessageTransmissionException($"Sending of '{ request.Name }' with id '{ request.Id }' timed out.", ex)); }catch (Exception ex) when(ex is not MessageTransmissionException) { throw Assert.Exception(new MessageTransmissionException($"Sending of '{ request.Name }' with id '{ request.Id }' failed.", ex)); } }
/// <summary> /// Initializes a new instance of the <see cref="GrpcAgentResult"/> class that is considered successful. /// </summary> /// <param name="status">The <see cref="Status"/>.</param> /// <param name="trailers">The trailers <see cref="Metadata"/>.</param> /// <param name="request">The gRPC request value.</param> /// <param name="response">The gRPC response value (optional).</param> public GrpcAgentResult(Status status, Metadata trailers, IMessage?request, object?response = null) { IsSuccess = true; Status = status; ResponseTrailers = trailers; Request = request; Response = response; }
public async Task <RuntimeResult> IncludePreviousMessagesAsync ( [RequireEntityOwnerOrPermission(typeof(EditRoleplay), PermissionTarget.Other)] Roleplay roleplay, [OverrideTypeReader(typeof(UncachedMessageTypeReader <IMessage>))] IMessage startMessage, [OverrideTypeReader(typeof(UncachedMessageTypeReader <IMessage>))] IMessage?finalMessage = null ) { finalMessage ??= this.Context.Message; if (startMessage.Channel != finalMessage.Channel) { return(RuntimeCommandResult.FromError("The messages are not in the same channel.")); } var addedOrUpdatedMessageCount = 0; var latestMessage = startMessage; while (latestMessage.Timestamp < finalMessage.Timestamp) { var messages = (await this.Context.Channel.GetMessagesAsync ( latestMessage, Direction.After ).FlattenAsync()).OrderBy(m => m.Timestamp).ToList(); latestMessage = messages.Last(); foreach (var message in messages) { // Jump out if we've passed the final message if (message.Timestamp > finalMessage.Timestamp) { break; } if (!(message is IUserMessage userMessage)) { continue; } var modifyResult = await _discordRoleplays.ConsumeMessageAsync(userMessage); if (modifyResult.IsSuccess) { ++addedOrUpdatedMessageCount; } } } return(RuntimeCommandResult.FromSuccess ( $"{addedOrUpdatedMessageCount} messages added to \"{roleplay.Name}\"." )); }
public static Task SafeDelete(this IMessage?message) { try { return(message?.DeleteAsync()); } catch (Exception) { return(Task.CompletedTask); } }
private async Task TryTrackMessageAsync( IGuild?guild, IMessage?message, Func <ulong, Task> asyncTrackAction, CancellationToken cancellationToken) { if (guild is null) { MessageLogMessages.IgnoringNonGuildMessage(_logger); return; } if (message is { })
private async Task TryLogAsync( ISocketGuild?guild, IMessage?oldMessage, IMessage?newMessage, IISocketMessageChannel channel, Func <Embed> renderLogMessage, CancellationToken cancellationToken) { if (guild is null) { MessageLoggingLogMessages.IgnoringNonGuildMessage(_logger); return; } // I.E. we have content for both messages and can see for sure it hasn't changed, E.G. Embed changes if ((oldMessage?.Content == newMessage?.Content) && (oldMessage is { }) &&
public Task BindModelAsync(ModelBindingContext bindingContext) { byte[]? data = bindingContext.HttpContext.GetData(); if (data == null) { return(Task.CompletedTask); } Type type = bindingContext.ModelType; IMessage?message = data.ToObject(type); if (message == null) { return(Task.CompletedTask); } bindingContext.Result = ModelBindingResult.Success(message); return(Task.CompletedTask); }
/// <summary> /// Initializes a new instance of the <see cref="GrpcAgentResult"/> class that is considered unsuccessful. /// </summary> /// <param name="rex">The <see cref="RpcException"/>.</param> /// <param name="request">The gRPC request value.</param> public GrpcAgentResult(RpcException rex, IMessage?request) { Exception = Check.NotNull(rex, nameof(rex)); Status = Exception.Status; ErrorMessage = Exception.Status.Detail; ResponseTrailers = Exception.Trailers; Request = request; if (ResponseTrailers.Count > 0) { var t = ResponseTrailers.Where(x => x.Key == GrpcConsts.ErrorTypeHeaderName).SingleOrDefault(); if (System.Enum.TryParse <ErrorType>(t.Value, out var et)) { ErrorType = et; } } IsSuccess = false; }
/// <inheritdoc /> public bool Equals(IMessage?other) { return(other is not null && string.Equals(Id, other.Id, StringComparison.Ordinal) && string.Equals(Body, other.Body, StringComparison.Ordinal) && string.Equals(ChatId, other.ChatId, StringComparison.Ordinal) && string.Equals(Author, other.Author, StringComparison.Ordinal) && string.Equals(SenderName, other.SenderName, StringComparison.Ordinal) && string.Equals(QuotedMessageId, other.QuotedMessageId, StringComparison.Ordinal) && string.Equals(QuotedMessageBody, other.QuotedMessageBody, StringComparison.Ordinal) && Type == other.Type && FromMe == other.FromMe && Time == other.Time && MessageNumber == other.MessageNumber && Self == other.Self && IsForwarded == other.IsForwarded && QuotedMessageId == other.QuotedMessageId && QuotedMessageType == other.QuotedMessageType && ChatName == other.ChatName); }
/// <summary> /// Ensures the parameters for executing stray methods are valid /// </summary> /// <param name="info">The method info</param> /// <param name="user">The user executing the method</param> /// <param name="channel">The channel the method is executing for</param> /// <param name="message">The message the method is executing for</param> /// <returns>An array of all of the parameters for the method</returns> public object?[] Parameters(MethodInfo info, IUser user, IChannel channel, IMessage?message) { var paras = info.GetParameters(); if (paras.Length == 0) { return(Array.Empty <object>()); } var outPars = new object?[paras.Length]; for (var i = 0; i < paras.Length; i++) { var par = paras[i]; if (typeof(IUser).IsAssignableFrom(par.ParameterType)) { outPars[i] = user; continue; } if (typeof(IChannel).IsAssignableFrom(par.ParameterType)) { outPars[i] = channel; continue; } if (typeof(IMessage).IsAssignableFrom(par.ParameterType)) { outPars[i] = message; continue; } var inter = _provider.GetService(par.ParameterType); outPars[i] = inter; } return(outPars); }
private async Task HandleNotificationAsync(IMessageDelete notification, IMessage?previousMessage, CancellationToken cancellationToken) { if (!notification.GuildID.HasValue) { return; } var channelId = notification.ChannelID; var isThreadChannel = await _threadSvc.IsThreadChannelAsync(channelId, cancellationToken); if (!isThreadChannel) { return; } var guild = notification.GuildID; using var logScope = MessageLoggingLogMessages.BeginMessageNotificationScope(_logger, guild.Value.Value, notification.ChannelID.Value, notification.ID.Value); MessageLoggingLogMessages.MessageDeletedHandling(_logger); await TryLogAsync( guild, previousMessage?.Author.ID, null, null, notification.ChannelID, () => { var fields = Enumerable.Empty <IEmbedField>() .Concat(FormatMessageContent(previousMessage?.Content ?? "[Message was not in cache]") .EnumerateLongTextAsFieldBuilders("**Content**")) .Append(new EmbedField("Channel ID", notification.ChannelID.Value.ToString(), true)) .Append(new EmbedField("Message ID", notification.ID.Value.ToString(), true)); // ↓ This character is a "wastebasket", don't worry return(new Embed(Description: $"\\🗑️ **Message deleted in <#{notification.ChannelID.Value}>**", Footer: new EmbedFooter("", default, default),
private static async Task <Task> ClearReactionsAfterDelay(ISocketMessageChannel channel) { while (activeHelpEmbeds.Count > 0) { Log.Write("Checking For Inactive Help Windows from total of " + activeHelpEmbeds.Count, "Bot"); DateTime runTime = DateTime.Now; foreach (KeyValuePair <ulong, ActiveHelp> help in activeHelpEmbeds) { if ((runTime - help.Value.LastInteractedWith).TotalSeconds > 20) { IMessage?cmdMessage = null; IMessage message = await channel.GetMessageAsync(help.Key); if (message.Reference != null && message.Reference.MessageId.IsSpecified) { cmdMessage = await channel.GetMessageAsync(message.Reference.MessageId.Value); } await message.DeleteAsync(); if (cmdMessage != null) { await cmdMessage.DeleteAsync(); } activeHelpEmbeds.Remove(help.Key); } } await Task.Delay(15000); } Log.Write("All Help Windows Inactive", "Bot"); return(Task.CompletedTask); }
private async Task <(IMessage?requestMessage, StatusCode statusCode, string?errorMessage)> CreateMessage(HttpRequest request) { IMessage?requestMessage; if (_bodyDescriptor != null) { if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { return(null, StatusCode.InvalidArgument, "Request content-type of application/json is required."); } if (!request.Body.CanSeek) { // JsonParser does synchronous reads. In order to avoid blocking on the stream, we asynchronously // read everything into a buffer, and then seek back to the beginning. request.EnableBuffering(); Debug.Assert(request.Body.CanSeek); await request.Body.DrainAsync(CancellationToken.None); request.Body.Seek(0L, SeekOrigin.Begin); } var encoding = RequestEncoding.SelectCharacterEncoding(request); // TODO: Handle unsupported encoding using (var requestReader = new HttpRequestStreamReader(request.Body, encoding)) { if (_bodyDescriptorRepeated) { var containingMessage = ParseRepeatedContent(requestReader); if (_resolvedBodyFieldDescriptors !.Count > 0) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, _resolvedBodyFieldDescriptors, containingMessage); } else { requestMessage = containingMessage; } } else { IMessage bodyContent; try { bodyContent = JsonParser.Default.Parse(requestReader, _bodyDescriptor); } catch (InvalidJsonException) { return(null, StatusCode.InvalidArgument, "Request JSON payload is not correctly formatted."); } catch (InvalidProtocolBufferException exception) { return(null, StatusCode.InvalidArgument, exception.Message); } if (_bodyFieldDescriptors != null) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, _bodyFieldDescriptors, bodyContent); } else { requestMessage = bodyContent; } } }
/// <summary> /// Invokes the gRPC call with no result asynchronously. /// </summary> /// <param name="func">The <paramref name="func"/> to perform the gRPC call.</param> /// <param name="request">The gRPC request value (for auditing).</param> /// <param name="requestOptions">The optional <see cref="GrpcRequestOptions"/>.</param> /// <param name="memberName">The method or property name of the caller to the method.</param> /// <param name="filePath">The full path of the source file that contains the caller.</param> /// <param name="lineNumber">The line number in the source file at which the method is called.</param> /// <returns>The <see cref="GrpcAgentResult"/>.</returns> public Task <GrpcAgentResult> InvokeAsync(Func <TClient, CallOptions, AsyncUnaryCall <Empty> > func, IMessage?request, GrpcRequestOptions?requestOptions = null, [CallerMemberName] string?memberName = null, [CallerFilePath] string?filePath = null, [CallerLineNumber] int lineNumber = 0) { if (requestOptions?.ETag != null) { throw new NotImplementedException(); } return(GrpcAgentInvoker.Current.InvokeAsync(this, async() => { try { var options = new CallOptions(CreateRequestHeaders()); using var call = Check.NotNull(func, nameof(func)).Invoke(Client, options); await call.ResponseAsync.ConfigureAwait(false); return new GrpcAgentResult(call.GetStatus(), call.GetTrailers(), request); } catch (RpcException rex) { return new GrpcAgentResult(rex, request); } }, null !, memberName, filePath, lineNumber));
/// <summary> /// Generates the message components for the given interaction /// </summary> /// <typeparam name="T">The class type that handles the component interactions</typeparam> /// <param name="channel">The channel the interaction will happen in</param> /// <param name="user">The user that triggered the interaction</param> /// <param name="message">The message that triggered the interaction</param> /// <returns>The message components to be sent to discord</returns> public Task <MessageComponent> Components <T>(IChannel channel, IUser user, IMessage?message = null) where T : ComponentHandler => Components(typeof(T), channel, user, message);
public MessageHandlerResult(IMessage?response = null, bool isDone = false) { Response = response; IsDone = isDone; }
/// <summary> /// Generates the message components for the given interaction /// </summary> /// <param name="type">The class type that handles the component interactions</param> /// <param name="channel">The channel the interaction will happen in</param> /// <param name="user">The user that triggered the interaction</param> /// <param name="message">The message that triggered the interaction</param> /// <returns>The message components to be sent to discord</returns> public async Task <MessageComponent> Components(Type type, IChannel channel, IUser user, IMessage?message) { if (!typeof(ComponentHandler).IsAssignableFrom(type)) { throw new ArgumentException($"Type does not implement `{nameof(ComponentHandler)}`", type.FullName); } var builder = new ComponentBuilder(); var methods = type.GetMethods(); foreach (var method in methods) { var compAtr = method.GetCustomAttribute <ComponentAttribute>(); if (compAtr == null) { continue; } if (compAtr is ButtonAttribute btn) { HandleButton(btn, method, builder); continue; } if (compAtr is not SelectMenuAttribute sm) { _logger.LogWarning($"Unknown `{nameof(ComponentAttribute)}`: {compAtr.GetType().Name}"); continue; } await HandleSelectMenu(sm, method, type, builder, channel, user, message); } return(builder.Build()); }
private static async IAsyncEnumerable <IMessage> ScrapeChannel(IMessageChannel channel, IMessage?start = null) { //If start message is not set then get the latest message in the channel now start ??= (await channel.GetMessagesAsync(1).FlattenAsync()).SingleOrDefault(); // Keep loading pages until the start message is null while (start != null) { // Add a slight delay between fetching pages so we don't hammer discord too hard await Task.Delay(150); // Get the next page of messages var page = (await channel.GetMessagesAsync(start, Direction.Before, 99).FlattenAsync()).OrderByDescending(a => a.CreatedAt).ToArray(); // Set the start of the next page to the end of this page start = page.LastOrDefault(); // yield every message in page foreach (var message in page) { yield return(message); } } }
/// <summary> /// Handles adding <see cref="SelectMenuComponent"/> to the given interaction /// </summary> /// <param name="sm">The <see cref="SelectMenuAttribute"/> that represents the select menu handler</param> /// <param name="method">The method that will be triggered when handling the interaction</param> /// <param name="type">The type of the class that will handle the interaction</param> /// <param name="builder">The <see cref="ComponentBuilder"/></param> /// <param name="channel">The channel the interaction happened in</param> /// <param name="user">The user that triggered the interaction</param> /// <param name="message">The message that triggered the interaction</param> /// <returns>A task representing the completion of the request</returns> /// <exception cref="ArgumentException">Thrown when the min or max values field on the select menu are less than 1</exception> public async Task HandleSelectMenu(SelectMenuAttribute sm, MethodInfo method, Type type, ComponentBuilder builder, IChannel channel, IUser user, IMessage?message) { if (sm.MinValues < 1 || sm.MaxValues < 1) { throw new ArgumentException("Min/Max values for Select Menu must be 1 or greater.", "(Max/Min)Values"); } var id = _handlers.IdFromMethod(method); var options = new List <SelectMenuOptionBuilder>(); if (!string.IsNullOrEmpty(sm.OptionsMethod)) { options.AddRange(await MenuOptionsFromMethod(sm.OptionsMethod, type, channel, user, message)); } options.AddRange(MenuOptionsFromAttributes(method)); builder.WithSelectMenu(id, options, sm.Placeholder, sm.MinValues, sm.MaxValues, sm.Disabled, sm.Row); }
/// <summary> /// Gets all of the <see cref="SelectMenuOptionBuilder[]"/> for the given method /// </summary> /// <param name="method">The method to get the select menu option builders from</param> /// <param name="type">The type of the class that will handle the interaction</param> /// <param name="channel">The channel the interaction happened in</param> /// <param name="user">The user that triggered the interaction</param> /// <param name="message">The message that triggered the interaction</param> /// <returns>A task representing the completion of the request</returns> /// <exception cref="ArgumentException">Thrown if the method is invalid</exception> /// <exception cref="NullReferenceException">Thrown if the service type is invalid</exception> public async Task <SelectMenuOptionBuilder[]> MenuOptionsFromMethod(string method, Type type, IChannel channel, IUser user, IMessage?message) { var minfo = type.GetMethod(method); if (minfo == null) { throw new ArgumentException($"Cannot find method `{method}` in `{type.Name}` type.", nameof(method)); } var instance = _provider.GetService(type); if (instance == null) { throw new NullReferenceException($"Cannot get instance of type `{type.Name}`"); } if (minfo.ReturnType != typeof(Task <SelectMenuOptionBuilder[]>)) { throw new ArgumentException($"Method `{method}` on type `{type.Name}` does not return `Task<SelectMenuOptionBuilder[]>`"); } ((ComponentHandler)instance).SetContext(channel, user, _client, message); var pars = Parameters(minfo, user, channel, message); var res = minfo.Invoke(instance, pars); if (res == null) { throw new ArgumentException($"Results of `{method}` on type `{type.Name}` returned null."); } var returnType = (Task <SelectMenuOptionBuilder[]>)res; return(await returnType); }
public void Reply(IMessage?response) { MessageContext.ReplyResponse = response; }
private async void StartStream(YoutubeClient youtube, AudioClient audioClient, AudioOnlyStreamInfo streamInfo, IMessage?message) { Stream ytStream = await youtube.Videos.Streams.GetAsync(streamInfo); // Convert yt stream MemoryStream memoryStream = new MemoryStream(); await Cli.Wrap("ffmpeg") .WithArguments(" -hide_banner -loglevel panic -i pipe:0 -ac 2 -f s16le -ar 48000 pipe:1") .WithStandardInputPipe(PipeSource.FromStream(ytStream)) .WithStandardOutputPipe(PipeTarget.ToStream(memoryStream)) .ExecuteAsync(); // Clear stream before beginning if (audioClient.CurrentStream != null) { audioClient.CurrentStream.Dispose(); audioClient.CurrentStream = null; } AudioOutStream discord = audioClient.Client.CreatePCMStream(AudioApplication.Mixed); audioClient.CurrentStream = discord; // Delete calling command if (message != null) { await message.DeleteAsync(); } // Start playing music await this.WriteStreamToVoiceChannel(audioClient, discord, memoryStream); }