2023-09-29 15:31:45 +03:00
|
|
|
using System.ComponentModel;
|
|
|
|
using System.Drawing;
|
|
|
|
using System.Text;
|
|
|
|
using JetBrains.Annotations;
|
2023-09-30 16:58:32 +03:00
|
|
|
using Octobot.Data;
|
2023-10-12 18:37:25 +03:00
|
|
|
using Octobot.Extensions;
|
2023-12-31 15:27:00 +03:00
|
|
|
using Octobot.Parsers;
|
2023-09-30 16:58:32 +03:00
|
|
|
using Octobot.Services;
|
2023-09-29 15:31:45 +03:00
|
|
|
using Remora.Commands.Attributes;
|
|
|
|
using Remora.Commands.Groups;
|
|
|
|
using Remora.Discord.API.Abstractions.Objects;
|
|
|
|
using Remora.Discord.API.Abstractions.Rest;
|
|
|
|
using Remora.Discord.Commands.Attributes;
|
|
|
|
using Remora.Discord.Commands.Contexts;
|
|
|
|
using Remora.Discord.Commands.Feedback.Services;
|
|
|
|
using Remora.Discord.Extensions.Embeds;
|
|
|
|
using Remora.Discord.Extensions.Formatting;
|
|
|
|
using Remora.Rest.Core;
|
|
|
|
using Remora.Results;
|
|
|
|
|
2023-09-30 16:58:32 +03:00
|
|
|
namespace Octobot.Commands;
|
2023-09-29 15:31:45 +03:00
|
|
|
|
|
|
|
/// <summary>
|
2024-03-13 20:40:29 +03:00
|
|
|
/// Handles tool commands: /userinfo, /guildinfo, /random, /timestamp, /8ball.
|
2023-09-29 15:31:45 +03:00
|
|
|
/// </summary>
|
|
|
|
[UsedImplicitly]
|
|
|
|
public class ToolsCommandGroup : CommandGroup
|
|
|
|
{
|
|
|
|
private readonly ICommandContext _context;
|
2023-11-04 21:28:22 +03:00
|
|
|
private readonly IFeedbackService _feedback;
|
2023-09-29 15:31:45 +03:00
|
|
|
private readonly IDiscordRestGuildAPI _guildApi;
|
|
|
|
private readonly GuildDataService _guildData;
|
|
|
|
private readonly IDiscordRestUserAPI _userApi;
|
|
|
|
|
|
|
|
public ToolsCommandGroup(
|
2023-11-04 21:28:22 +03:00
|
|
|
ICommandContext context, IFeedbackService feedback,
|
2023-09-29 15:31:45 +03:00
|
|
|
GuildDataService guildData, IDiscordRestGuildAPI guildApi,
|
2024-03-18 21:23:42 +03:00
|
|
|
IDiscordRestUserAPI userApi)
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
|
|
|
_context = context;
|
|
|
|
_guildData = guildData;
|
|
|
|
_feedback = feedback;
|
|
|
|
_guildApi = guildApi;
|
|
|
|
_userApi = userApi;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A slash command that shows information about user.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// Information in the output:
|
|
|
|
/// <list type="bullet">
|
|
|
|
/// <item>Display name</item>
|
|
|
|
/// <item>Discord user since</item>
|
|
|
|
/// <item>Guild nickname</item>
|
|
|
|
/// <item>Guild member since</item>
|
|
|
|
/// <item>Nitro booster since</item>
|
|
|
|
/// <item>Guild roles</item>
|
|
|
|
/// <item>Active mute information</item>
|
|
|
|
/// <item>Active ban information</item>
|
|
|
|
/// <item>Is on guild status</item>
|
|
|
|
/// </list>
|
|
|
|
/// </remarks>
|
|
|
|
/// <param name="target">The user to show info about.</param>
|
|
|
|
/// <returns>
|
|
|
|
/// A feedback sending result which may or may not have succeeded.
|
|
|
|
/// </returns>
|
2023-10-06 15:35:08 +03:00
|
|
|
[Command("userinfo")]
|
2023-09-29 15:31:45 +03:00
|
|
|
[DiscordDefaultDMPermission(false)]
|
|
|
|
[Description("Shows info about user")]
|
|
|
|
[UsedImplicitly]
|
2023-10-06 15:35:08 +03:00
|
|
|
public async Task<Result> ExecuteUserInfoAsync(
|
2023-09-29 15:31:45 +03:00
|
|
|
[Description("User to show info about")]
|
|
|
|
IUser? target = null)
|
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
|
|
|
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
|
|
|
}
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
|
|
|
if (!botResult.IsDefined(out var bot))
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
return Result.FromError(botResult);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
|
|
|
if (!executorResult.IsDefined(out var executor))
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
return Result.FromError(executorResult);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var data = await _guildData.GetData(guildId, CancellationToken);
|
|
|
|
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
return await ShowUserInfoAsync(target ?? executor, bot, data, guildId, CancellationToken);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private async Task<Result> ShowUserInfoAsync(
|
2023-10-04 18:21:10 +03:00
|
|
|
IUser target, IUser bot, GuildData data, Snowflake guildId, CancellationToken ct = default)
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
var builder = new StringBuilder().AppendLine($"### <@{target.ID}>");
|
2023-09-29 15:31:45 +03:00
|
|
|
|
2024-02-06 15:09:26 +03:00
|
|
|
if (target.GlobalName.IsDefined(out var globalName))
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoDisplayName)
|
2024-02-06 15:09:26 +03:00
|
|
|
.AppendLine(Markdown.InlineCode(globalName));
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoDiscordUserSince)
|
2023-10-04 18:21:10 +03:00
|
|
|
.AppendLine(Markdown.Timestamp(target.ID.Timestamp));
|
2023-09-29 15:31:45 +03:00
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var memberData = data.GetOrCreateMemberData(target.ID);
|
2023-09-29 15:31:45 +03:00
|
|
|
|
|
|
|
var embedColor = ColorsList.Cyan;
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var guildMemberResult = await _guildApi.GetGuildMemberAsync(guildId, target.ID, ct);
|
2023-09-29 15:31:45 +03:00
|
|
|
DateTimeOffset? communicationDisabledUntil = null;
|
|
|
|
if (guildMemberResult.IsDefined(out var guildMember))
|
|
|
|
{
|
|
|
|
communicationDisabledUntil = guildMember.CommunicationDisabledUntil.OrDefault(null);
|
|
|
|
|
|
|
|
embedColor = AppendGuildInformation(embedColor, guildMember, builder);
|
|
|
|
}
|
|
|
|
|
2023-12-21 18:35:10 +03:00
|
|
|
var wasMuted = (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) ||
|
|
|
|
communicationDisabledUntil is not null;
|
|
|
|
var wasBanned = memberData.BannedUntil is not null;
|
|
|
|
var wasKicked = memberData.Kicked;
|
2023-09-29 15:31:45 +03:00
|
|
|
|
2023-12-21 18:35:10 +03:00
|
|
|
if (wasMuted || wasBanned || wasKicked)
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
|
|
|
builder.Append("### ")
|
2023-10-06 15:35:08 +03:00
|
|
|
.AppendLine(Markdown.Bold(Messages.UserInfoPunishments));
|
2023-09-29 15:31:45 +03:00
|
|
|
|
2023-12-21 18:35:10 +03:00
|
|
|
embedColor = AppendPunishmentsInformation(wasMuted, wasKicked, wasBanned, memberData,
|
|
|
|
builder, embedColor, communicationDisabledUntil);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
2023-12-21 18:35:10 +03:00
|
|
|
if (!guildMemberResult.IsSuccess && !wasBanned)
|
2023-09-29 15:31:45 +03:00
|
|
|
{
|
|
|
|
builder.Append("### ")
|
2023-10-06 15:35:08 +03:00
|
|
|
.AppendLine(Markdown.Bold(Messages.UserInfoNotOnGuild));
|
2023-09-29 15:31:45 +03:00
|
|
|
|
|
|
|
embedColor = ColorsList.Default;
|
|
|
|
}
|
|
|
|
|
|
|
|
var embed = new EmbedBuilder().WithSmallTitle(
|
2023-10-06 15:23:45 +03:00
|
|
|
string.Format(Messages.InformationAbout, target.GetTag()), bot)
|
2023-09-29 15:31:45 +03:00
|
|
|
.WithDescription(builder.ToString())
|
|
|
|
.WithColour(embedColor)
|
2023-10-06 15:23:45 +03:00
|
|
|
.WithLargeUserAvatar(target)
|
2023-10-04 18:21:10 +03:00
|
|
|
.WithFooter($"ID: {target.ID.ToString()}")
|
2023-09-29 15:31:45 +03:00
|
|
|
.Build();
|
|
|
|
|
2023-12-17 18:44:18 +03:00
|
|
|
return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
2023-12-21 18:35:10 +03:00
|
|
|
private static Color AppendPunishmentsInformation(bool wasMuted, bool wasKicked, bool wasBanned,
|
|
|
|
MemberData memberData, StringBuilder builder, Color embedColor, DateTimeOffset? communicationDisabledUntil)
|
|
|
|
{
|
|
|
|
if (wasMuted)
|
|
|
|
{
|
|
|
|
AppendMuteInformation(memberData, communicationDisabledUntil, builder);
|
|
|
|
embedColor = ColorsList.Red;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wasKicked)
|
|
|
|
{
|
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoKicked);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wasBanned)
|
|
|
|
{
|
|
|
|
AppendBanInformation(memberData, builder);
|
|
|
|
embedColor = ColorsList.Black;
|
|
|
|
}
|
|
|
|
|
|
|
|
return embedColor;
|
|
|
|
}
|
|
|
|
|
2023-09-29 15:31:45 +03:00
|
|
|
private static Color AppendGuildInformation(Color color, IGuildMember guildMember, StringBuilder builder)
|
|
|
|
{
|
|
|
|
if (guildMember.Nickname.IsDefined(out var nickname))
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoGuildNickname)
|
2023-09-29 15:31:45 +03:00
|
|
|
.AppendLine(Markdown.InlineCode(nickname));
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoGuildMemberSince)
|
2023-09-29 15:31:45 +03:00
|
|
|
.AppendLine(Markdown.Timestamp(guildMember.JoinedAt));
|
|
|
|
|
|
|
|
if (guildMember.PremiumSince.IsDefined(out var premiumSince))
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoGuildMemberPremiumSince)
|
2023-09-29 15:31:45 +03:00
|
|
|
.AppendLine(Markdown.Timestamp(premiumSince.Value));
|
|
|
|
color = ColorsList.Magenta;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (guildMember.Roles.Count > 0)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoGuildRoles);
|
2023-09-29 15:31:45 +03:00
|
|
|
for (var i = 0; i < guildMember.Roles.Count - 1; i++)
|
|
|
|
{
|
|
|
|
builder.Append($"<@&{guildMember.Roles[i]}>, ");
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.AppendLine($"<@&{guildMember.Roles[^1]}>");
|
|
|
|
}
|
|
|
|
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void AppendBanInformation(MemberData memberData, StringBuilder builder)
|
|
|
|
{
|
|
|
|
if (memberData.BannedUntil < DateTimeOffset.MaxValue)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoBanned)
|
|
|
|
.AppendSubBulletPointLine(string.Format(
|
2023-09-29 15:31:45 +03:00
|
|
|
Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.BannedUntil.Value)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoBannedPermanently);
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private static void AppendMuteInformation(
|
|
|
|
MemberData memberData, DateTimeOffset? communicationDisabledUntil, StringBuilder builder)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendBulletPointLine(Messages.UserInfoMuted);
|
2023-09-29 15:31:45 +03:00
|
|
|
if (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendSubBulletPointLine(Messages.UserInfoMutedByMuteRole)
|
|
|
|
.AppendSubBulletPointLine(string.Format(
|
2023-09-29 15:31:45 +03:00
|
|
|
Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.MutedUntil.Value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (communicationDisabledUntil is not null)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
builder.AppendSubBulletPointLine(Messages.UserInfoMutedByTimeout)
|
|
|
|
.AppendSubBulletPointLine(string.Format(
|
2023-09-29 15:31:45 +03:00
|
|
|
Messages.DescriptionActionExpiresAt, Markdown.Timestamp(communicationDisabledUntil.Value)));
|
|
|
|
}
|
|
|
|
}
|
2023-09-29 19:22:44 +03:00
|
|
|
|
2023-10-06 15:23:45 +03:00
|
|
|
/// <summary>
|
|
|
|
/// A slash command that shows guild information.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// Information in the output:
|
|
|
|
/// <list type="bullet">
|
|
|
|
/// <item>Guild description</item>
|
|
|
|
/// <item>Creation date</item>
|
|
|
|
/// <item>Guild's language</item>
|
|
|
|
/// <item>Guild's owner</item>
|
|
|
|
/// <item>Boost level</item>
|
|
|
|
/// <item>Boost count</item>
|
|
|
|
/// </list>
|
|
|
|
/// </remarks>
|
|
|
|
/// <returns>
|
|
|
|
/// A feedback sending result which may or may not have succeeded.
|
|
|
|
/// </returns>
|
|
|
|
[Command("guildinfo")]
|
|
|
|
[DiscordDefaultDMPermission(false)]
|
|
|
|
[Description("Shows info current guild")]
|
|
|
|
[UsedImplicitly]
|
|
|
|
public async Task<Result> ExecuteGuildInfoAsync()
|
|
|
|
{
|
|
|
|
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
|
|
|
{
|
|
|
|
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
|
|
|
}
|
|
|
|
|
|
|
|
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
|
|
|
if (!botResult.IsDefined(out var bot))
|
|
|
|
{
|
|
|
|
return Result.FromError(botResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
var guildResult = await _guildApi.GetGuildAsync(guildId, ct: CancellationToken);
|
|
|
|
if (!guildResult.IsDefined(out var guild))
|
|
|
|
{
|
|
|
|
return Result.FromError(guildResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
var data = await _guildData.GetData(guildId, CancellationToken);
|
|
|
|
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
|
|
|
|
|
|
|
return await ShowGuildInfoAsync(bot, guild, CancellationToken);
|
|
|
|
}
|
|
|
|
|
2023-12-05 22:24:55 +03:00
|
|
|
private Task<Result> ShowGuildInfoAsync(IUser bot, IGuild guild, CancellationToken ct)
|
2023-10-06 15:23:45 +03:00
|
|
|
{
|
|
|
|
var description = new StringBuilder().AppendLine($"## {guild.Name}");
|
|
|
|
|
|
|
|
if (guild.Description is not null)
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
description.AppendBulletPointLine(Messages.GuildInfoDescription)
|
2023-10-06 15:23:45 +03:00
|
|
|
.AppendLine(Markdown.InlineCode(guild.Description));
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
description.AppendBulletPointLine(Messages.GuildInfoCreatedAt)
|
2023-10-06 15:23:45 +03:00
|
|
|
.AppendLine(Markdown.Timestamp(guild.ID.Timestamp))
|
2023-12-04 17:09:47 +03:00
|
|
|
.AppendBulletPointLine(Messages.GuildInfoOwner)
|
2023-10-06 15:23:45 +03:00
|
|
|
.AppendLine(Mention.User(guild.OwnerID));
|
|
|
|
|
|
|
|
var embedColor = ColorsList.Cyan;
|
|
|
|
|
|
|
|
if (guild.PremiumTier > PremiumTier.None)
|
|
|
|
{
|
|
|
|
description.Append("### ").AppendLine(Messages.GuildInfoServerBoost)
|
2023-12-04 17:09:47 +03:00
|
|
|
.AppendBulletPoint(Messages.GuildInfoBoostTier)
|
2023-10-06 15:23:45 +03:00
|
|
|
.Append(": ").AppendLine(Markdown.InlineCode(guild.PremiumTier.ToString()))
|
2023-12-04 17:09:47 +03:00
|
|
|
.AppendBulletPoint(Messages.GuildInfoBoostCount)
|
2023-10-06 15:23:45 +03:00
|
|
|
.Append(": ").AppendLine(Markdown.InlineCode(guild.PremiumSubscriptionCount.ToString()));
|
|
|
|
embedColor = ColorsList.Magenta;
|
|
|
|
}
|
|
|
|
|
|
|
|
var embed = new EmbedBuilder().WithSmallTitle(
|
|
|
|
string.Format(Messages.InformationAbout, guild.Name), bot)
|
|
|
|
.WithDescription(description.ToString())
|
|
|
|
.WithColour(embedColor)
|
|
|
|
.WithLargeGuildIcon(guild)
|
|
|
|
.WithGuildBanner(guild)
|
|
|
|
.WithFooter($"ID: {guild.ID.ToString()}")
|
|
|
|
.Build();
|
|
|
|
|
2023-12-17 18:44:18 +03:00
|
|
|
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
2023-10-06 15:23:45 +03:00
|
|
|
}
|
|
|
|
|
2023-09-29 19:22:44 +03:00
|
|
|
/// <summary>
|
|
|
|
/// A slash command that generates a random number using maximum and minimum numbers.
|
|
|
|
/// </summary>
|
2023-10-04 15:32:54 +03:00
|
|
|
/// <param name="first">The first number used for randomization.</param>
|
|
|
|
/// <param name="second">The second number used for randomization. Default value: 0</param>
|
2023-09-29 19:22:44 +03:00
|
|
|
/// <returns>
|
|
|
|
/// A feedback sending result which may or may not have succeeded.
|
|
|
|
/// </returns>
|
|
|
|
[Command("random")]
|
|
|
|
[DiscordDefaultDMPermission(false)]
|
|
|
|
[Description("Generates a random number")]
|
|
|
|
[UsedImplicitly]
|
|
|
|
public async Task<Result> ExecuteRandomAsync(
|
2023-10-04 15:32:54 +03:00
|
|
|
[Description("First number")] long first,
|
|
|
|
[Description("Second number (Default: 0)")]
|
|
|
|
long? second = null)
|
2023-09-29 19:22:44 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
2023-09-29 19:22:44 +03:00
|
|
|
{
|
|
|
|
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
|
|
|
}
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
|
|
|
if (!executorResult.IsDefined(out var executor))
|
2023-09-29 19:22:44 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
return Result.FromError(executorResult);
|
2023-09-29 19:22:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var data = await _guildData.GetData(guildId, CancellationToken);
|
|
|
|
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
return await SendRandomNumberAsync(first, second, executor, CancellationToken);
|
2023-09-29 19:22:44 +03:00
|
|
|
}
|
|
|
|
|
2023-12-05 22:24:55 +03:00
|
|
|
private Task<Result> SendRandomNumberAsync(long first, long? secondNullable,
|
2023-10-04 18:21:10 +03:00
|
|
|
IUser executor, CancellationToken ct)
|
2023-09-29 19:22:44 +03:00
|
|
|
{
|
2023-10-04 15:32:54 +03:00
|
|
|
const long secondDefault = 0;
|
|
|
|
var second = secondNullable ?? secondDefault;
|
|
|
|
|
|
|
|
var min = Math.Min(first, second);
|
|
|
|
var max = Math.Max(first, second);
|
|
|
|
|
|
|
|
var i = Random.Shared.NextInt64(min, max + 1);
|
|
|
|
|
|
|
|
var description = new StringBuilder().Append("# ").Append(i);
|
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
description.AppendLine().AppendBulletPoint(string.Format(
|
2023-10-04 15:32:54 +03:00
|
|
|
Messages.RandomMin, Markdown.InlineCode(min.ToString())));
|
|
|
|
if (secondNullable is null && first >= secondDefault)
|
2023-09-29 19:22:44 +03:00
|
|
|
{
|
2023-10-04 15:32:54 +03:00
|
|
|
description.Append(' ').Append(Messages.Default);
|
|
|
|
}
|
2023-09-29 19:22:44 +03:00
|
|
|
|
2023-12-04 17:09:47 +03:00
|
|
|
description.AppendLine().AppendBulletPoint(string.Format(
|
2023-10-04 15:32:54 +03:00
|
|
|
Messages.RandomMax, Markdown.InlineCode(max.ToString())));
|
|
|
|
if (secondNullable is null && first < secondDefault)
|
|
|
|
{
|
|
|
|
description.Append(' ').Append(Messages.Default);
|
2023-09-29 19:22:44 +03:00
|
|
|
}
|
|
|
|
|
2023-10-04 15:32:54 +03:00
|
|
|
var embedColor = ColorsList.Blue;
|
|
|
|
if (secondNullable is not null && min == max)
|
|
|
|
{
|
|
|
|
description.AppendLine().Append(Markdown.Italicise(Messages.RandomMinMaxSame));
|
|
|
|
embedColor = ColorsList.Red;
|
|
|
|
}
|
2023-09-29 19:22:44 +03:00
|
|
|
|
2023-10-04 15:32:54 +03:00
|
|
|
var embed = new EmbedBuilder().WithSmallTitle(
|
2023-10-04 18:21:10 +03:00
|
|
|
string.Format(Messages.RandomTitle, executor.GetTag()), executor)
|
2023-10-04 15:32:54 +03:00
|
|
|
.WithDescription(description.ToString())
|
|
|
|
.WithColour(embedColor)
|
2023-09-29 19:22:44 +03:00
|
|
|
.Build();
|
|
|
|
|
2023-12-17 18:44:18 +03:00
|
|
|
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
2023-09-29 19:22:44 +03:00
|
|
|
}
|
2023-10-03 15:07:41 +03:00
|
|
|
|
|
|
|
private static readonly TimestampStyle[] AllStyles =
|
2023-12-20 19:23:37 +03:00
|
|
|
[
|
2023-10-03 15:07:41 +03:00
|
|
|
TimestampStyle.ShortDate,
|
|
|
|
TimestampStyle.LongDate,
|
|
|
|
TimestampStyle.ShortTime,
|
|
|
|
TimestampStyle.LongTime,
|
|
|
|
TimestampStyle.ShortDateTime,
|
|
|
|
TimestampStyle.LongDateTime,
|
|
|
|
TimestampStyle.RelativeTime
|
2023-12-20 19:23:37 +03:00
|
|
|
];
|
2023-10-03 15:07:41 +03:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A slash command that shows the current timestamp with an optional offset in all styles supported by Discord.
|
|
|
|
/// </summary>
|
2023-12-31 15:27:00 +03:00
|
|
|
/// <param name="stringOffset">The offset for the current timestamp.</param>
|
2023-10-03 15:07:41 +03:00
|
|
|
/// <returns>
|
|
|
|
/// A feedback sending result which may or may not have succeeded.
|
|
|
|
/// </returns>
|
|
|
|
[Command("timestamp")]
|
|
|
|
[DiscordDefaultDMPermission(false)]
|
|
|
|
[Description("Shows a timestamp in all styles")]
|
|
|
|
[UsedImplicitly]
|
|
|
|
public async Task<Result> ExecuteTimestampAsync(
|
2023-12-31 15:27:00 +03:00
|
|
|
[Description("Offset from current time")] [Option("offset")]
|
|
|
|
string? stringOffset = null)
|
2023-10-03 15:07:41 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
2023-10-03 15:07:41 +03:00
|
|
|
{
|
|
|
|
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
|
|
|
}
|
|
|
|
|
2023-12-31 15:27:00 +03:00
|
|
|
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
|
|
|
if (!botResult.IsDefined(out var bot))
|
|
|
|
{
|
|
|
|
return Result.FromError(botResult);
|
|
|
|
}
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
|
|
|
if (!executorResult.IsDefined(out var executor))
|
2023-10-03 15:07:41 +03:00
|
|
|
{
|
2023-10-04 18:21:10 +03:00
|
|
|
return Result.FromError(executorResult);
|
2023-10-03 15:07:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var data = await _guildData.GetData(guildId, CancellationToken);
|
|
|
|
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
|
|
|
|
2023-12-31 15:27:00 +03:00
|
|
|
if (stringOffset is null)
|
|
|
|
{
|
|
|
|
return await SendTimestampAsync(null, executor, CancellationToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
var parseResult = TimeSpanParser.TryParse(stringOffset);
|
|
|
|
if (!parseResult.IsDefined(out var offset))
|
|
|
|
{
|
|
|
|
var failedEmbed = new EmbedBuilder()
|
|
|
|
.WithSmallTitle(Messages.InvalidTimeSpan, bot)
|
2024-03-17 16:46:53 +03:00
|
|
|
.WithDescription(Messages.TimeSpanExample)
|
2023-12-31 15:27:00 +03:00
|
|
|
.WithColour(ColorsList.Red)
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: CancellationToken);
|
|
|
|
}
|
|
|
|
|
2023-10-04 18:21:10 +03:00
|
|
|
return await SendTimestampAsync(offset, executor, CancellationToken);
|
2023-10-03 15:07:41 +03:00
|
|
|
}
|
|
|
|
|
2023-12-05 22:24:55 +03:00
|
|
|
private Task<Result> SendTimestampAsync(TimeSpan? offset, IUser executor, CancellationToken ct)
|
2023-10-03 15:07:41 +03:00
|
|
|
{
|
|
|
|
var timestamp = DateTimeOffset.UtcNow.Add(offset ?? TimeSpan.Zero).ToUnixTimeSeconds();
|
|
|
|
|
|
|
|
var description = new StringBuilder().Append("# ").AppendLine(timestamp.ToString());
|
|
|
|
|
|
|
|
if (offset is not null)
|
|
|
|
{
|
|
|
|
description.AppendLine(string.Format(
|
|
|
|
Messages.TimestampOffset, Markdown.InlineCode(offset.ToString() ?? string.Empty))).AppendLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var markdownTimestamp in AllStyles.Select(style => Markdown.Timestamp(timestamp, style)))
|
|
|
|
{
|
2023-12-04 17:09:47 +03:00
|
|
|
description.AppendBulletPoint(Markdown.InlineCode(markdownTimestamp))
|
2023-10-03 15:07:41 +03:00
|
|
|
.Append(" → ").AppendLine(markdownTimestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
var embed = new EmbedBuilder().WithSmallTitle(
|
2023-10-04 18:21:10 +03:00
|
|
|
string.Format(Messages.TimestampTitle, executor.GetTag()), executor)
|
2023-10-03 15:07:41 +03:00
|
|
|
.WithDescription(description.ToString())
|
|
|
|
.WithColour(ColorsList.Blue)
|
|
|
|
.Build();
|
|
|
|
|
2023-12-17 18:44:18 +03:00
|
|
|
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
2023-10-03 15:07:41 +03:00
|
|
|
}
|
2024-03-13 20:40:29 +03:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A slash command that shows a random answer from the Magic 8-Ball.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="question">Unused input.</param>
|
|
|
|
/// <remarks>
|
|
|
|
/// The 8-Ball answers were taken from <a href="https://en.wikipedia.org/wiki/Magic_8_Ball#Possible_answers">Wikipedia</a>.
|
|
|
|
/// </remarks>
|
|
|
|
/// <returns>
|
|
|
|
/// A feedback sending result which may or may not have succeeded.
|
|
|
|
/// </returns>
|
|
|
|
[Command("8ball")]
|
|
|
|
[DiscordDefaultDMPermission(false)]
|
|
|
|
[Description("Ask the Magic 8-Ball a question")]
|
|
|
|
[UsedImplicitly]
|
|
|
|
public async Task<Result> ExecuteEightBallAsync(
|
|
|
|
// let the user think he's actually asking the ball a question
|
|
|
|
string question)
|
|
|
|
{
|
|
|
|
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
|
|
|
{
|
|
|
|
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
|
|
|
}
|
|
|
|
|
|
|
|
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
|
|
|
if (!botResult.IsDefined(out var bot))
|
|
|
|
{
|
|
|
|
return Result.FromError(botResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
var data = await _guildData.GetData(guildId, CancellationToken);
|
|
|
|
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
|
|
|
|
|
|
|
return await AnswerEightBallAsync(bot, CancellationToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static readonly string[] AnswerTypes =
|
|
|
|
[
|
|
|
|
"Positive", "Questionable", "Neutral", "Negative"
|
|
|
|
];
|
|
|
|
|
|
|
|
private Task<Result> AnswerEightBallAsync(IUser bot, CancellationToken ct)
|
|
|
|
{
|
|
|
|
var typeNumber = Random.Shared.Next(0, 4);
|
|
|
|
var embedColor = typeNumber switch
|
|
|
|
{
|
|
|
|
0 => ColorsList.Blue,
|
|
|
|
1 => ColorsList.Green,
|
|
|
|
2 => ColorsList.Yellow,
|
|
|
|
3 => ColorsList.Red,
|
|
|
|
_ => throw new ArgumentOutOfRangeException(null, nameof(typeNumber))
|
|
|
|
};
|
|
|
|
|
|
|
|
var answer = $"EightBall{AnswerTypes[typeNumber]}{Random.Shared.Next(1, 6)}".Localized();
|
|
|
|
|
|
|
|
var embed = new EmbedBuilder().WithSmallTitle(answer, bot)
|
|
|
|
.WithColour(embedColor)
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
|
|
|
}
|
2023-09-29 15:31:45 +03:00
|
|
|
}
|