feat: implement sending welcome message
This commit is contained in:
@@ -25,7 +25,7 @@ internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
||||
},
|
||||
};
|
||||
private readonly AsyncLock _lock = new();
|
||||
private readonly Dictionary<string, Func<DiscordEvent, IServiceProvider, Task>> _eventHandlers = [];
|
||||
private readonly Dictionary<string, Func<DiscordEvent, IServiceProvider, CancellationToken, Task>> _eventHandlers = [];
|
||||
|
||||
private string _gatewayUrl = string.Empty;
|
||||
private IWebSocket? _webSocket;
|
||||
@@ -85,7 +85,7 @@ internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
||||
await CloseIfOpenAsync(WebSocketCloseStatus.NormalClosure, "Disconnecting", cancellationToken);
|
||||
}
|
||||
|
||||
public void On(string eventName, Func<DiscordEvent, IServiceProvider, Task> handler)
|
||||
public void On(string eventName, Func<DiscordEvent, IServiceProvider, CancellationToken, Task> handler)
|
||||
{
|
||||
if (DiscordEventTypes.IsValidEvent(eventName) is false)
|
||||
{
|
||||
@@ -103,7 +103,7 @@ internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
||||
_eventHandlers[eventName] += handler;
|
||||
}
|
||||
|
||||
public void Off(string eventName, Func<DiscordEvent, IServiceProvider, Task> handler)
|
||||
public void Off(string eventName, Func<DiscordEvent, IServiceProvider, CancellationToken, Task> handler)
|
||||
{
|
||||
if (DiscordEventTypes.IsValidEvent(eventName) is false)
|
||||
{
|
||||
@@ -271,7 +271,7 @@ internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
||||
{
|
||||
await using var scope = _serviceScopeFactory.CreateAsyncScope();
|
||||
// TODO: Pass cancellation token to handler
|
||||
await handler(de, scope.ServiceProvider);
|
||||
await handler(de, scope.ServiceProvider, cancellationToken);
|
||||
}
|
||||
# pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -17,10 +17,10 @@ internal sealed class DiscordRestClient : IDiscordRestClient
|
||||
|
||||
public async Task<string> GetGatewayUrlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = new Uri("gateway", UriKind.Relative);
|
||||
var response = await _httpClient.GetAsync(uri, cancellationToken);
|
||||
var gatewayEndpoint = new Uri("gateway", UriKind.Relative);
|
||||
var response = await _httpClient.GetAsync(gatewayEndpoint, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
_logger.LogError("Failed to get gateway URL: {StatusCode}", response.StatusCode);
|
||||
throw new DiscordRestClientException("Failed to get gateway URL.");
|
||||
@@ -36,8 +36,49 @@ internal sealed class DiscordRestClient : IDiscordRestClient
|
||||
|
||||
return gatewayResponse.Url;
|
||||
}
|
||||
|
||||
public async Task<MessageCreateData> CreateMessageAsync(string channelId, CreateMessageRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var channelEndpoint = new Uri($"channels/{channelId}/messages", UriKind.Relative);
|
||||
var response = await _httpClient.PostAsJsonAsync(channelEndpoint, request, cancellationToken);
|
||||
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
_logger.LogError("Failed to create message: {StatusCode}", response.StatusCode);
|
||||
throw new DiscordRestClientException("Failed to create message.");
|
||||
}
|
||||
|
||||
var messageCreateData = await response.Content.ReadFromJsonAsync<MessageCreateData>(cancellationToken);
|
||||
|
||||
if (messageCreateData is null)
|
||||
{
|
||||
_logger.LogError("Failed to deserialize message create response.");
|
||||
throw new DiscordRestClientException("Failed to deserialize message create response.");
|
||||
}
|
||||
|
||||
return messageCreateData;
|
||||
}
|
||||
}
|
||||
|
||||
internal record GatewayResponse(
|
||||
internal sealed record GatewayResponse(
|
||||
[property: JsonPropertyName("url")] string Url
|
||||
);
|
||||
|
||||
internal sealed record CreateMessageRequest(
|
||||
[property: JsonPropertyName("content")] string Content,
|
||||
[property: JsonPropertyName("message_reference")] MessageReference? MessageReference
|
||||
);
|
||||
|
||||
internal sealed record MessageReference(
|
||||
[property: JsonPropertyName("type")] int Type,
|
||||
[property: JsonPropertyName("message_id")] string MessageId,
|
||||
[property: JsonPropertyName("channel_id")] string ChannelId,
|
||||
[property: JsonPropertyName("guild_id")] string GuildId,
|
||||
[property: JsonPropertyName("fail_if_not_exists")] bool FailIfNotExists
|
||||
);
|
||||
|
||||
internal static class MessageReferenceTypes
|
||||
{
|
||||
public const int Default = 0;
|
||||
public const int Forward = 1;
|
||||
}
|
||||
@@ -4,6 +4,6 @@ internal interface IDiscordGatewayClient : IDisposable
|
||||
{
|
||||
Task ConnectAsync(CancellationToken cancellationToken);
|
||||
Task DisconnectAsync(CancellationToken cancellationToken);
|
||||
void On(string eventName, Func<DiscordEvent, IServiceProvider, Task> handler);
|
||||
void Off(string eventName, Func<DiscordEvent, IServiceProvider, Task> handler);
|
||||
void On(string eventName, Func<DiscordEvent, IServiceProvider, CancellationToken, Task> handler);
|
||||
void Off(string eventName, Func<DiscordEvent, IServiceProvider, CancellationToken, Task> handler);
|
||||
}
|
||||
@@ -3,4 +3,5 @@ namespace StevesBot.Worker.Discord;
|
||||
internal interface IDiscordRestClient
|
||||
{
|
||||
Task<string> GetGatewayUrlAsync(CancellationToken cancellationToken);
|
||||
Task<MessageCreateData> CreateMessageAsync(string channelId, CreateMessageRequest request, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
@@ -22,6 +24,7 @@ builder.Services
|
||||
var discordOptions = sp.GetRequiredService<DiscordClientOptions>();
|
||||
c.BaseAddress = new Uri(discordOptions.ApiUrl);
|
||||
c.DefaultRequestHeaders.Authorization = new("Bot", discordOptions.AppToken);
|
||||
c.DefaultRequestHeaders.Add("User-Agent", $"DiscordBot (https://github.com/StevanFreeborn/steves-bot, 0.0.0)");
|
||||
})
|
||||
.AddStandardResilienceHandler();
|
||||
|
||||
|
||||
@@ -16,34 +16,32 @@ internal class Worker : IHostedService
|
||||
{
|
||||
_logger.LogInformation("Connecting Discord Gateway Client");
|
||||
|
||||
// TODO: Provide access to the gateway client
|
||||
// in delegate...don't try to resolve it from the service provider
|
||||
// TODO: Provide .On and .Off method overloads to allow
|
||||
// caller to not need to use discard for unused parameters
|
||||
_discordGatewayClient.On(DiscordEventTypes.MessageCreate, static (discordEvent, sp) =>
|
||||
_discordGatewayClient.On(DiscordEventTypes.MessageCreate, static async (discordEvent, sp, cancellationToken) =>
|
||||
{
|
||||
var discordRestClient = sp.GetRequiredService<IDiscordRestClient>();
|
||||
var logger = sp.GetRequiredService<ILogger<DiscordGatewayClient>>();
|
||||
|
||||
// TODO: When a user join message is received,
|
||||
// we are going to reply to the message with
|
||||
// a welcome greeting
|
||||
// A user join message is type 8
|
||||
// Will need to use REST API to reply to the message
|
||||
|
||||
if (
|
||||
discordEvent is MessageCreateDiscordEvent mcde &&
|
||||
mcde.IsMessageType(DiscordMessageTypes.UserJoin)
|
||||
)
|
||||
if (discordEvent is not MessageCreateDiscordEvent mcde || mcde.IsMessageType(DiscordMessageTypes.UserJoin) == false)
|
||||
{
|
||||
logger.LogInformation("Guild Id: {GuildId}", mcde.Data.GuildId);
|
||||
logger.LogInformation("Channel Id: {ChannelId}", mcde.Data.ChannelId);
|
||||
logger.LogInformation("Message Id: {MessageId}", mcde.Data.Id);
|
||||
logger.LogInformation("User Id: {UserId}", mcde.Data.Author.Id);
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
logger.LogInformation("Received user join message for user: {UserId}", mcde.Data.Author.Id);
|
||||
|
||||
var request = new CreateMessageRequest(
|
||||
Content: $"Welcome to the server <@{mcde.Data.Author.Id}>! We're glad to have you here.",
|
||||
MessageReference: new(
|
||||
Type: MessageReferenceTypes.Default,
|
||||
MessageId: mcde.Data.Id,
|
||||
ChannelId: mcde.Data.ChannelId,
|
||||
GuildId: mcde.Data.GuildId,
|
||||
FailIfNotExists: false
|
||||
)
|
||||
);
|
||||
|
||||
var message = await discordRestClient.CreateMessageAsync(mcde.Data.ChannelId, request, cancellationToken);
|
||||
|
||||
logger.LogInformation("Created welcome message with Id: {MessageId} for user: {UserId}", message.Id, mcde.Data.Author.Id);
|
||||
});
|
||||
|
||||
return _discordGatewayClient.ConnectAsync(cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user