mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Add /remind command
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
20ffbbaafb
commit
d46b08df08
18 changed files with 990 additions and 1295 deletions
|
@ -1,6 +1,5 @@
|
||||||
using Boyfriend.Commands;
|
using Boyfriend.Commands;
|
||||||
using Boyfriend.Services;
|
using Boyfriend.Services;
|
||||||
using Boyfriend.Services.Data;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
@ -80,7 +79,8 @@ public class Boyfriend {
|
||||||
.WithCommandGroup<ClearCommandGroup>()
|
.WithCommandGroup<ClearCommandGroup>()
|
||||||
.WithCommandGroup<KickCommandGroup>()
|
.WithCommandGroup<KickCommandGroup>()
|
||||||
.WithCommandGroup<MuteCommandGroup>()
|
.WithCommandGroup<MuteCommandGroup>()
|
||||||
.WithCommandGroup<PingCommandGroup>();
|
.WithCommandGroup<PingCommandGroup>()
|
||||||
|
.WithCommandGroup<RemindCommandGroup>();
|
||||||
var responderTypes = typeof(Boyfriend).Assembly
|
var responderTypes = typeof(Boyfriend).Assembly
|
||||||
.GetExportedTypes()
|
.GetExportedTypes()
|
||||||
.Where(t => t.IsResponder());
|
.Where(t => t.IsResponder());
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
// TODO: remove this when all colors are used
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
|
|
||||||
namespace Boyfriend;
|
namespace Boyfriend;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Boyfriend.Services.Data;
|
using Boyfriend.Services;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Boyfriend.Services;
|
using Boyfriend.Services;
|
||||||
using Boyfriend.Services.Data;
|
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Boyfriend.Services.Data;
|
using Boyfriend.Services;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Boyfriend.Services;
|
using Boyfriend.Services;
|
||||||
using Boyfriend.Services.Data;
|
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Boyfriend.Services;
|
using Boyfriend.Services;
|
||||||
using Boyfriend.Services.Data;
|
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Boyfriend.Services.Data;
|
using Boyfriend.Services;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
|
66
Commands/RemindCommandGroup.cs
Normal file
66
Commands/RemindCommandGroup.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,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();
|
public List<Snowflake> Roles { get; set; } = new();
|
||||||
|
public List<Reminder> Reminders { get; } = new();
|
||||||
}
|
}
|
||||||
|
|
9
Data/Reminder.cs
Normal file
9
Data/Reminder.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
|
namespace Boyfriend.Data;
|
||||||
|
|
||||||
|
public struct Reminder {
|
||||||
|
public DateTimeOffset RemindAt;
|
||||||
|
public string Text;
|
||||||
|
public Snowflake Channel;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
using Boyfriend.Data;
|
using Boyfriend.Data;
|
||||||
using Boyfriend.Services.Data;
|
using Boyfriend.Services;
|
||||||
using DiffPlex.DiffBuilder;
|
using DiffPlex.DiffBuilder;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
2108
Messages.Designer.cs
generated
2108
Messages.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -456,8 +456,8 @@
|
||||||
<data name="MissingReminderText" xml:space="preserve">
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
<value>You need to specify reminder text!</value>
|
<value>You need to specify reminder text!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackReminderAdded" xml:space="preserve">
|
<data name="DescriptionReminderCreated" xml:space="preserve">
|
||||||
<value>OK, I'll mention you on <t:{0}:f></value>
|
<value>OK, I'll mention you on {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidRemindIn" xml:space="preserve">
|
<data name="InvalidRemindIn" xml:space="preserve">
|
||||||
<value>You need to specify when I should send you the reminder!</value>
|
<value>You need to specify when I should send you the reminder!</value>
|
||||||
|
@ -537,4 +537,13 @@
|
||||||
<data name="AboutDeveloper@neroduckale" xml:space="preserve">
|
<data name="AboutDeveloper@neroduckale" xml:space="preserve">
|
||||||
<value>developer</value>
|
<value>developer</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
|
|
|
@ -453,8 +453,8 @@
|
||||||
<data name="MissingReminderText" xml:space="preserve">
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
<value>Тебе нужно указать текст напоминания!</value>
|
<value>Тебе нужно указать текст напоминания!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackReminderAdded" xml:space="preserve">
|
<data name="DescriptionReminderCreated" xml:space="preserve">
|
||||||
<value>Хорошо, я упомяну тебя <t:{0}:f></value>
|
<value>Хорошо, я упомяну тебя {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidRemindIn" xml:space="preserve">
|
<data name="InvalidRemindIn" xml:space="preserve">
|
||||||
<value>Нужно указать время, через которое придёт напоминание!</value>
|
<value>Нужно указать время, через которое придёт напоминание!</value>
|
||||||
|
@ -537,4 +537,13 @@
|
||||||
<data name="AboutDeveloper@mctaylors" xml:space="preserve">
|
<data name="AboutDeveloper@mctaylors" xml:space="preserve">
|
||||||
<value>дизайнер лого и эмбедов, создатель Boyfriend's Wiki</value>
|
<value>дизайнер лого и эмбедов, создатель Boyfriend's Wiki</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
|
|
|
@ -456,8 +456,8 @@
|
||||||
<data name="MissingReminderText" xml:space="preserve">
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
<value>для крафта напоминалки нужен текст</value>
|
<value>для крафта напоминалки нужен текст</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackReminderAdded" xml:space="preserve">
|
<data name="DescriptionReminderCreated" xml:space="preserve">
|
||||||
<value>вас понял, упоминание будет <t:{0}:f></value>
|
<value>вас понял, упоминание будет {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidRemindIn" xml:space="preserve">
|
<data name="InvalidRemindIn" xml:space="preserve">
|
||||||
<value>шизоид у меня на часах такого нету</value>
|
<value>шизоид у меня на часах такого нету</value>
|
||||||
|
@ -537,4 +537,13 @@
|
||||||
<data name="AboutDeveloper@Octol1ttle" xml:space="preserve">
|
<data name="AboutDeveloper@Octol1ttle" xml:space="preserve">
|
||||||
<value>САМЫЙ ВАЖНЫЙ чел написавший кода больше всех (99.99%)</value>
|
<value>САМЫЙ ВАЖНЫЙ чел написавший кода больше всех (99.99%)</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
|
|
|
@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Boyfriend.Services.Data;
|
namespace Boyfriend.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles saving, loading, initializing and providing <see cref="GuildData" />.
|
/// Handles saving, loading, initializing and providing <see cref="GuildData" />.
|
|
@ -1,5 +1,4 @@
|
||||||
using Boyfriend.Data;
|
using Boyfriend.Data;
|
||||||
using Boyfriend.Services.Data;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
@ -95,11 +94,12 @@ 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>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>Sends reminders about an upcoming scheduled event.</item>
|
||||||
/// <item>Automatically starts scheduled events if <see cref="GuildConfiguration.AutoStartEvents"/> is enabled.</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>
|
||||||
|
/// <item>Sends reminders to members.</item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// This is done here and not in a <see cref="IResponder{TGatewayEvent}" /> for the following reasons:
|
/// This is done here and not in a <see cref="IResponder{TGatewayEvent}" /> for the following reasons:
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
|
@ -118,20 +118,46 @@ public class GuildUpdateService : BackgroundService {
|
||||||
var defaultRoleSnowflake = data.Configuration.DefaultRole.ToDiscordSnowflake();
|
var defaultRoleSnowflake = data.Configuration.DefaultRole.ToDiscordSnowflake();
|
||||||
|
|
||||||
foreach (var memberData in data.MemberData.Values) {
|
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))
|
if (defaultRoleSnowflake.Value is not 0 && !memberData.Roles.Contains(defaultRoleSnowflake))
|
||||||
_ = _guildApi.AddGuildMemberRoleAsync(
|
_ = _guildApi.AddGuildMemberRoleAsync(
|
||||||
guildId, userIdSnowflake, defaultRoleSnowflake, ct: ct);
|
guildId, userId, defaultRoleSnowflake, ct: ct);
|
||||||
|
|
||||||
if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
|
if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
|
||||||
var unbanResult = await _guildApi.RemoveGuildBanAsync(
|
var unbanResult = await _guildApi.RemoveGuildBanAsync(
|
||||||
guildId, userIdSnowflake, Messages.PunishmentExpired.EncodeHeader(), ct);
|
guildId, userId, Messages.PunishmentExpired.EncodeHeader(), ct);
|
||||||
if (unbanResult.IsSuccess)
|
if (unbanResult.IsSuccess)
|
||||||
memberData.BannedUntil = null;
|
memberData.BannedUntil = null;
|
||||||
else
|
else
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"Error in automatic user unban request.\n{ErrorMessage}", unbanResult.Error.Message);
|
"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);
|
var eventsResult = await _eventApi.ListScheduledEventsForGuildAsync(guildId, ct: ct);
|
||||||
|
|
Loading…
Add table
Reference in a new issue