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

Add /remind command

Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
Octol1ttle 2023-07-08 16:07:37 +05:00
parent 20ffbbaafb
commit d46b08df08
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
18 changed files with 990 additions and 1295 deletions

View file

@ -1,6 +1,5 @@
using Boyfriend.Commands;
using Boyfriend.Services;
using Boyfriend.Services.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@ -80,7 +79,8 @@ public class Boyfriend {
.WithCommandGroup<ClearCommandGroup>()
.WithCommandGroup<KickCommandGroup>()
.WithCommandGroup<MuteCommandGroup>()
.WithCommandGroup<PingCommandGroup>();
.WithCommandGroup<PingCommandGroup>()
.WithCommandGroup<RemindCommandGroup>();
var responderTypes = typeof(Boyfriend).Assembly
.GetExportedTypes()
.Where(t => t.IsResponder());

View file

@ -1,8 +1,5 @@
using System.Drawing;
// TODO: remove this when all colors are used
// ReSharper disable UnusedMember.Global
namespace Boyfriend;
/// <summary>

View file

@ -1,6 +1,6 @@
using System.ComponentModel;
using System.Text;
using Boyfriend.Services.Data;
using Boyfriend.Services;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Rest;

View file

@ -1,7 +1,6 @@
using System.ComponentModel;
using System.Text;
using Boyfriend.Services;
using Boyfriend.Services.Data;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;

View file

@ -1,6 +1,6 @@
using System.ComponentModel;
using System.Text;
using Boyfriend.Services.Data;
using Boyfriend.Services;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;

View file

@ -1,6 +1,5 @@
using System.ComponentModel;
using Boyfriend.Services;
using Boyfriend.Services.Data;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;

View file

@ -1,7 +1,6 @@
using System.ComponentModel;
using System.Text;
using Boyfriend.Services;
using Boyfriend.Services.Data;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;

View file

@ -1,5 +1,5 @@
using System.ComponentModel;
using Boyfriend.Services.Data;
using Boyfriend.Services;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Rest;

View file

@ -0,0 +1,66 @@
using System.ComponentModel;
using Boyfriend.Data;
using Boyfriend.Services;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.Commands.Contexts;
using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Extensions.Formatting;
using Remora.Results;
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
/// <summary>
/// Handles the command to manage reminders: /remind
/// </summary>
public class RemindCommandGroup : CommandGroup {
private readonly ICommandContext _context;
private readonly GuildDataService _dataService;
private readonly FeedbackService _feedbackService;
private readonly IDiscordRestUserAPI _userApi;
public RemindCommandGroup(
ICommandContext context, GuildDataService dataService, FeedbackService feedbackService,
IDiscordRestUserAPI userApi) {
_context = context;
_dataService = dataService;
_feedbackService = feedbackService;
_userApi = userApi;
}
[Command("remind")]
[Description("крафтит напоминалки")]
public async Task<Result> AddReminderAsync(TimeSpan duration, string text) {
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId))
return Result.FromError(
new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context"));
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
if (!userResult.IsDefined(out var user))
return Result.FromError(userResult);
var remindAt = DateTimeOffset.UtcNow.Add(duration);
(await _dataService.GetMemberData(guildId.Value, userId.Value, CancellationToken)).Reminders.Add(
new Reminder {
RemindAt = remindAt,
Channel = channelId.Value,
Text = text
});
var embed = new EmbedBuilder().WithSmallTitle(string.Format(Messages.ReminderCreated, user.GetTag()), user)
.WithDescription(string.Format(Messages.DescriptionReminderCreated, Markdown.Timestamp(remindAt)))
.WithColour(ColorsList.Green)
.Build();
if (!embed.IsDefined(out var built))
return Result.FromError(embed);
return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken);
}
}

View file

@ -14,4 +14,5 @@ public class MemberData {
public ulong Id { get; }
public DateTimeOffset? BannedUntil { get; set; }
public List<Snowflake> Roles { get; set; } = new();
public List<Reminder> Reminders { get; } = new();
}

9
Data/Reminder.cs Normal file
View file

@ -0,0 +1,9 @@
using Remora.Rest.Core;
namespace Boyfriend.Data;
public struct Reminder {
public DateTimeOffset RemindAt;
public string Text;
public Snowflake Channel;
}

View file

@ -1,5 +1,5 @@
using Boyfriend.Data;
using Boyfriend.Services.Data;
using Boyfriend.Services;
using DiffPlex.DiffBuilder;
using Microsoft.Extensions.Logging;
using Remora.Discord.API.Abstractions.Gateway.Events;

2108
Messages.Designer.cs generated

File diff suppressed because it is too large Load diff

View file

@ -456,9 +456,9 @@
<data name="MissingReminderText" xml:space="preserve">
<value>You need to specify reminder text!</value>
</data>
<data name="FeedbackReminderAdded" xml:space="preserve">
<value>OK, I'll mention you on &lt;t:{0}:f&gt;</value>
</data>
<data name="DescriptionReminderCreated" xml:space="preserve">
<value>OK, I'll mention you on {0}</value>
</data>
<data name="InvalidRemindIn" xml:space="preserve">
<value>You need to specify when I should send you the reminder!</value>
</data>
@ -537,4 +537,13 @@
<data name="AboutDeveloper@neroduckale" xml:space="preserve">
<value>developer</value>
</data>
<data name="ReminderCreated" xml:space="preserve">
<value>Reminder for {0} created</value>
</data>
<data name="Reminder" xml:space="preserve">
<value>Reminder for {0}</value>
</data>
<data name="DescriptionReminder" xml:space="preserve">
<value>You asked me to remind you {0}</value>
</data>
</root>

View file

@ -453,9 +453,9 @@
<data name="MissingReminderText" xml:space="preserve">
<value>Тебе нужно указать текст напоминания!</value>
</data>
<data name="FeedbackReminderAdded" xml:space="preserve">
<value>Хорошо, я упомяну тебя &lt;t:{0}:f&gt;</value>
</data>
<data name="DescriptionReminderCreated" xml:space="preserve">
<value>Хорошо, я упомяну тебя {0}</value>
</data>
<data name="InvalidRemindIn" xml:space="preserve">
<value>Нужно указать время, через которое придёт напоминание!</value>
</data>
@ -537,4 +537,13 @@
<data name="AboutDeveloper@mctaylors" xml:space="preserve">
<value>дизайнер лого и эмбедов, создатель Boyfriend's Wiki</value>
</data>
<data name="ReminderCreated" xml:space="preserve">
<value>Напоминание для {0} создано</value>
</data>
<data name="Reminder" xml:space="preserve">
<value>Напоминание для {0}</value>
</data>
<data name="DescriptionReminder" xml:space="preserve">
<value>Вы просили напомнить вам {0}</value>
</data>
</root>

View file

@ -456,9 +456,9 @@
<data name="MissingReminderText" xml:space="preserve">
<value>для крафта напоминалки нужен текст</value>
</data>
<data name="FeedbackReminderAdded" xml:space="preserve">
<value>вас понял, упоминание будет &lt;t:{0}:f&gt;</value>
</data>
<data name="DescriptionReminderCreated" xml:space="preserve">
<value>вас понял, упоминание будет {0}</value>
</data>
<data name="InvalidRemindIn" xml:space="preserve">
<value>шизоид у меня на часах такого нету</value>
</data>
@ -537,4 +537,13 @@
<data name="AboutDeveloper@Octol1ttle" xml:space="preserve">
<value>САМЫЙ ВАЖНЫЙ чел написавший кода больше всех (99.99%)</value>
</data>
<data name="ReminderCreated" xml:space="preserve">
<value>напоминалка для {0} скрафченА</value>
</data>
<data name="Reminder" xml:space="preserve">
<value>напоминалка для {0}</value>
</data>
<data name="DescriptionReminder" xml:space="preserve">
<value>ты хотел чтоб я напомнил тебе {0}</value>
</data>
</root>

View file

@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Rest.Core;
namespace Boyfriend.Services.Data;
namespace Boyfriend.Services;
/// <summary>
/// Handles saving, loading, initializing and providing <see cref="GuildData" />.

View file

@ -1,5 +1,4 @@
using Boyfriend.Data;
using Boyfriend.Services.Data;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Remora.Discord.API.Abstractions.Objects;
@ -95,11 +94,12 @@ public class GuildUpdateService : BackgroundService {
/// This method does the following:
/// <list type="bullet">
/// <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>Automatically grants members the guild's <see cref="GuildConfiguration.DefaultRole"/> if one is set.</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 completion notifications.</item>
/// <item>Sends reminders to members.</item>
/// </list>
/// This is done here and not in a <see cref="IResponder{TGatewayEvent}" /> for the following reasons:
/// <list type="bullet">
@ -118,20 +118,46 @@ public class GuildUpdateService : BackgroundService {
var defaultRoleSnowflake = data.Configuration.DefaultRole.ToDiscordSnowflake();
foreach (var memberData in data.MemberData.Values) {
var userIdSnowflake = memberData.Id.ToDiscordSnowflake();
var userId = memberData.Id.ToDiscordSnowflake();
if (defaultRoleSnowflake.Value is not 0 && !memberData.Roles.Contains(defaultRoleSnowflake))
_ = _guildApi.AddGuildMemberRoleAsync(
guildId, userIdSnowflake, defaultRoleSnowflake, ct: ct);
guildId, userId, defaultRoleSnowflake, ct: ct);
if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
var unbanResult = await _guildApi.RemoveGuildBanAsync(
guildId, userIdSnowflake, Messages.PunishmentExpired.EncodeHeader(), ct);
guildId, userId, Messages.PunishmentExpired.EncodeHeader(), ct);
if (unbanResult.IsSuccess)
memberData.BannedUntil = null;
else
_logger.LogWarning(
"Error in automatic user unban request.\n{ErrorMessage}", unbanResult.Error.Message);
}
var userResult = await _userApi.GetUserAsync(userId, ct);
if (!userResult.IsDefined(out var user)) continue;
for (var i = memberData.Reminders.Count - 1; i >= 0; i--) {
var reminder = memberData.Reminders[i];
if (DateTimeOffset.UtcNow < reminder.RemindAt) continue;
var embed = new EmbedBuilder().WithSmallTitle(
string.Format(Messages.Reminder, user.GetTag()), user)
.WithDescription(
string.Format(Messages.DescriptionReminder, Markdown.InlineCode(reminder.Text)))
.WithColour(ColorsList.Magenta)
.Build();
if (!embed.IsDefined(out var built)) continue;
var messageResult = await _channelApi.CreateMessageAsync(
reminder.Channel, Mention.User(user), embeds: new[] { built }, ct: ct);
if (!messageResult.IsSuccess)
_logger.LogWarning(
"Error in reminder send.\n{ErrorMessage}", messageResult.Error.Message);
memberData.Reminders.Remove(reminder);
}
}
var eventsResult = await _eventApi.ListScheduledEventsForGuildAsync(guildId, ct: ct);