mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Remora.Discord part 3 out of ∞
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
c4835a4e78
commit
67a15f3822
8 changed files with 322 additions and 198 deletions
|
@ -62,7 +62,7 @@ public class Boyfriend {
|
||||||
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>();
|
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>();
|
||||||
|
|
||||||
services.Configure<DiscordGatewayClientOptions>(
|
services.Configure<DiscordGatewayClientOptions>(
|
||||||
options => options.Intents |= GatewayIntents.MessageContents);
|
options => options.Intents |= GatewayIntents.MessageContents | GatewayIntents.GuildMembers);
|
||||||
}
|
}
|
||||||
).ConfigureLogging(
|
).ConfigureLogging(
|
||||||
c => c.AddConsole()
|
c => c.AddConsole()
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DiffPlex" Version="1.7.1"/>
|
||||||
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
|
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.3.23174.8"/>
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.3.23174.8"/>
|
||||||
<PackageReference Include="Remora.Discord" Version="2023.3.0"/>
|
<PackageReference Include="Remora.Discord" Version="2023.3.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- TODO: remove this when done -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="old\**"/>
|
<Compile Remove="old\**"/>
|
||||||
<Compile Update="Messages.Designer.cs">
|
<Compile Update="Messages.Designer.cs">
|
||||||
|
@ -35,7 +37,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Remove="old\**"/>
|
<EmbeddedResource Remove="old\**"/>
|
||||||
<EmbeddedResource Update="Messages.resx.bak">
|
<EmbeddedResource Update="Messages.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
|
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using DiffPlex;
|
||||||
|
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;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
@ -29,15 +31,16 @@ public class GuildCreateResponder : IResponder<IGuildCreate> {
|
||||||
var guild = gatewayEvent.Guild.AsT0;
|
var guild = gatewayEvent.Guild.AsT0;
|
||||||
Boyfriend.Logger.LogInformation("Joined guild \"{Name}\"", guild.Name);
|
Boyfriend.Logger.LogInformation("Joined guild \"{Name}\"", guild.Name);
|
||||||
|
|
||||||
var channelResult = guild.ID.GetChannel("PrivateFeedbackChannel");
|
var channelResult = guild.ID.GetConfigChannel("PrivateFeedbackChannel");
|
||||||
if (!channelResult.IsDefined(out var channel)) return Result.FromSuccess();
|
if (!channelResult.IsDefined(out var channel)) return Result.FromSuccess();
|
||||||
|
|
||||||
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||||
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
||||||
|
|
||||||
if (guild.GetConfigBool("ReceiveStartupMessages").IsDefined(out var shouldSendStartupMessage)
|
if (!guild.GetConfigBool("ReceiveStartupMessages").IsDefined(out var shouldSendStartupMessage)
|
||||||
&& shouldSendStartupMessage) {
|
|| !shouldSendStartupMessage) return Result.FromSuccess();
|
||||||
Messages.Culture = guild.GetCulture();
|
|
||||||
|
Messages.Culture = guild.ID.GetGuildCulture();
|
||||||
var i = Random.Shared.Next(1, 4);
|
var i = Random.Shared.Next(1, 4);
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
|
@ -53,35 +56,31 @@ public class GuildCreateResponder : IResponder<IGuildCreate> {
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
channel, embeds: new[] { built }!, ct: ct);
|
channel, embeds: new[] { built }!, ct: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.FromSuccess();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
private readonly IDiscordRestAuditLogAPI _auditLogApi;
|
private readonly IDiscordRestAuditLogAPI _auditLogApi;
|
||||||
private readonly CacheService _cacheService;
|
private readonly CacheService _cacheService;
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
private readonly IDiscordRestUserAPI _userApi;
|
|
||||||
|
|
||||||
public MessageDeletedResponder(
|
public MessageDeletedResponder(
|
||||||
IDiscordRestChannelAPI channelApi, IDiscordRestUserAPI userApi, CacheService cacheService,
|
IDiscordRestAuditLogAPI auditLogApi, CacheService cacheService, IDiscordRestChannelAPI channelApi) {
|
||||||
IDiscordRestAuditLogAPI auditLogApi) {
|
|
||||||
_channelApi = channelApi;
|
|
||||||
_userApi = userApi;
|
|
||||||
_cacheService = cacheService;
|
|
||||||
_auditLogApi = auditLogApi;
|
_auditLogApi = auditLogApi;
|
||||||
|
_cacheService = cacheService;
|
||||||
|
_channelApi = channelApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IMessageDelete gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IMessageDelete gatewayEvent, CancellationToken ct = default) {
|
||||||
if (!gatewayEvent.GuildID.IsDefined(out var guildId)) return Result.FromSuccess();
|
if (!gatewayEvent.GuildID.IsDefined(out var guildId)) return Result.FromSuccess();
|
||||||
|
|
||||||
var channelResult = guildId.GetChannel("PrivateFeedbackChannel");
|
var channelResult = guildId.GetConfigChannel("PrivateFeedbackChannel");
|
||||||
if (!channelResult.IsDefined(out var channel)) return Result.FromSuccess();
|
if (!channelResult.IsDefined(out var logChannel)) return Result.FromSuccess();
|
||||||
|
|
||||||
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
||||||
new KeyHelpers.MessageCacheKey(gatewayEvent.ChannelID, gatewayEvent.ID), ct);
|
new KeyHelpers.MessageCacheKey(gatewayEvent.ChannelID, gatewayEvent.ID), ct);
|
||||||
if (messageResult.IsDefined(out var message)) {
|
if (!messageResult.IsDefined(out var message)) return Result.FromError(messageResult);
|
||||||
|
if (string.IsNullOrWhiteSpace(message.Content)) return Result.FromSuccess();
|
||||||
|
|
||||||
var auditLogResult = await _auditLogApi.GetGuildAuditLogAsync(
|
var auditLogResult = await _auditLogApi.GetGuildAuditLogAsync(
|
||||||
guildId, actionType: AuditLogEvent.MessageDelete, limit: 1, ct: ct);
|
guildId, actionType: AuditLogEvent.MessageDelete, limit: 1, ct: ct);
|
||||||
if (!auditLogResult.IsDefined(out var auditLogPage)) return Result.FromError(auditLogResult);
|
if (!auditLogResult.IsDefined(out var auditLogPage)) return Result.FromError(auditLogResult);
|
||||||
|
@ -93,29 +92,121 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
var user = message.Author;
|
var user = message.Author;
|
||||||
if (options.ChannelID == gatewayEvent.ChannelID
|
if (options.ChannelID == gatewayEvent.ChannelID
|
||||||
&& DateTimeOffset.UtcNow.Subtract(auditLog.ID.Timestamp).TotalSeconds <= 2) {
|
&& DateTimeOffset.UtcNow.Subtract(auditLog.ID.Timestamp).TotalSeconds <= 2) {
|
||||||
var userResult = await _userApi.GetUserAsync(auditLog.UserID!.Value, ct);
|
var userResult = await _cacheService.TryGetValueAsync<IUser>(
|
||||||
|
new KeyHelpers.UserCacheKey(auditLog.UserID!.Value), ct);
|
||||||
if (!userResult.IsDefined(out user)) return Result.FromError(userResult);
|
if (!userResult.IsDefined(out user)) return Result.FromError(userResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Messages.Culture = guildId.GetGuildCulture();
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithAuthor(string.Format(Messages.CachedMessageDeleted, message.Author))
|
.WithSmallTitle(
|
||||||
.WithTitle(
|
|
||||||
message.Author,
|
message.Author,
|
||||||
string.Format(
|
string.Format(
|
||||||
Messages.CachedMessageDeleted,
|
Messages.CachedMessageDeleted,
|
||||||
$"{message.Author.Username}#{message.Author.Discriminator:0000}"))
|
message.Author.GetTag()))
|
||||||
.WithDescription(Markdown.BlockCode(message.Content.SanitizeForBlockCode()))
|
.WithDescription(
|
||||||
|
$"{Mention.Channel(gatewayEvent.ChannelID)}\n{Markdown.BlockCode(message.Content.SanitizeForBlockCode())}")
|
||||||
.WithActionFooter(user)
|
.WithActionFooter(user)
|
||||||
.WithTimestamp(message.Timestamp)
|
.WithTimestamp(message.Timestamp)
|
||||||
.WithColour(Color.Crimson)
|
.WithColour(Color.Crimson)
|
||||||
.Build();
|
.Build();
|
||||||
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
|
logChannel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
|
private readonly CacheService _cacheService;
|
||||||
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
|
||||||
|
public MessageEditedResponder(CacheService cacheService, IDiscordRestChannelAPI channelApi) {
|
||||||
|
_cacheService = cacheService;
|
||||||
|
_channelApi = channelApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result> RespondAsync(IMessageUpdate gatewayEvent, CancellationToken ct = default) {
|
||||||
|
if (!gatewayEvent.GuildID.IsDefined(out var guildId)) return Result.FromSuccess();
|
||||||
|
if (!gatewayEvent.ChannelID.IsDefined(out var channelId))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ChannelID)));
|
||||||
|
if (!gatewayEvent.ID.IsDefined(out var messageId))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ID)));
|
||||||
|
if (!gatewayEvent.Content.IsDefined(out var newContent))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.Content)));
|
||||||
|
if (!gatewayEvent.EditedTimestamp.IsDefined(out var timestamp))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.EditedTimestamp)));
|
||||||
|
|
||||||
|
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
||||||
|
new KeyHelpers.MessageCacheKey(channelId, messageId), ct);
|
||||||
|
if (!messageResult.IsDefined(out var message)) return Result.FromError(messageResult);
|
||||||
|
if (string.IsNullOrWhiteSpace(message.Content)
|
||||||
|
|| string.IsNullOrWhiteSpace(newContent)
|
||||||
|
|| message.Content == newContent) return Result.FromSuccess();
|
||||||
|
|
||||||
|
var logChannelResult = guildId.GetConfigChannel("PrivateFeedbackChannel");
|
||||||
|
if (!logChannelResult.IsDefined(out var logChannel)) return Result.FromSuccess();
|
||||||
|
|
||||||
|
var currentUserResult = await _cacheService.TryGetValueAsync<IUser>(
|
||||||
|
new KeyHelpers.CurrentUserCacheKey(), ct);
|
||||||
|
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
||||||
|
|
||||||
|
var diff = new SideBySideDiffBuilder(Differ.Instance).BuildDiffModel(message.Content, newContent, true, true);
|
||||||
|
|
||||||
|
Messages.Culture = guildId.GetGuildCulture();
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithSmallTitle(
|
||||||
|
message.Author,
|
||||||
|
string.Format(Messages.CachedMessageEdited, message.Author.GetTag()),
|
||||||
|
$"https://discord.com/channels/{guildId}/{channelId}/{messageId}")
|
||||||
|
.WithDescription($"{Mention.Channel(message.ChannelID)}\n{diff.AsMarkdown()}")
|
||||||
|
.WithUserFooter(currentUser)
|
||||||
|
.WithTimestamp(timestamp.Value)
|
||||||
|
.WithColour(Color.Gold)
|
||||||
|
.Build();
|
||||||
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
|
logChannel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GuildMemberAddResponder : IResponder<IGuildMemberAdd> {
|
||||||
|
private readonly CacheService _cacheService;
|
||||||
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
|
||||||
|
public GuildMemberAddResponder(CacheService cacheService, IDiscordRestChannelAPI channelApi) {
|
||||||
|
_cacheService = cacheService;
|
||||||
|
_channelApi = channelApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default) {
|
||||||
|
if (!gatewayEvent.GuildID.GetConfigString("WelcomeMessage").IsDefined(out var welcomeMessage)
|
||||||
|
|| welcomeMessage is "off" or "disable" or "disabled")
|
||||||
|
return Result.FromSuccess();
|
||||||
|
if (welcomeMessage is "default" or "reset") {
|
||||||
|
Messages.Culture = gatewayEvent.GuildID.GetGuildCulture();
|
||||||
|
welcomeMessage = Messages.DefaultWelcomeMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gatewayEvent.GuildID.GetConfigChannel("PublicFeedbackChannel").IsDefined(out var channel))
|
||||||
|
return Result.FromSuccess();
|
||||||
|
if (!gatewayEvent.User.IsDefined(out var user))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.User)));
|
||||||
|
|
||||||
|
var guildResult = await _cacheService.TryGetValueAsync<IGuild>(
|
||||||
|
new KeyHelpers.GuildCacheKey(gatewayEvent.GuildID), ct);
|
||||||
|
if (!guildResult.IsDefined(out var guild)) return Result.FromError(guildResult);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithSmallTitle(user, string.Format(welcomeMessage, user.GetTag(), guild.Name))
|
||||||
|
.WithGuildFooter(guild)
|
||||||
|
.WithTimestamp(gatewayEvent.JoinedAt)
|
||||||
|
.WithColour(Color.LawnGreen)
|
||||||
|
.Build();
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
channel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
channel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Result)messageResult;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using DiffPlex.DiffBuilder.Model;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Objects;
|
using Remora.Discord.API.Objects;
|
||||||
using Remora.Discord.Extensions.Embeds;
|
using Remora.Discord.Extensions.Embeds;
|
||||||
|
using Remora.Discord.Extensions.Formatting;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
using Remora.Results;
|
using Remora.Results;
|
||||||
|
|
||||||
|
@ -21,15 +24,20 @@ public static class Extensions {
|
||||||
return value is not null ? Result<bool>.FromSuccess(value.Value) : Result<bool>.FromError(new NotFoundError());
|
return value is not null ? Result<bool>.FromSuccess(value.Value) : Result<bool>.FromError(new NotFoundError());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result<Snowflake> GetChannel(this Snowflake guildId, string key) {
|
public static Result<Snowflake> GetConfigChannel(this Snowflake guildId, string key) {
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<ulong?>($"GuildConfigs:{guildId}:{key}");
|
var value = Boyfriend.GuildConfiguration.GetValue<ulong?>($"GuildConfigs:{guildId}:{key}");
|
||||||
return value is not null
|
return value is not null
|
||||||
? Result<Snowflake>.FromSuccess(DiscordSnowflake.New(value.Value))
|
? Result<Snowflake>.FromSuccess(DiscordSnowflake.New(value.Value))
|
||||||
: Result<Snowflake>.FromError(new NotFoundError());
|
: Result<Snowflake>.FromError(new NotFoundError());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CultureInfo GetCulture(this IGuild guild) {
|
public static Result<string> GetConfigString(this Snowflake guildId, string key) {
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<string?>($"GuildConfigs:{guild.ID}:Language");
|
var value = Boyfriend.GuildConfiguration.GetValue<string?>($"GuildConfigs:{guildId}:{key}");
|
||||||
|
return value is not null ? Result<string>.FromSuccess(value) : Result<string>.FromError(new NotFoundError());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CultureInfo GetGuildCulture(this Snowflake guildId) {
|
||||||
|
var value = Boyfriend.GuildConfiguration.GetValue<string?>($"GuildConfigs:{guildId}:Language");
|
||||||
return value is not null ? CultureInfoCache[value] : CultureInfoCache["en"];
|
return value is not null ? CultureInfoCache[value] : CultureInfoCache["en"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +47,7 @@ public static class Extensions {
|
||||||
? avatarUrlResult.Entity.AbsoluteUri
|
? avatarUrlResult.Entity.AbsoluteUri
|
||||||
: CDN.GetDefaultUserAvatarUrl(user, imageSize: 256).Entity.AbsoluteUri;
|
: CDN.GetDefaultUserAvatarUrl(user, imageSize: 256).Entity.AbsoluteUri;
|
||||||
|
|
||||||
return builder.WithFooter(new EmbedFooter($"{user.Username}#{user.Discriminator:0000}", avatarUrl));
|
return builder.WithFooter(new EmbedFooter(user.GetTag(), avatarUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EmbedBuilder WithActionFooter(this EmbedBuilder builder, IUser user) {
|
public static EmbedBuilder WithActionFooter(this EmbedBuilder builder, IUser user) {
|
||||||
|
@ -49,21 +57,44 @@ public static class Extensions {
|
||||||
: CDN.GetDefaultUserAvatarUrl(user, imageSize: 256).Entity.AbsoluteUri;
|
: CDN.GetDefaultUserAvatarUrl(user, imageSize: 256).Entity.AbsoluteUri;
|
||||||
|
|
||||||
return builder.WithFooter(
|
return builder.WithFooter(
|
||||||
new EmbedFooter($"{Messages.IssuedBy}:\n{user.Username}#{user.Discriminator:0000}", avatarUrl));
|
new EmbedFooter($"{Messages.IssuedBy}:\n{user.GetTag()}", avatarUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EmbedBuilder WithTitle(this EmbedBuilder builder, IUser avatarSource, string text) {
|
public static EmbedBuilder WithSmallTitle(
|
||||||
|
this EmbedBuilder builder, IUser avatarSource, string text, string? url = default) {
|
||||||
var avatarUrlResult = CDN.GetUserAvatarUrl(avatarSource, imageSize: 256);
|
var avatarUrlResult = CDN.GetUserAvatarUrl(avatarSource, imageSize: 256);
|
||||||
|
|
||||||
var avatarUrl = avatarUrlResult.IsSuccess
|
var avatarUrl = avatarUrlResult.IsSuccess
|
||||||
? avatarUrlResult.Entity
|
? avatarUrlResult.Entity
|
||||||
: CDN.GetDefaultUserAvatarUrl(avatarSource, imageSize: 256).Entity;
|
: CDN.GetDefaultUserAvatarUrl(avatarSource, imageSize: 256).Entity;
|
||||||
|
|
||||||
builder.Author = new EmbedAuthorBuilder(text, iconUrl: avatarUrl.AbsoluteUri);
|
builder.Author = new EmbedAuthorBuilder(text, url, avatarUrl.AbsoluteUri);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EmbedBuilder WithGuildFooter(this EmbedBuilder builder, IGuild guild) {
|
||||||
|
var iconUrlResult = CDN.GetGuildIconUrl(guild, imageSize: 256);
|
||||||
|
var iconUrl = iconUrlResult.IsSuccess
|
||||||
|
? iconUrlResult.Entity.AbsoluteUri
|
||||||
|
: default(Optional<string>);
|
||||||
|
|
||||||
|
return builder.WithFooter(new EmbedFooter(guild.Name, iconUrl));
|
||||||
|
}
|
||||||
|
|
||||||
public static string SanitizeForBlockCode(this string s) {
|
public static string SanitizeForBlockCode(this string s) {
|
||||||
return s.Replace("```", "```");
|
return s.Replace("```", "```");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AsMarkdown(this SideBySideDiffModel model) {
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
foreach (var line in model.OldText.Lines.Where(piece => !string.IsNullOrWhiteSpace(piece.Text)))
|
||||||
|
builder.Append("-- ").AppendLine(line.Text);
|
||||||
|
foreach (var line in model.NewText.Lines) builder.Append("++ ").AppendLine(line.Text);
|
||||||
|
|
||||||
|
return Markdown.BlockCode(builder.ToString().SanitizeForBlockCode(), "diff");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetTag(this IUser user) {
|
||||||
|
return $"{user.Username}#{user.Discriminator:0000}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
<value>Cleared message from {0} in channel {1}: {2}</value>
|
<value>Cleared message from {0} in channel {1}: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CachedMessageEdited" xml:space="preserve">
|
<data name="CachedMessageEdited" xml:space="preserve">
|
||||||
<value>Edited message in channel {0}: {1} -> {2}</value>
|
<value>Edited message by {0}:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
||||||
<value>{0}, welcome to {1}</value>
|
<value>{0}, welcome to {1}</value>
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
<value>Очищено сообщение от {0} в канале {1}: {2}</value>
|
<value>Очищено сообщение от {0} в канале {1}: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CachedMessageEdited" xml:space="preserve">
|
<data name="CachedMessageEdited" xml:space="preserve">
|
||||||
<value>Отредактировано сообщение в канале {0}: {1} -> {2}</value>
|
<value>Сообщение {0} отредактировано:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
||||||
<value>{0}, добро пожаловать на сервер {1}</value>
|
<value>{0}, добро пожаловать на сервер {1}</value>
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
<value>вырезано сообщение (используя `!clear`) от {0} в канале {1}: {2}</value>
|
<value>вырезано сообщение (используя `!clear`) от {0} в канале {1}: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CachedMessageEdited" xml:space="preserve">
|
<data name="CachedMessageEdited" xml:space="preserve">
|
||||||
<value>переделано сообщение от {0}: {1} -> {2}</value>
|
<value>сообщение {0} переделано:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
<data name="DefaultWelcomeMessage" xml:space="preserve">
|
||||||
<value>{0}, добро пожаловать на сервер {1}</value>
|
<value>{0}, добро пожаловать на сервер {1}</value>
|
||||||
|
|
Loading…
Add table
Reference in a new issue