【问题标题】:Unable to update previous messages after reconnection重新连接后无法更新以前的消息
【发布时间】:2019-07-10 17:45:00
【问题描述】:

我创建了一个带有命令的机器人,允许用户为其频道配置某种“供稿”。

这个提要应该发送一条消息,保存公会、频道和消息 ID。并在独立更新周期中,尝试使用新信息更新消息。

只要在同一个会话中,这一切都很好。

假设机器人由于不和谐中断而失去连接,并在 x 时间后重新连接,机器人似乎不再能够找到,因此不再更新消息。

尤其是id好像无法检索到消息
var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;

值得注意的是,我使用了_settings,它以 json 格式保存,并在机器人重新启动时再次加载。

我还确认该消息仍然存在于该频道的服务器中,具有相同的消息 ID。并且该机器人有权查看频道的消息历史记录。

所以我的问题是,为什么GetMessageAsync 在重新连接后无法检索以前发布的消息?

最初调用的命令

public async Task BindPlaytimeFeedAsync(ICommandContext context)
{
    var builder = await _scumService.GetTop25PlaytimeByDate(new DateTime(), DateTime.Now);

    var message = await context.Channel.SendMessageAsync(null, false, builder.Build());
    _settings.PlaytimeFeed = new MessageInfo()
    {
        GuildId = context.Guild.Id,
        ChannelId = context.Channel.Id,
        MessageId = message.Id,
    };
    var ptFeedMessage = await context.Channel.SendMessageAsync("Playtime feed is now bound to this channel (this message self-destructs in 5 seconds)");
    await Task.Delay(5000);
    await ptFeedMessage.DeleteAsync();
}

Feed 的刷新间隔是在机器人本身旁边使用计时器定义的,如下所示。

...
_client = new DiscordSocketClient(
    new DiscordSocketConfig
    {
        LogLevel = LogSeverity.Verbose,
        AlwaysDownloadUsers = true, // Start the cache off with updated information.
        MessageCacheSize = 1000
    }
);
_service = ConfigureServices();

_feedInterval = new Timer(async (e) =>
{
    Console.WriteLine("doing feed stuff");
    await HandleFeedsAsync();
}, null, 15000, 300000);
CmdHandler = new CommandHandler(_service, state);

...

private async Task HandleFeedsAsync()
{
    var botSettings = _service.GetService<ISettings>() as BotSettings;

    await HandleKdFeedAsync(botSettings.KdFeed);
    await HandlePlaytimeFeedAsync(botSettings.PlaytimeFeed);
    await HandleWeeklyPlaytimeFeed(botSettings.WeeklyPlaytimeFeed);
    await HandleAdminFeed(botSettings);
}

最终使用下面的 sn-p 覆盖消息。

private async Task HandlePlaytimeFeedAsync(MessageInfo playtimeFeed)
{
    if (playtimeFeed == null)
        return;

    var scumService = _service.GetService<ScumService>();
    var guild = _client.GetGuild(playtimeFeed.GuildId);
    var channel = guild.GetTextChannel(playtimeFeed.ChannelId);
    var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;
    if (message == null)
        return;
    var builder = await scumService.GetTop25PlaytimeByDate(new DateTime(), DateTime.Now);

    await message.ModifyAsync(prop =>
    {
        prop.Embed = builder.Build();
    });
}

【问题讨论】:

  • 最可能的原因是,该消息不再在缓存中,这意味着它无法转换为 SocketUserMessage。因此,虽然它可能会被检索到,但您的演员表会返回 null。当您正在处理的消息可能是 Socket 实体或 Rest 实体时,只需使用接口与之交互 - IUserMessage
  • @Anu6is 会有点令人困惑,重新连接后获得的对象类型会切换。尽管如此,总体而言,使用界面会是一个更好的主意。感谢您的建议。
  • 理论上即使没有断开连接,对象类型也可以切换。如果缓存已满,您的消息可以从缓存中弹出,下次检索时将通过休息请求检索。我相信如果您的会话在断开连接后无法恢复,缓存会在重新连接时被清除。
  • @Anu6is 有道理,试了一下,确实解决了这个问题。介意把它作为答案,以便我可以接受它^^?

标签: c# discord.net


【解决方案1】:

var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;

GetMessageAsync 方法尝试从缓存中检索作为SocketUserMessage 的消息,但是如果在缓存中未找到该消息,则会执行一个休息请求,该请求将返回一个RestUserMessge。通过对GetMessageAsync 的结果执行软转换,如果/当返回RestUserMessage 时,您可以获得空值。

当您正在处理的消息可能是 Socket 实体或 Rest 实体时,只需使用接口与之交互 - IUserMessage

【讨论】:

    猜你喜欢
    • 2017-07-21
    • 2016-01-30
    • 2014-03-31
    • 1970-01-01
    • 2021-03-14
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多