将本地图像与 EmbedBuilder 结合使用

Using a local image with EmbedBuilder

根据Discord.NETdocumentation page for the EmbedBuilder class的语法(转换为VB)添加一个本地图片 EmbedBuilder 对象应该看起来像这样:

    Dim fileName = "image.png"
    Dim embed = New EmbedBuilder() With {
        .ImageUrl = $"attachment://{fileName}"
    }.Build()

我正在尝试使用类似这样的方法将动态创建的图像添加到 EmbedBuilder,但我似乎无法使其正常工作。这基本上是我得到的:

Dim TweetBuilder As New Discord.EmbedBuilder
Dim DynamicImagePath As String = CreateDynamicImage()
Dim AttachURI As String = $"attachment:///" & DynamicImagePath.Replace("\", "/").Replace(" ", "%20")

With Builder
    .Description = "SAMPLE DESCRIPTION"
    .ImageUrl = AttachURI
End With

MyClient.GetGuild(ServerID).GetTextChannel(PostChannelID).SendMessageAsync("THIS IS A TEST", False, Builder.Build)

我的 CreateDynamicImage 方法 returns 本地创建图像的完整路径(例如,C:\Folder\Another Folder\image.png)。我已经为此进行了大量的“战斗”/测试,以克服我最初因为路径中的 [SPACE] 而遇到的 Url must be a well-formed URI 异常。

MyClient 是在别处设置的 Discord.WebSocket.SocketClient 对象。

SendMessageAsync方法确实在正确的频道上将嵌入发送到 Discord,但没有嵌入图像。

如果我改为使用 SendFileAsync 方法发送图像(像这样):

MyClient.GetGuild(ServerID).GetTextChannel(PostChannelID).SendFileAsync(DynamicImagePath, "THIS IS A TEST", False, Builder.Build)

图像已发送,但作为消息的一部分,而不是作为嵌入的一部分包含(这是预期的行为 - 我只提出它b/c 这是我测试的一部分,以确保将图像实际发送到 Discord 没有问题。

我试过使用 file:/// 方案而不是 attachment:/// 方案,但这导致整个 post 根本无法进入 Discord。

此外,我已经尝试将 ImageUrl 属性 设置为 Web 资源(例如,https://www.somesite.com/someimage.png)并且嵌入看起来与图像和所有内容完全符合预期成功 post 到 Discord。

所以,我现在想知道我是不是漏掉了什么,还是我做的完全错了?

我已经尽我所能,但我还是卡在了你现在所在的位置。 我的猜测是 Discord 不喜欢来自 https://cdn.discordapp.com/attachments, and only accepts the new files from https://media.discordapp.net 的嵌入图像。不过我可能是错的,这就是它对我有用的方式。

我认为这只是一个视觉故障,我发现如果您在常规 Discord 客户端中为来自 cdn.discordapp.com/attchments 的图像发送 link,它会出错并由于某种原因显示了一个空嵌入。

这是有道理的,因为嵌入图像中使用的默认 link 实际上以 https://cdn.discordapp.com/attachments/.. 开头。

您可以使用 https://media.discordapp.net 解决此问题,但似乎 Discord.net 配置为使用旧域。

我 cross-posted 把这个发给 issue #1609 in the Discord.Net GitHub project 以便更好地了解对此有哪些选项可用,并收到了对该问题的很好解释:

The Embed (and EmbedImage) objects don't do anything with files. They simply pass the URI as configured straight into Discord. Discord then expects a URI in the form attachment://filename.ext if you want to refer to an attached image.

What you need to do is use SendFileAsync with the embed. You have two options here:

Use SendFileAsync with the Stream stream, string filename overload. I think this makes it clear what you need to do: you provide a file stream (via File.OpenRead or similar) and a filename. The provided filename does not have to match any file on disk. > So, for example:

var embed = new EmbedBuilder()
    .WithImageUrl("attachment://myimage.png")
    .Build();
await channel.SendFileAsync(stream, "myimage.png", embed: embed);

Alternatively, you can use SendFileAsync with the string filePath overload. Internally, this gets a stream of the file at the path, and sets filename (as sent to Discord) to the last part of the path. So it's equivalent to:

using var stream = File.OpenRead(filePath);
var filename = Path.GetFileName(filePath);
await channel.SendFileAsync(stream, filename);

From here, you can see that if you want to use the string filePath overload, you need to set embed image URI to something like $"attachment://{Path.GetFileName(filePath)}", because the attachment filename must match the one sent to Discord.

几乎用我上面的代码就可以了,但是我误解了方法的意图和用法,属性。我想我认为 .ImageUrl 属性 以某种方式“自动”在后台启动了 Stream。另外,我错过了一个非常重要的部分:

As it's an async method, you must await (or whatever the VB.NET equivalent is) on SendFileAsync.

所以,在将我的调用方法变成异步方法之后,我的代码现在看起来像这样:

Private Async Sub TestMessageToDiscord()
    Dim Builder As New Discord.EmbedBuilder
    Dim AttachmentPath As String = CreateDynamicImage()  '<-- Returns the full, local path to the created file

    With Builder
        .Description = "SAMPLE DESCRIPTION"
        .ImageUrl = $"attachment://{IO.Path.GetFileName(AttachmentPath)}"
    End With

    Using AttachmentStream As IO.Stream = IO.File.OpenRead(AttachmentPath)
        Await MyClient.GetGuild(ServerID).GetTextChannel(PostChannelID).SendFileAsync(AttachmentStream, IO.Path.GetFileName(AttachmentPath), "THIS IS A TEST", False, Builder.Build)
    End Using
End Sub

现在,一切都完全按预期工作,我不必求助于将图像上传到托管站点并使用新的 URL(我实际上在我之前就已经开始工作了在 GitHub 上收到了回复。我确信 代码不会浪费 )。


编辑

好的,所以我 仍然 出于一个原因最终回到我单独托管的图像选项:我有一个单独的事件方法来修改原始 Embed 对象在此期间我想删除图像并替换文本。然而,当该事件触发时,文本被替换,图像被“移动”到 Discord 消息的正文中。虽然我 可能 已经能够弄清楚如何完全摆脱图像,但我决定“退后一步”,因为我已经制定了托管图像解决方案。