fix(bot): 修复手动同步无法发现新频道的问题
- BotUpdateHub: 添加 QuickCollectMyChatMemberAsync 方法 - 在 BotPoller 恢复时也收集 my_chat_member updates - 解决 BotAutoSyncEnabled=false 时手动同步失效的问题 同时优化 .dockerignore: - 排除 artifacts 目录 (7.2GB) - 排除模块源码和文档目录 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
f5ba6be472
commit
30ce5f12cd
2 files changed
+97
-1
No files matched your search
+26
-1
@@ -1,15 +1,40 @@
|
||||
# Git & IDE
|
||||
.git
|
||||
.serena
|
||||
.tmp
|
||||
.vs
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Build outputs
|
||||
**/bin
|
||||
**/obj
|
||||
artifacts
|
||||
|
||||
# Docker runtime data
|
||||
docker-data
|
||||
|
||||
# Temporary & logs
|
||||
.tmp
|
||||
logs
|
||||
sessions
|
||||
todolist
|
||||
|
||||
# Database & cache
|
||||
*.db
|
||||
*.log
|
||||
*.user
|
||||
*.suo
|
||||
*.cache
|
||||
|
||||
# Documentation & screenshots (not needed for build)
|
||||
docs
|
||||
screenshot
|
||||
README.md
|
||||
|
||||
# Module source (development only)
|
||||
模块源码
|
||||
|
||||
# Misc
|
||||
*.md
|
||||
!Directory.Build.props
|
||||
|
||||
@@ -189,6 +189,11 @@ public sealed class BotUpdateHub : IAsyncDisposable
|
||||
if (lastUpdateId.HasValue)
|
||||
{
|
||||
nextOffset = lastUpdateId.Value + 1;
|
||||
|
||||
// 恢复时:从上次的 offset 开始收集 my_chat_member(用于发现新加入的频道)。
|
||||
// 这些 updates 稍后会被主循环重新拉取(幂等),但手动同步时可以立即使用缓存。
|
||||
pendingMyChatMember = await QuickCollectMyChatMemberAsync(
|
||||
token, nextOffset, botApi, logger, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -201,6 +206,72 @@ public sealed class BotUpdateHub : IAsyncDisposable
|
||||
return new BotPoller(botId, token, nextOffset, pendingMyChatMember, scopeFactory, botApi, logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速收集从指定 offset 开始的 my_chat_member updates(不推进主循环 offset)。
|
||||
/// 用于 Panel 恢复/手动同步时发现新加入的频道。
|
||||
/// </summary>
|
||||
private static async Task<List<JsonElement>?> QuickCollectMyChatMemberAsync(
|
||||
string token,
|
||||
long startOffset,
|
||||
TelegramBotApiClient botApi,
|
||||
ILogger logger,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var pending = new List<JsonElement>();
|
||||
long offset = startOffset;
|
||||
|
||||
// 最多尝试 20 次(2000 条),避免无限循环
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var updates = await botApi.CallAsync(token, "getUpdates", new Dictionary<string, string?>
|
||||
{
|
||||
["offset"] = offset.ToString(),
|
||||
["timeout"] = "0", // 快速返回,不长轮询
|
||||
["limit"] = "100",
|
||||
["allowed_updates"] = AllowedMyChatMemberOnlyJson
|
||||
}, cancellationToken);
|
||||
|
||||
if (updates.ValueKind != JsonValueKind.Array)
|
||||
break;
|
||||
|
||||
long? maxUpdateId = null;
|
||||
foreach (var u in updates.EnumerateArray())
|
||||
{
|
||||
if (!TryGetUpdateId(u, out var id))
|
||||
continue;
|
||||
|
||||
maxUpdateId = maxUpdateId.HasValue ? Math.Max(maxUpdateId.Value, id) : id;
|
||||
|
||||
if (u.ValueKind == JsonValueKind.Object && u.TryGetProperty("my_chat_member", out _))
|
||||
{
|
||||
pending.Add(u.Clone());
|
||||
if (pending.Count > PendingMyChatMemberMax)
|
||||
pending.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!maxUpdateId.HasValue)
|
||||
break;
|
||||
|
||||
offset = maxUpdateId.Value + 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "QuickCollectMyChatMember failed at offset {Offset}", offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.Count > 0)
|
||||
logger.LogInformation("QuickCollectMyChatMember collected {Count} my_chat_member updates", pending.Count);
|
||||
|
||||
return pending.Count > 0 ? pending : null;
|
||||
}
|
||||
|
||||
public BotUpdateSubscription Subscribe()
|
||||
{
|
||||
var id = Guid.NewGuid();
|
||||
|
||||
Reference in new issue
Block a user