1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-04-20 00:43:36 +03:00

Implement some unused guild configuration options

Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
Octol1ttle 2023-06-16 13:45:57 +05:00
parent 4702d2fcba
commit 635bf26601
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
5 changed files with 68 additions and 12 deletions

View file

@ -1,3 +1,5 @@
using Remora.Rest.Core;
namespace Boyfriend.Data; namespace Boyfriend.Data;
public class MemberData { public class MemberData {
@ -8,4 +10,5 @@ public class MemberData {
public ulong Id { get; } public ulong Id { get; }
public DateTimeOffset? BannedUntil { get; set; } public DateTimeOffset? BannedUntil { get; set; }
public List<Snowflake> Roles { get; set; } = new();
} }

View file

@ -288,3 +288,20 @@ public class GuildScheduledEventDeleteResponder : IResponder<IGuildScheduledEven
guildData.Configuration.EventNotificationChannel.ToDiscordSnowflake(), embeds: new[] { built }, ct: ct); guildData.Configuration.EventNotificationChannel.ToDiscordSnowflake(), embeds: new[] { built }, ct: ct);
} }
} }
/// <summary>
/// Handles updating <see cref="MemberData.Roles" /> when a guild member is updated.
/// </summary>
public class GuildMemberUpdateResponder : IResponder<IGuildMemberUpdate> {
private readonly GuildDataService _dataService;
public GuildMemberUpdateResponder(GuildDataService dataService) {
_dataService = dataService;
}
public async Task<Result> RespondAsync(IGuildMemberUpdate gatewayEvent, CancellationToken ct = default) {
var memberData = await _dataService.GetMemberData(gatewayEvent.GuildID, gatewayEvent.User.ID, ct);
memberData.Roles = gatewayEvent.Roles.ToList();
return Result.FromSuccess();
}
}

View file

@ -23,7 +23,7 @@ public class InteractionResponders : InteractionGroup {
/// A button that will output an ephemeral embed containing the information about a scheduled event. /// A button that will output an ephemeral embed containing the information about a scheduled event.
/// </summary> /// </summary>
/// <param name="state">The ID of the guild and scheduled event, encoded as "guildId:eventId".</param> /// <param name="state">The ID of the guild and scheduled event, encoded as "guildId:eventId".</param>
/// <returns>A feedback sending result which may or may not have succeeded.</returns> /// <returns>An ephemeral feedback sending result which may or may not have succeeded.</returns>
[Button("scheduled-event-details")] [Button("scheduled-event-details")]
public async Task<Result> OnStatefulButtonClicked(string? state = null) { public async Task<Result> OnStatefulButtonClicked(string? state = null) {
if (state is null) return Result.FromError(new ArgumentNullError(nameof(state))); if (state is null) return Result.FromError(new ArgumentNullError(nameof(state)));

View file

@ -1,6 +1,8 @@
using System.Text.Json; using System.Text.Json;
using Boyfriend.Data; using Boyfriend.Data;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Rest.Core; using Remora.Rest.Core;
namespace Boyfriend.Services.Data; namespace Boyfriend.Services.Data;
@ -10,9 +12,14 @@ namespace Boyfriend.Services.Data;
/// </summary> /// </summary>
public class GuildDataService : IHostedService { public class GuildDataService : IHostedService {
private readonly Dictionary<Snowflake, GuildData> _datas = new(); private readonly Dictionary<Snowflake, GuildData> _datas = new();
private readonly IDiscordRestGuildAPI _guildApi;
private readonly ILogger<GuildDataService> _logger;
// https://github.com/dotnet/aspnetcore/issues/39139 // https://github.com/dotnet/aspnetcore/issues/39139
public GuildDataService(IHostApplicationLifetime lifetime) { public GuildDataService(
IHostApplicationLifetime lifetime, IDiscordRestGuildAPI guildApi, ILogger<GuildDataService> logger) {
_guildApi = guildApi;
_logger = logger;
lifetime.ApplicationStopping.Register(ApplicationStopping); lifetime.ApplicationStopping.Register(ApplicationStopping);
} }
@ -75,6 +82,11 @@ public class GuildDataService : IHostedService {
await using var dataStream = File.OpenRead(dataPath); await using var dataStream = File.OpenRead(dataPath);
var data = await JsonSerializer.DeserializeAsync<MemberData>(dataStream, cancellationToken: ct); var data = await JsonSerializer.DeserializeAsync<MemberData>(dataStream, cancellationToken: ct);
if (data is null) continue; if (data is null) continue;
var memberResult = await _guildApi.GetGuildMemberAsync(guildId, data.Id.ToDiscordSnowflake(), ct);
if (memberResult.IsSuccess)
data.Roles = memberResult.Entity.Roles.ToList();
else
_logger.LogWarning("Error in member retrieval.\n{ErrorMessage}", memberResult.Error.Message);
memberData.Add(data.Id, data); memberData.Add(data.Id, data);
} }
@ -91,9 +103,9 @@ public class GuildDataService : IHostedService {
return (await GetData(guildId, ct)).Configuration; return (await GetData(guildId, ct)).Configuration;
} }
/*public async Task<MemberData> GetMemberData(Snowflake guildId, Snowflake userId, CancellationToken ct = default) { public async Task<MemberData> GetMemberData(Snowflake guildId, Snowflake userId, CancellationToken ct = default) {
return (await GetData(guildId, ct)).GetMemberData(userId); return (await GetData(guildId, ct)).GetMemberData(userId);
}*/ }
public IEnumerable<Snowflake> GetGuildIds() { public IEnumerable<Snowflake> GetGuildIds() {
return _datas.Keys; return _datas.Keys;

View file

@ -63,7 +63,9 @@ public class GuildUpdateService : BackgroundService {
/// This method does the following: /// This method does the following:
/// <list type="bullet"> /// <list type="bullet">
/// <item>Automatically unbans users once their ban period has expired.</item> /// <item>Automatically unbans users once their ban period has expired.</item>
/// <item>Automatically grants users the guild's <see cref="GuildConfiguration.DefaultRole"/> if one is set.</item>
/// <item>Sends reminders about an upcoming scheduled event.</item> /// <item>Sends reminders about an upcoming scheduled event.</item>
/// <item>Automatically starts scheduled events if <see cref="GuildConfiguration.AutoStartEvents"/> is enabled.</item>
/// <item>Sends scheduled event start notifications.</item> /// <item>Sends scheduled event start notifications.</item>
/// <item>Sends scheduled event completion notifications.</item> /// <item>Sends scheduled event completion notifications.</item>
/// </list> /// </list>
@ -73,7 +75,7 @@ public class GuildUpdateService : BackgroundService {
/// Downtime would affect the reliability of notifications and automatic unbans if this logic were to be in a /// Downtime would affect the reliability of notifications and automatic unbans if this logic were to be in a
/// <see cref="IResponder{TGatewayEvent}" />. /// <see cref="IResponder{TGatewayEvent}" />.
/// </item> /// </item>
/// <item>The Discord API doesn't provide necessary about scheduled event updates.</item> /// <item>The Discord API doesn't provide necessary information about scheduled event updates.</item>
/// </list> /// </list>
/// </remarks> /// </remarks>
/// <param name="guildId">The ID of the guild to update.</param> /// <param name="guildId">The ID of the guild to update.</param>
@ -81,16 +83,28 @@ public class GuildUpdateService : BackgroundService {
private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) { private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) {
var data = await _dataService.GetData(guildId, ct); var data = await _dataService.GetData(guildId, ct);
Messages.Culture = data.Culture; Messages.Culture = data.Culture;
var defaultRoleSnowflake = data.Configuration.DefaultRole.ToDiscordSnowflake();
foreach (var memberData in data.MemberData.Values) {
var userIdSnowflake = memberData.Id.ToDiscordSnowflake();
if (!memberData.Roles.Contains(defaultRoleSnowflake)) {
var defaultRoleResult = await _guildApi.AddGuildMemberRoleAsync(
guildId, userIdSnowflake, defaultRoleSnowflake, ct: ct);
if (!defaultRoleResult.IsSuccess)
_logger.LogWarning(
"Error in automatic default role add request.\n{ErrorMessage}",
defaultRoleResult.Error.Message);
}
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
foreach (var memberData in data.MemberData.Values)
if (DateTimeOffset.UtcNow > memberData.BannedUntil) { if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
var unbanResult = await _guildApi.RemoveGuildBanAsync( var unbanResult = await _guildApi.RemoveGuildBanAsync(
guildId, memberData.Id.ToDiscordSnowflake(), Messages.PunishmentExpired.EncodeHeader(), ct); guildId, userIdSnowflake, Messages.PunishmentExpired.EncodeHeader(), ct);
if (unbanResult.IsSuccess) if (unbanResult.IsSuccess)
memberData.BannedUntil = null; memberData.BannedUntil = null;
else else
_logger.LogWarning("Error in member data update.\n{ErrorMessage}", unbanResult.Error.Message); _logger.LogWarning(
"Error in automatic user unban request.\n{ErrorMessage}", unbanResult.Error.Message);
}
} }
var eventsResult = await _eventApi.ListScheduledEventsForGuildAsync(guildId, ct: ct); var eventsResult = await _eventApi.ListScheduledEventsForGuildAsync(guildId, ct: ct);
@ -104,7 +118,17 @@ public class GuildUpdateService : BackgroundService {
} else { } else {
var storedEvent = data.ScheduledEvents[scheduledEvent.ID.Value]; var storedEvent = data.ScheduledEvents[scheduledEvent.ID.Value];
if (storedEvent.Status == scheduledEvent.Status) { if (storedEvent.Status == scheduledEvent.Status) {
if (DateTimeOffset.UtcNow if (DateTimeOffset.UtcNow >= scheduledEvent.ScheduledStartTime) {
if (scheduledEvent.Status is not GuildScheduledEventStatus.Active) {
var startResult = await _eventApi.ModifyGuildScheduledEventAsync(
guildId, scheduledEvent.ID,
status: GuildScheduledEventStatus.Active, ct: ct);
if (!startResult.IsSuccess)
_logger.LogWarning(
"Error in automatic scheduled event start request.\n{ErrorMessage}",
startResult.Error.Message);
}
} else if (DateTimeOffset.UtcNow
>= scheduledEvent.ScheduledStartTime - data.Configuration.EventEarlyNotificationOffset >= scheduledEvent.ScheduledStartTime - data.Configuration.EventEarlyNotificationOffset
&& !storedEvent.EarlyNotificationSent) { && !storedEvent.EarlyNotificationSent) {
var earlyResult = await SendScheduledEventUpdatedMessage(scheduledEvent, data, true, ct); var earlyResult = await SendScheduledEventUpdatedMessage(scheduledEvent, data, true, ct);