/// <exception cref="ArgumentNullException"></exception> async Task <DiscordMessage> CreateMessage(Snowflake channelId, HttpContent fileContent, string fileName, CreateMessageOptions options) { if (string.IsNullOrWhiteSpace(fileName)) { // Technically this is also handled when setting the field on the multipart form data throw new ArgumentNullException(nameof(fileName)); } DiscordApiData returnData = await rest.Send(() => { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{RestClient.BASE_URL}/channels/{channelId}/messages"); MultipartFormDataContent data = new MultipartFormDataContent(); data.Add(fileContent, "file", fileName); if (options != null) { DiscordApiData payloadJson = options.Build(); data.Add(new StringContent(payloadJson.SerializeToJson()), "payload_json"); } request.Content = data; return(request); }, $"channels/{channelId}/messages").ConfigureAwait(false); return(new DiscordMessage(this, returnData)); }
/// <exception cref="DiscordHttpApiException"></exception> public Task <DiscordApiData> Patch(string action, DiscordApiData data, string rateLimitRoute, CancellationToken?cancellationToken = null) { return(Send(() => { HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), $"{BASE_URL}/{action}"); request.Content = new StringContent(data.SerializeToJson(), Encoding.UTF8, "application/json"); return request; }, rateLimitRoute, cancellationToken)); }
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="DiscordHttpApiException"></exception> async Task <DiscordMessage> ExecuteWebhook(Snowflake webhookId, string token, HttpContent fileContent, string fileName, ExecuteWebhookOptions options, bool waitAndReturnMessage) { if (token == null) { throw new ArgumentNullException(nameof(token)); } if (string.IsNullOrWhiteSpace(token)) { throw new ArgumentException("Token cannot be empty or only contain whitespace characters.", nameof(token)); } if (string.IsNullOrEmpty(fileName)) { // Technically already handled when adding the field to the multipart form data. throw new ArgumentNullException(nameof(fileName)); } DiscordApiData returnData = await rest.Send(() => { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{RestClient.BASE_URL}/webhooks/{webhookId}/{token}"); MultipartFormDataContent data = new MultipartFormDataContent(); data.Add(fileContent, "file", fileName); if (options != null) { DiscordApiData payloadJson = options.Build(); data.Add(new StringContent(payloadJson.SerializeToJson()), "payload_json"); } request.Content = data; return(request); }, $"webhooks/{webhookId}/token").ConfigureAwait(false); return(waitAndReturnMessage ? new DiscordMessage(this, returnData) : null); }
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception> /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception> /// <exception cref="JsonWriterException">Thrown if the given data cannot be serialized as JSON.</exception> protected async Task SendAsync(DiscordApiData data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (socket.State != WebSocketState.Open) { throw new InvalidOperationException("Cannot send data when the socket is not open."); } // Serialize the data as JSON and convert to bytes byte[] bytes = Encoding.UTF8.GetBytes(data.SerializeToJson()); // Wait for any existing send operations, // ClientWebSocket only supports one send operation at a time. using (await sendLock.LockAsync().ConfigureAwait(false)) { // Now that we have acquired the lock, check if the socket is still open. // If not, just ignore this message so we can effectively cancel any pending sends after close. if (socket.State == WebSocketState.Open) { try { await SendData(bytes).ConfigureAwait(false); } catch (InvalidOperationException iex) // also catches ObjectDisposedException { throw new DiscordWebSocketException("The WebSocket connection was closed while sending data.", DiscordWebSocketError.ConnectionClosed, iex); } catch (OperationCanceledException ocex) { throw new DiscordWebSocketException("The WebSocket connection was aborted while sending data.", DiscordWebSocketError.ConnectionClosed, ocex); } catch (WebSocketException wsex) { if (wsex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) { throw new DiscordWebSocketException("The WebSocket connection was closed while sending data.", DiscordWebSocketError.ConnectionClosed, wsex); } else { // The only known WebSocketException is ConnectionClosedPrematurely, so // log any others to be handled later. log.LogError($"[SendAsync] Unexpected WebSocketException error: code = {wsex.WebSocketErrorCode}, error = {wsex}"); // Should never happen throw new DiscordWebSocketException("An unexpected WebSocket error occured while sending data.", DiscordWebSocketError.Unexpected, wsex); } } catch (Exception ex) { // We are not expecting any other exceptions, but since the exceptions thrown from // System.Net.WebSockets.ClientWebSocket.SendAsync is not documented, log any // unknown exceptions so we can handle them later. log.LogError($"[SendAsync] Unhandled exception: {ex}"); // Should never happen, but we should consolidate everything into DiscordWebSocketException // so handling exceptions we do not know about is at least consistent. throw new DiscordWebSocketException("An unexpected error occured while sending data.", DiscordWebSocketError.Unexpected, ex); } } } }