Port the DM stuff
This commit is contained in:
parent
a2c8cbb560
commit
9079f1c59c
@ -55,6 +55,17 @@ namespace Myriad.Extensions
|
||||
return restUser;
|
||||
}
|
||||
|
||||
public static async ValueTask<Channel?> GetOrFetchChannel(this IDiscordCache cache, DiscordApiClient rest, ulong channelId)
|
||||
{
|
||||
if (cache.TryGetChannel(channelId, out var cacheChannel))
|
||||
return cacheChannel;
|
||||
|
||||
var restChannel = await rest.GetChannel(channelId);
|
||||
if (restChannel != null)
|
||||
await cache.SaveChannel(restChannel);
|
||||
return restChannel;
|
||||
}
|
||||
|
||||
public static async Task<Channel> GetOrCreateDmChannel(this IDiscordCache cache, DiscordApiClient rest, ulong recipientId)
|
||||
{
|
||||
if (cache.TryGetDmChannel(recipientId, out var cacheChannel))
|
||||
|
@ -1,7 +1,10 @@
|
||||
namespace Myriad.Gateway
|
||||
using Myriad.Utils;
|
||||
|
||||
namespace Myriad.Gateway
|
||||
{
|
||||
public record MessageUpdateEvent(ulong Id, ulong ChannelId): IGatewayEvent
|
||||
{
|
||||
public Optional<string?> Content { get; init; }
|
||||
// TODO: lots of partials
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ namespace Myriad.Serialization
|
||||
|
||||
opts.Converters.Add(new PermissionSetJsonConverter());
|
||||
opts.Converters.Add(new ShardInfoJsonConverter());
|
||||
opts.Converters.Add(new OptionalConverterFactory());
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
@ -7,26 +7,31 @@ using Myriad.Utils;
|
||||
|
||||
namespace Myriad.Serialization
|
||||
{
|
||||
public class OptionalConverter: JsonConverter<IOptional>
|
||||
public class OptionalConverterFactory: JsonConverterFactory
|
||||
{
|
||||
public override IOptional? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
public class Inner<T>: JsonConverter<Optional<T>>
|
||||
{
|
||||
var innerType = typeToConvert.GetGenericArguments()[0];
|
||||
var inner = JsonSerializer.Deserialize(ref reader, innerType, options);
|
||||
public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var inner = JsonSerializer.Deserialize<T>(ref reader, options);
|
||||
return new(inner!);
|
||||
}
|
||||
|
||||
// TODO: rewrite to JsonConverterFactory to cut down on reflection
|
||||
return (IOptional?) Activator.CreateInstance(
|
||||
typeof(Optional<>).MakeGenericType(innerType),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
null,
|
||||
new[] {inner},
|
||||
null);
|
||||
public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value.HasValue ? value.GetValue() : default, typeof(T), options);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IOptional value, JsonSerializerOptions options)
|
||||
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var innerType = value.GetType().GetGenericArguments()[0];
|
||||
JsonSerializer.Serialize(writer, value.GetValue(), innerType, options);
|
||||
var innerType = typeToConvert.GetGenericArguments()[0];
|
||||
return (JsonConverter?) Activator.CreateInstance(
|
||||
typeof(Inner<>).MakeGenericType(innerType),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type typeToConvert)
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Mail;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Myriad.Utils;
|
||||
|
||||
namespace Myriad.Types
|
||||
{
|
||||
@ -59,8 +62,8 @@ namespace Myriad.Types
|
||||
public Reference? MessageReference { get; set; }
|
||||
public MessageFlags Flags { get; init; }
|
||||
|
||||
// todo: null vs. absence
|
||||
public Message? ReferencedMessage { get; init; }
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public Optional<Message?> ReferencedMessage { get; init; }
|
||||
|
||||
public record Reference(ulong? GuildId, ulong? ChannelId, ulong? MessageId);
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Myriad.Serialization;
|
||||
|
||||
namespace Myriad.Utils
|
||||
namespace Myriad.Utils
|
||||
{
|
||||
public interface IOptional
|
||||
{
|
||||
bool HasValue { get; }
|
||||
object? GetValue();
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(OptionalConverter))]
|
||||
public readonly struct Optional<T>: IOptional
|
||||
{
|
||||
public Optional(T value)
|
||||
|
@ -101,18 +101,6 @@ namespace PluralKit.Bot
|
||||
internal IDatabase Database => _db;
|
||||
internal ModelRepository Repository => _repo;
|
||||
|
||||
public Task<DiscordMessage> Reply(string text, DiscordEmbed embed,
|
||||
IEnumerable<IMention>? mentions = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<DiscordMessage> Reply(DiscordEmbed embed,
|
||||
IEnumerable<IMention>? mentions = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Message> Reply(string text = null, Embed embed = null, AllowedMentions? mentions = null)
|
||||
{
|
||||
if (!BotPermissions.HasFlag(PermissionSet.SendMessages))
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Myriad.Cache;
|
||||
using Myriad.Extensions;
|
||||
using Myriad.Types;
|
||||
|
||||
|
@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Myriad.Rest.Exceptions;
|
||||
using Myriad.Types;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@ -140,14 +141,14 @@ namespace PluralKit.Bot
|
||||
|
||||
try
|
||||
{
|
||||
var dm = await ctx.Rest.CreateDmAsync(ctx.Author.Id);
|
||||
var dm = await ctx.Rest.CreateDmAsync(ctx.AuthorNew.Id);
|
||||
// TODO: send file
|
||||
var msg = await dm.SendFileAsync("system.json", stream, $"{Emojis.Success} Here you go!");
|
||||
await dm.SendMessageAsync($"<{msg.Attachments[0].Url}>");
|
||||
|
||||
// If the original message wasn't posted in DMs, send a public reminder
|
||||
// TODO: DMs
|
||||
// if (!(ctx.Channel is DiscordDmChannel))
|
||||
// await ctx.Reply($"{Emojis.Success} Check your DMs!");
|
||||
if (ctx.ChannelNew.Type == Channel.ChannelType.Dm)
|
||||
await ctx.Reply($"{Emojis.Success} Check your DMs!");
|
||||
}
|
||||
catch (UnauthorizedException)
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ namespace PluralKit.Bot
|
||||
public async Task ViewMember(Context ctx, PKMember target)
|
||||
{
|
||||
var system = await _db.Execute(c => _repo.GetSystem(c, target.System));
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.LookupContextFor(system)));
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(system, target, ctx.GuildNew, ctx.LookupContextFor(system)));
|
||||
}
|
||||
|
||||
public async Task Soulscream(Context ctx, PKMember target)
|
||||
|
@ -228,7 +228,7 @@ namespace PluralKit.Bot {
|
||||
var message = await _db.Execute(c => _repo.GetMessage(c, messageId));
|
||||
if (message == null) throw Errors.MessageNotFound(messageId);
|
||||
|
||||
await ctx.Reply(embed: await _embeds.CreateMessageInfoEmbed(ctx.Shard, message));
|
||||
await ctx.Reply(embed: await _embeds.CreateMessageInfoEmbed(message));
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ namespace PluralKit.Bot
|
||||
throw new PKError("Your system has no members! Please create at least one member before using this command.");
|
||||
|
||||
var randInt = randGen.Next(members.Count);
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(ctx.System, members[randInt], ctx.Guild, ctx.LookupContextFor(ctx.System)));
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(ctx.System, members[randInt], ctx.GuildNew, ctx.LookupContextFor(ctx.System)));
|
||||
}
|
||||
|
||||
public async Task Group(Context ctx)
|
||||
@ -73,7 +73,7 @@ namespace PluralKit.Bot
|
||||
var ms = members.ToList();
|
||||
|
||||
var randInt = randGen.Next(ms.Count);
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(ctx.System, ms[randInt], ctx.Guild, ctx.LookupContextFor(ctx.System)));
|
||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(ctx.System, ms[randInt], ctx.GuildNew, ctx.LookupContextFor(ctx.System)));
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,6 @@ namespace PluralKit.Bot
|
||||
async Task Inner()
|
||||
{
|
||||
await Task.Delay(MessageDeleteDelay);
|
||||
// TODO
|
||||
await _db.Execute(c => _repo.DeleteMessage(c, evt.Id));
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using Myriad.Gateway;
|
||||
using Myriad.Rest;
|
||||
using Myriad.Rest.Exceptions;
|
||||
using Myriad.Rest.Types;
|
||||
using Myriad.Rest.Types.Requests;
|
||||
using Myriad.Types;
|
||||
|
||||
using PluralKit.Core;
|
||||
@ -22,10 +23,11 @@ namespace PluralKit.Bot
|
||||
private readonly CommandMessageService _commandMessageService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDiscordCache _cache;
|
||||
private readonly EmbedService _embeds;
|
||||
private readonly Bot _bot;
|
||||
private readonly DiscordApiClient _rest;
|
||||
|
||||
public ReactionAdded(ILogger logger, IDatabase db, ModelRepository repo, CommandMessageService commandMessageService, IDiscordCache cache, Bot bot, DiscordApiClient rest)
|
||||
public ReactionAdded(ILogger logger, IDatabase db, ModelRepository repo, CommandMessageService commandMessageService, IDiscordCache cache, Bot bot, DiscordApiClient rest, EmbedService embeds)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
@ -33,6 +35,7 @@ namespace PluralKit.Bot
|
||||
_cache = cache;
|
||||
_bot = bot;
|
||||
_rest = rest;
|
||||
_embeds = embeds;
|
||||
_logger = logger.ForContext<ReactionAdded>();
|
||||
}
|
||||
|
||||
@ -151,13 +154,22 @@ namespace PluralKit.Bot
|
||||
|
||||
private async ValueTask HandleQueryReaction(MessageReactionAddEvent evt, FullMessage msg)
|
||||
{
|
||||
var guild = _cache.GetGuild(evt.GuildId!.Value);
|
||||
|
||||
// Try to DM the user info about the message
|
||||
// var member = await evt.Guild.GetMember(evt.User.Id);
|
||||
try
|
||||
{
|
||||
// TODO: how to DM?
|
||||
// await member.SendMessageAsync(embed: await _embeds.CreateMemberEmbed(msg.System, msg.Member, evt.Guild, LookupContext.ByNonOwner));
|
||||
// await member.SendMessageAsync(embed: await _embeds.CreateMessageInfoEmbed(shard, msg));
|
||||
var dm = await _cache.GetOrCreateDmChannel(_rest, evt.UserId);
|
||||
await _rest.CreateMessage(dm.Id, new MessageRequest
|
||||
{
|
||||
Embed = await _embeds.CreateMemberEmbed(msg.System, msg.Member, guild, LookupContext.ByNonOwner)
|
||||
});
|
||||
|
||||
await _rest.CreateMessage(dm.Id, new MessageRequest
|
||||
{
|
||||
Embed = await _embeds.CreateMessageInfoEmbed(msg)
|
||||
});
|
||||
}
|
||||
catch (UnauthorizedException) { } // No permissions to DM, can't check for this :(
|
||||
|
||||
@ -192,9 +204,12 @@ namespace PluralKit.Bot
|
||||
// If not, tell them in DMs (if we can)
|
||||
try
|
||||
{
|
||||
// todo: how to dm
|
||||
// await guildUser.SendMessageFixedAsync($"{Emojis.Error} {msg.Member.DisplayName()}'s system has disabled reaction pings. If you want to mention them anyway, you can copy/paste the following message:");
|
||||
// await guildUser.SendMessageFixedAsync($"<@{msg.Message.Sender}>".AsCode());
|
||||
var dm = await _cache.GetOrCreateDmChannel(_rest, evt.UserId);
|
||||
await _rest.CreateMessage(dm.Id, new MessageRequest
|
||||
{
|
||||
Content = $"{Emojis.Error} {msg.Member.DisplayName()}'s system has disabled reaction pings. If you want to mention them anyway, you can copy/paste the following message:"
|
||||
});
|
||||
await _rest.CreateMessage(dm.Id, new MessageRequest {Content = $"<@{msg.Message.Sender}>".AsCode()});
|
||||
}
|
||||
catch (UnauthorizedException) { }
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using DSharpPlus;
|
||||
using DSharpPlus.Entities;
|
||||
|
||||
using Humanizer;
|
||||
|
||||
using Myriad.Builders;
|
||||
using Myriad.Cache;
|
||||
using Myriad.Extensions;
|
||||
using Myriad.Rest;
|
||||
using Myriad.Types;
|
||||
|
||||
@ -22,13 +22,11 @@ namespace PluralKit.Bot {
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly IDiscordCache _cache;
|
||||
private readonly DiscordApiClient _rest;
|
||||
|
||||
public EmbedService(DiscordShardedClient client, IDatabase db, ModelRepository repo, IDiscordCache cache, DiscordApiClient rest)
|
||||
public EmbedService(IDatabase db, ModelRepository repo, IDiscordCache cache, DiscordApiClient rest)
|
||||
{
|
||||
_client = client;
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
_cache = cache;
|
||||
@ -39,14 +37,7 @@ namespace PluralKit.Bot {
|
||||
{
|
||||
async Task<(ulong Id, User? User)> Inner(ulong id)
|
||||
{
|
||||
if (_cache.TryGetUser(id, out var cachedUser))
|
||||
return (id, cachedUser);
|
||||
|
||||
var user = await _rest.GetUser(id);
|
||||
if (user == null)
|
||||
return (id, null);
|
||||
// todo: move to "GetUserCached" helper
|
||||
await _cache.SaveUser(user);
|
||||
var user = await _cache.GetOrFetchUser(_rest, id);
|
||||
return (id, user);
|
||||
}
|
||||
|
||||
@ -108,7 +99,7 @@ namespace PluralKit.Bot {
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async Task<Embed> CreateMemberEmbed(PKSystem system, PKMember member, DiscordGuild guild, LookupContext ctx)
|
||||
public async Task<Embed> CreateMemberEmbed(PKSystem system, PKMember member, Guild guild, LookupContext ctx)
|
||||
{
|
||||
|
||||
// string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(system.Zone));
|
||||
@ -233,26 +224,33 @@ namespace PluralKit.Bot {
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async Task<Embed> CreateMessageInfoEmbed(DiscordClient client, FullMessage msg)
|
||||
public async Task<Embed> CreateMessageInfoEmbed(FullMessage msg)
|
||||
{
|
||||
var channel = await _cache.GetOrFetchChannel(_rest, msg.Message.Channel);
|
||||
var ctx = LookupContext.ByNonOwner;
|
||||
var channel = await _client.GetChannel(msg.Message.Channel);
|
||||
var serverMsg = channel != null ? await channel.GetMessage(msg.Message.Mid) : null;
|
||||
var serverMsg = channel != null ? await _rest.GetMessage(msg.Message.Channel, msg.Message.Mid) : null;
|
||||
|
||||
// Need this whole dance to handle cases where:
|
||||
// - the user is deleted (userInfo == null)
|
||||
// - the bot's no longer in the server we're querying (channel == null)
|
||||
// - the member is no longer in the server we're querying (memberInfo == null)
|
||||
DiscordMember memberInfo = null;
|
||||
DiscordUser userInfo = null;
|
||||
if (channel != null) memberInfo = await channel.Guild.GetMember(msg.Message.Sender);
|
||||
if (memberInfo != null) userInfo = memberInfo; // Don't do an extra request if we already have this info from the member lookup
|
||||
else userInfo = await client.GetUser(msg.Message.Sender);
|
||||
// TODO: optimize ordering here a bit with new cache impl; and figure what happens if bot leaves server -> channel still cached -> hits this bit and 401s?
|
||||
GuildMemberPartial memberInfo = null;
|
||||
User userInfo = null;
|
||||
if (channel != null)
|
||||
{
|
||||
var m = await _rest.GetGuildMember(channel.GuildId!.Value, msg.Message.Sender);
|
||||
if (m != null)
|
||||
// Don't do an extra request if we already have this info from the member lookup
|
||||
userInfo = m.User;
|
||||
memberInfo = m;
|
||||
}
|
||||
else userInfo = await _cache.GetOrFetchUser(_rest, msg.Message.Sender);
|
||||
|
||||
// Calculate string displayed under "Sent by"
|
||||
string userStr;
|
||||
if (memberInfo != null && memberInfo.Nickname != null)
|
||||
userStr = $"**Username:** {memberInfo.NameAndMention()}\n**Nickname:** {memberInfo.Nickname}";
|
||||
if (memberInfo != null && memberInfo.Nick != null)
|
||||
userStr = $"**Username:** {userInfo.NameAndMention()}\n**Nickname:** {memberInfo.Nick}";
|
||||
else if (userInfo != null) userStr = userInfo.NameAndMention();
|
||||
else userStr = $"*(deleted user {msg.Message.Sender})*";
|
||||
|
||||
@ -270,7 +268,8 @@ namespace PluralKit.Bot {
|
||||
var roles = memberInfo?.Roles?.ToList();
|
||||
if (roles != null && roles.Count > 0)
|
||||
{
|
||||
var rolesString = string.Join(", ", roles.Select(role => role.Name));
|
||||
// TODO: what if role isn't in cache? figure out a fallback
|
||||
var rolesString = string.Join(", ", roles.Select(id => _cache.GetRole(id).Name));
|
||||
eb.Field(new($"Account roles ({roles.Count})", rolesString.Truncate(1024)));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user