Skip to content

fix: restore local bot api startup#274

Open
ModerRAS wants to merge 1 commit intomasterfrom
fix/local-bot-api-bootstrap
Open

fix: restore local bot api startup#274
ModerRAS wants to merge 1 commit intomasterfrom
fix/local-bot-api-bootstrap

Conversation

@ModerRAS
Copy link
Copy Markdown
Owner

@ModerRAS ModerRAS commented Apr 21, 2026

Summary

  • route embedded elegram-bot-api.exe startup through the shared managed child-process launcher instead of ad-hoc Process.Start`n- use ProcessStartInfo.ArgumentList and consume child stdout/stderr so managed child processes do not break on spaced paths or stall on redirected output
  • add ExternalLocalBotApiBaseUrl to Config/Env so the bot can run against another local Bot API instance without enabling the embedded one
  • wait for loopback local API endpoints during startup and update docs/tests accordingly

Why

The embedded Local Bot API path was not using the same managed startup flow as other child processes, which made startup fragile and prevented consistent Job Object-based lifetime handling. On top of that, configuration did not expose a clean way to use an already-running external Local Bot API instance.

Validation

  • dotnet build TelegramSearchBot.sln -c Release
  • dotnet test TelegramSearchBot.sln -c Release --no-build

Summary by CodeRabbit

  • New Features

    • Added support for external Local Bot API: configure custom local API endpoints via new ExternalLocalBotApiBaseUrl setting
    • 2GB file upload capability now available for both built-in and external local Bot API modes
  • Documentation

    • Updated README with configuration examples for built-in and external local Bot API services
    • Enhanced architecture documentation reflecting both local API deployment options
  • Tests

    • Added endpoint resolution test coverage

Route embedded telegram-bot-api startup through the managed child-process launcher, add explicit ExternalLocalBotApiBaseUrl support for external local API usage, and consume child process output so startup no longer breaks on spaced paths or silent process failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

This PR adds support for external Local Bot API endpoints by introducing a new ExternalLocalBotApiBaseUrl configuration option. Changes include updated documentation, centralized endpoint resolution logic in Env.cs, new unit tests, and refactored bootstrap/startup procedures to handle both embedded and external local API modes.

Changes

Cohort / File(s) Summary
Documentation Updates
Docs/Architecture_Overview.md, Docs/README_MCP.md, README.md
Updated documentation to describe the new ExternalLocalBotApiBaseUrl configuration option and clarified distinction between embedded (requires TelegramBotApiId/TelegramBotApiHash) and external local Bot API modes (uses ExternalLocalBotApiBaseUrl). File size limits expanded from "本地 Bot API" to "本地 Bot API(内置或外部)".
Endpoint Resolution & Configuration
TelegramSearchBot.Common/Env.cs
Added ExternalLocalBotApiBaseUrl setting and BotApiEndpointSettings record. Introduced ResolveBotApiEndpoint() and NormalizeBaseUrl() methods to centralize URL normalization and selection logic between local-embedded, external-local, and default fallback modes.
Endpoint Resolution Tests
TelegramSearchBot.Test/Common/EnvBotApiEndpointTests.cs
Added three xUnit [Fact] tests validating ResolveBotApiEndpoint() behavior for embedded local mode, external local mode, and default fallback mode.
Process Management & Bootstrap
TelegramSearchBot/AppBootstrap/AppBootstrap.cs
Enhanced ChildProcessManager with process tracking and disposal. Refactored Fork() implementations to use managed process creation flow (CreateManagedStartInfo(), StartManagedProcess()) with standardized encoding, logging, and lifecycle handling. Updated rate-limiting to key by executable path instead of first argument.
Startup Logic & Service Waiter
TelegramSearchBot/AppBootstrap/GeneralBootstrap.cs
Replaced fixed service-specific waiters with generalized WaitForTcpServiceReady(). Implemented conditional startup logic: StartEmbeddedLocalBotApiAsync() for embedded mode with validation, and WaitForExternalLocalBotApiIfNeededAsync() for external mode detection. Unified startup flow in Startup() method.

Sequence Diagram

sequenceDiagram
    participant Config as Configuration
    participant Env as Env.ResolveBotApiEndpoint
    participant Bootstrap as Bootstrap.Startup
    participant EmbeddedMode as Embedded Mode
    participant ExternalMode as External Mode
    participant TCPWaiter as WaitForTcpServiceReady
    participant Service as Bot API Service

    Config->>Env: Load settings (EnableLocalBotAPI, ExternalLocalBotApiBaseUrl)
    Env->>Env: Determine endpoint mode
    
    alt EnableLocalBotAPI == true
        Env->>EmbeddedMode: Resolve to embedded local mode
        EmbeddedMode->>Bootstrap: Return http://127.0.0.1:{port}
        Bootstrap->>EmbeddedMode: StartEmbeddedLocalBotApiAsync()
        EmbeddedMode->>Service: Fork telegram-bot-api.exe process
        Service->>TCPWaiter: Process started
        TCPWaiter->>Service: Poll TCP port
        Service->>TCPWaiter: Port becomes available
    else ExternalLocalBotApiBaseUrl provided
        Env->>ExternalMode: Resolve to external local mode
        ExternalMode->>Bootstrap: Return ExternalLocalBotApiBaseUrl
        Bootstrap->>ExternalMode: WaitForExternalLocalBotApiIfNeededAsync()
        ExternalMode->>TCPWaiter: Wait for external service port
        Service->>TCPWaiter: External service ready on port
    else Default
        Env->>Bootstrap: Resolve to cloud API (api.telegram.org)
        Bootstrap->>Bootstrap: Skip local API startup
    end
    
    Bootstrap->>Bootstrap: Startup complete
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • PR #271: Directly modifies AppBootstrap.Fork() and process-forking/startup logic, making it related to the Bootstrap refactoring in this PR.

Poem

🐰 A bunny hops through config trees,
Finding paths both far and near—
Embedded bots or extern flees,
The endpoint waits, both bright and clear.
With tests and care, the startup's sound,
Two modes dance where APIs found! 🤖✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: restore local bot api startup' directly relates to the core change of rerouting embedded telegram-bot-api.exe startup through the managed child-process launcher, which is the primary technical objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/local-bot-api-bootstrap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 PR检查报告

📋 检查概览

🧪 测试结果

平台 状态 详情
Ubuntu 🔴 失败 测试结果不可用
Windows 🔴 失败 测试结果不可用

📊 代码质量

  • ✅ 代码格式化检查
  • ✅ 安全漏洞扫描
  • ✅ 依赖包分析
  • ✅ 代码覆盖率收集

📁 测试产物

  • 测试结果文件已上传为artifacts
  • 代码覆盖率已上传到Codecov

🔗 相关链接


此报告由GitHub Actions自动生成

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Docs/Architecture_Overview.md (1)

131-136: ⚠️ Potential issue | 🟡 Minor

Align the config-management section with Config.json.

This PR adds a persistent Config.json option, but this section still points users at appsettings.json/environment/database config. Please update it to mention %LOCALAPPDATA%/TelegramSearchBot/Config.json as the source of these runtime settings. As per coding guidelines, **/Env.cs: Load configuration from %LOCALAPPDATA%/TelegramSearchBot/Config.json via Env.cs, not from appsettings.json.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Docs/Architecture_Overview.md` around lines 131 - 136, Update the "配置管理"
section to document the new persistent runtime config file: mention that runtime
settings are loaded from %LOCALAPPDATA%/TelegramSearchBot/Config.json
(Config.json) rather than appsettings.json or env/database sources, and state
that Env.cs implements this load path (i.e., Env.cs reads
%LOCALAPPDATA%/TelegramSearchBot/Config.json to populate runtime configuration);
remove or de-emphasize appsettings.json as the primary runtime source and note
appsettings.json/environment/database remain secondary/legacy where applicable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Docs/Architecture_Overview.md`:
- Around line 272-273: Update the document footer to reflect the new change by
replacing the existing footer fields "*最后更新: 2026-03-26*" and "*文档版本: 1.1*" with
the current last-updated date and new document version (e.g., update the date
and bump the version string accordingly) so the footer matches the new external
Local Bot API mode changes.

In `@TelegramSearchBot.Common/Env.cs`:
- Around line 72-76: The current branch that returns a new
BotApiEndpointSettings when normalizedExternalLocalBotApiBaseUrl is non-empty
incorrectly forces IsLocalApi=true for any string; update the guard in the code
that checks normalizedExternalLocalBotApiBaseUrl so it first parses and
validates it as an absolute URI with scheme http or https and, if this option is
intended to be local-only, also require the host is loopback/localhost (or an IP
in 127.0.0.0/8). Only if the URI validation passes should you construct and
return new BotApiEndpointSettings(normalizedExternalLocalBotApiBaseUrl, true,
normalizedExternalLocalBotApiBaseUrl); otherwise treat it as invalid (skip local
mode and/or throw/log an error).

In `@TelegramSearchBot/AppBootstrap/AppBootstrap.cs`:
- Around line 201-204: The process display name currently includes raw
command-line arguments (see Fork and the StartManagedProcess call), which leaks
secrets like --api-hash into logs; change the display name construction so it
does not include full args: either omit args entirely (use only exePath) or
build a redactedArgs string that replaces sensitive values for known flags (e.g.
--api-hash=, --token=, --password=) with a placeholder like "<REDACTED>" or show
only arg names without values, then pass that safe display string into
StartManagedProcess/StartManagedProcess logging; update the Fork method (and the
similar code between lines 246–278) to use this redaction logic and ensure
CreateManagedStartInfo remains unchanged.

In `@TelegramSearchBot/AppBootstrap/GeneralBootstrap.cs`:
- Around line 75-85: The StartEmbeddedLocalBotApiAsync method currently returns
silently when required pieces are missing; instead fail fast so configuration
errors are visible: when Env.EnableLocalBotAPI is true and the bot API
executable is missing (botApiExePath) or TelegramBotApiId/TelegramBotApiHash are
not configured, throw a clear exception (e.g., InvalidOperationException) with a
descriptive message rather than returning; update StartEmbeddedLocalBotApiAsync
to validate Env.EnableLocalBotAPI/Env.BaseUrl and then raise on these missing
prerequisites so callers (and startup) fail immediately and do not continue with
an unstarted embedded API.
- Around line 47-64: WaitForTcpServiceReady currently logs and returns on
dependency process exit or timeout which lets startup continue with missing
services; change it to fail-fast by throwing exceptions instead of returning:
when the optional Process has exited, log and then throw an
InvalidOperationException including serviceName and process.ExitCode; when
maxRetries elapse, log and then throw a TimeoutException including serviceName,
host and port. Update the method (WaitForTcpServiceReady) to preserve the
existing log messages but throw the appropriate exceptions so callers cannot
continue with unavailable TCP dependencies.

---

Outside diff comments:
In `@Docs/Architecture_Overview.md`:
- Around line 131-136: Update the "配置管理" section to document the new persistent
runtime config file: mention that runtime settings are loaded from
%LOCALAPPDATA%/TelegramSearchBot/Config.json (Config.json) rather than
appsettings.json or env/database sources, and state that Env.cs implements this
load path (i.e., Env.cs reads %LOCALAPPDATA%/TelegramSearchBot/Config.json to
populate runtime configuration); remove or de-emphasize appsettings.json as the
primary runtime source and note appsettings.json/environment/database remain
secondary/legacy where applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e81169c2-1e6b-4ffa-8bf3-acae2ab50d73

📥 Commits

Reviewing files that changed from the base of the PR and between 4659075 and 070422a.

📒 Files selected for processing (7)
  • Docs/Architecture_Overview.md
  • Docs/README_MCP.md
  • README.md
  • TelegramSearchBot.Common/Env.cs
  • TelegramSearchBot.Test/Common/EnvBotApiEndpointTests.cs
  • TelegramSearchBot/AppBootstrap/AppBootstrap.cs
  • TelegramSearchBot/AppBootstrap/GeneralBootstrap.cs

Comment on lines 272 to +273
*最后更新: 2026-03-26*
*文档版本: 1.1* No newline at end of file
*文档版本: 1.1*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Refresh the document footer.

The architecture doc content changed for the new external Local Bot API mode, but the footer still shows the prior update date/version.

📝 Proposed footer update
-*最后更新: 2026-03-26*
-*文档版本: 1.1*
+*最后更新: 2026-04-21*
+*文档版本: 1.2*
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*最后更新: 2026-03-26*
*文档版本: 1.1*
\ No newline at end of file
*文档版本: 1.1*
*最后更新: 2026-04-21*
*文档版本: 1.2*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Docs/Architecture_Overview.md` around lines 272 - 273, Update the document
footer to reflect the new change by replacing the existing footer fields "*最后更新:
2026-03-26*" and "*文档版本: 1.1*" with the current last-updated date and new
document version (e.g., update the date and bump the version string accordingly)
so the footer matches the new external Local Bot API mode changes.

Comment on lines +72 to +76
if (!string.IsNullOrWhiteSpace(normalizedExternalLocalBotApiBaseUrl)) {
return new BotApiEndpointSettings(
normalizedExternalLocalBotApiBaseUrl,
true,
normalizedExternalLocalBotApiBaseUrl);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate ExternalLocalBotApiBaseUrl before forcing local API mode.

Any non-empty value currently sets IsLocalApi=true, including malformed URLs or non-local hosts. That can leave startup checks skipped later and may make the Telegram client treat a remote endpoint as a trusted local Bot API. Please require an absolute http/https URI and, if this option is intended to be local-only, enforce loopback/localhost here.

🛡️ Proposed validation guard
-            if (!string.IsNullOrWhiteSpace(normalizedExternalLocalBotApiBaseUrl)) {
+            if (!string.IsNullOrWhiteSpace(normalizedExternalLocalBotApiBaseUrl)) {
+                if (!Uri.TryCreate(normalizedExternalLocalBotApiBaseUrl, UriKind.Absolute, out var externalUri) ||
+                    (externalUri.Scheme != Uri.UriSchemeHttp && externalUri.Scheme != Uri.UriSchemeHttps) ||
+                    !(externalUri.IsLoopback || string.Equals(externalUri.Host, "localhost", StringComparison.OrdinalIgnoreCase))) {
+                    throw new InvalidOperationException("ExternalLocalBotApiBaseUrl must be an absolute loopback http/https URL.");
+                }
+
                 return new BotApiEndpointSettings(
                     normalizedExternalLocalBotApiBaseUrl,
                     true,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!string.IsNullOrWhiteSpace(normalizedExternalLocalBotApiBaseUrl)) {
return new BotApiEndpointSettings(
normalizedExternalLocalBotApiBaseUrl,
true,
normalizedExternalLocalBotApiBaseUrl);
if (!string.IsNullOrWhiteSpace(normalizedExternalLocalBotApiBaseUrl)) {
if (!Uri.TryCreate(normalizedExternalLocalBotApiBaseUrl, UriKind.Absolute, out var externalUri) ||
(externalUri.Scheme != Uri.UriSchemeHttp && externalUri.Scheme != Uri.UriSchemeHttps) ||
!(externalUri.IsLoopback || string.Equals(externalUri.Host, "localhost", StringComparison.OrdinalIgnoreCase))) {
throw new InvalidOperationException("ExternalLocalBotApiBaseUrl must be an absolute loopback http/https URL.");
}
return new BotApiEndpointSettings(
normalizedExternalLocalBotApiBaseUrl,
true,
normalizedExternalLocalBotApiBaseUrl);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TelegramSearchBot.Common/Env.cs` around lines 72 - 76, The current branch
that returns a new BotApiEndpointSettings when
normalizedExternalLocalBotApiBaseUrl is non-empty incorrectly forces
IsLocalApi=true for any string; update the guard in the code that checks
normalizedExternalLocalBotApiBaseUrl so it first parses and validates it as an
absolute URI with scheme http or https and, if this option is intended to be
local-only, also require the host is loopback/localhost (or an IP in
127.0.0.0/8). Only if the URI validation passes should you construct and return
new BotApiEndpointSettings(normalizedExternalLocalBotApiBaseUrl, true,
normalizedExternalLocalBotApiBaseUrl); otherwise treat it as invalid (skip local
mode and/or throw/log an error).

Comment on lines 201 to +204
public static Process Fork(string exePath, string[] args, long? processMemoryLimitBytes = null) {
// 将参数数组转换为空格分隔的字符串,并正确处理包含空格的参数
string arguments = string.Join(" ", args.Select(arg => $"{arg}"));

// 启动新的进程(自己)
ProcessStartInfo startInfo = new ProcessStartInfo {
FileName = exePath,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};

var newProcess = Process.Start(startInfo);
if (newProcess == null) {
throw new Exception("启动新进程失败");
}
childProcessManager.AddProcess(newProcess, processMemoryLimitBytes);
Log.Logger.Information($"进程:{exePath} {string.Join(" ", args)}已启动");
return newProcess;
var workingDirectory = Path.GetDirectoryName(exePath);
var startInfo = CreateManagedStartInfo(exePath, args, string.IsNullOrWhiteSpace(workingDirectory) ? Environment.CurrentDirectory : workingDirectory);
return StartManagedProcess(startInfo, $"进程:{exePath} {string.Join(" ", args)}", processMemoryLimitBytes);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid logging raw child-process arguments.

processDisplayName includes every argument and is logged on startup plus every stdout/stderr line. GeneralBootstrap passes --api-hash={Env.TelegramBotApiHash} into this overload, so the Telegram API hash will be written to logs.

🔒 Proposed safer display name
 public static Process Fork(string exePath, string[] args, long? processMemoryLimitBytes = null) {
     var workingDirectory = Path.GetDirectoryName(exePath);
     var startInfo = CreateManagedStartInfo(exePath, args, string.IsNullOrWhiteSpace(workingDirectory) ? Environment.CurrentDirectory : workingDirectory);
-    return StartManagedProcess(startInfo, $"进程:{exePath} {string.Join(" ", args)}", processMemoryLimitBytes);
+    return StartManagedProcess(startInfo, $"进程:{exePath}", processMemoryLimitBytes);
 }

If argument visibility is needed, log only a redacted list.

Also applies to: 246-278

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TelegramSearchBot/AppBootstrap/AppBootstrap.cs` around lines 201 - 204, The
process display name currently includes raw command-line arguments (see Fork and
the StartManagedProcess call), which leaks secrets like --api-hash into logs;
change the display name construction so it does not include full args: either
omit args entirely (use only exePath) or build a redactedArgs string that
replaces sensitive values for known flags (e.g. --api-hash=, --token=,
--password=) with a placeholder like "<REDACTED>" or show only arg names without
values, then pass that safe display string into
StartManagedProcess/StartManagedProcess logging; update the Fork method (and the
similar code between lines 246–278) to use this redaction logic and ensure
CreateManagedStartInfo remains unchanged.

Comment on lines +47 to +64
private static async Task WaitForTcpServiceReady(string host, int port, string serviceName, Process? process = null, int maxRetries = 20, int delayMs = 500) {
for (int i = 0; i < maxRetries; i++) {
try {
using var tcp = new System.Net.Sockets.TcpClient();
await tcp.ConnectAsync("127.0.0.1", port);
Log.Information("Garnet 服务已就绪 (端口 {Port}),耗时约 {ElapsedMs}ms", port, i * delayMs);
if (process is { HasExited: true }) {
Log.Warning("{ServiceName} 进程在端口就绪前退出,退出码 {ExitCode}", serviceName, process.ExitCode);
return;
} catch {
await Task.Delay(delayMs);
}
}
Log.Warning("等待 Garnet 服务就绪超时 (端口 {Port}),将继续启动(Redis 连接会自动重试)", port);
}

/// <summary>
/// 等待本地 telegram-bot-api 服务端口就绪,最多等待 20 秒
/// </summary>
private static async Task WaitForLocalBotApiReady(int port, int maxRetries = 40, int delayMs = 500) {
for (int i = 0; i < maxRetries; i++) {
try {
using var tcp = new System.Net.Sockets.TcpClient();
await tcp.ConnectAsync("127.0.0.1", port);
Log.Information("telegram-bot-api 服务已就绪 (端口 {Port}),耗时约 {ElapsedMs}ms", port, i * delayMs);
await tcp.ConnectAsync(host, port);
Log.Information("{ServiceName} 服务已就绪 ({Host}:{Port}),耗时约 {ElapsedMs}ms", serviceName, host, port, i * delayMs);
return;
} catch {
await Task.Delay(delayMs);
}
}
Log.Warning("等待 telegram-bot-api 服务就绪超时 (端口 {Port}),将继续启动", port);
Log.Warning("等待 {ServiceName} 服务就绪超时 ({Host}:{Port}),将继续启动", serviceName, host, port);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail startup when a required TCP dependency exits or never becomes ready.

This helper logs and returns on process exit/timeout, so callers continue with Garnet or Local Bot API unavailable. For the scheduler path, that turns a deterministic startup failure into later Redis errors; for local Bot API mode, Telegram calls will hit a dead endpoint.

🛠️ Proposed fail-fast behavior
                 if (process is { HasExited: true }) {
                     Log.Warning("{ServiceName} 进程在端口就绪前退出,退出码 {ExitCode}", serviceName, process.ExitCode);
-                    return;
+                    throw new InvalidOperationException($"{serviceName} exited before {host}:{port} became ready.");
                 }
@@
-            Log.Warning("等待 {ServiceName} 服务就绪超时 ({Host}:{Port}),将继续启动", serviceName, host, port);
+            throw new TimeoutException($"Timed out waiting for {serviceName} at {host}:{port}.");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static async Task WaitForTcpServiceReady(string host, int port, string serviceName, Process? process = null, int maxRetries = 20, int delayMs = 500) {
for (int i = 0; i < maxRetries; i++) {
try {
using var tcp = new System.Net.Sockets.TcpClient();
await tcp.ConnectAsync("127.0.0.1", port);
Log.Information("Garnet 服务已就绪 (端口 {Port}),耗时约 {ElapsedMs}ms", port, i * delayMs);
if (process is { HasExited: true }) {
Log.Warning("{ServiceName} 进程在端口就绪前退出,退出码 {ExitCode}", serviceName, process.ExitCode);
return;
} catch {
await Task.Delay(delayMs);
}
}
Log.Warning("等待 Garnet 服务就绪超时 (端口 {Port}),将继续启动(Redis 连接会自动重试)", port);
}
/// <summary>
/// 等待本地 telegram-bot-api 服务端口就绪,最多等待 20 秒
/// </summary>
private static async Task WaitForLocalBotApiReady(int port, int maxRetries = 40, int delayMs = 500) {
for (int i = 0; i < maxRetries; i++) {
try {
using var tcp = new System.Net.Sockets.TcpClient();
await tcp.ConnectAsync("127.0.0.1", port);
Log.Information("telegram-bot-api 服务已就绪 (端口 {Port}),耗时约 {ElapsedMs}ms", port, i * delayMs);
await tcp.ConnectAsync(host, port);
Log.Information("{ServiceName} 服务已就绪 ({Host}:{Port}),耗时约 {ElapsedMs}ms", serviceName, host, port, i * delayMs);
return;
} catch {
await Task.Delay(delayMs);
}
}
Log.Warning("等待 telegram-bot-api 服务就绪超时 (端口 {Port}),将继续启动", port);
Log.Warning("等待 {ServiceName} 服务就绪超时 ({Host}:{Port}),将继续启动", serviceName, host, port);
}
private static async Task WaitForTcpServiceReady(string host, int port, string serviceName, Process? process = null, int maxRetries = 20, int delayMs = 500) {
for (int i = 0; i < maxRetries; i++) {
if (process is { HasExited: true }) {
Log.Warning("{ServiceName} 进程在端口就绪前退出,退出码 {ExitCode}", serviceName, process.ExitCode);
throw new InvalidOperationException($"{serviceName} exited before {host}:{port} became ready.");
}
try {
using var tcp = new System.Net.Sockets.TcpClient();
await tcp.ConnectAsync(host, port);
Log.Information("{ServiceName} 服务已就绪 ({Host}:{Port}),耗时约 {ElapsedMs}ms", serviceName, host, port, i * delayMs);
return;
} catch {
await Task.Delay(delayMs);
}
}
throw new TimeoutException($"Timed out waiting for {serviceName} at {host}:{port}.");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TelegramSearchBot/AppBootstrap/GeneralBootstrap.cs` around lines 47 - 64,
WaitForTcpServiceReady currently logs and returns on dependency process exit or
timeout which lets startup continue with missing services; change it to
fail-fast by throwing exceptions instead of returning: when the optional Process
has exited, log and then throw an InvalidOperationException including
serviceName and process.ExitCode; when maxRetries elapse, log and then throw a
TimeoutException including serviceName, host and port. Update the method
(WaitForTcpServiceReady) to preserve the existing log messages but throw the
appropriate exceptions so callers cannot continue with unavailable TCP
dependencies.

Comment on lines +75 to +85
private static async Task StartEmbeddedLocalBotApiAsync() {
string botApiExePath = Path.Combine(AppContext.BaseDirectory, "telegram-bot-api.exe");
if (!File.Exists(botApiExePath)) {
Log.Warning("未找到 telegram-bot-api 可执行文件 {Path},跳过内置本地 Bot API 启动", botApiExePath);
return;
}

if (string.IsNullOrWhiteSpace(Env.TelegramBotApiId) || string.IsNullOrWhiteSpace(Env.TelegramBotApiHash)) {
Log.Warning("EnableLocalBotAPI 为 true,但 TelegramBotApiId 或 TelegramBotApiHash 未配置,跳过内置本地 Bot API 启动");
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t silently skip embedded Local Bot API when it is enabled.

With Env.EnableLocalBotAPI=true, Env.BaseUrl already points to the embedded local endpoint. Returning here leaves the bot configured for an API server that was never started; fail fast instead so the misconfiguration is visible.

🛠️ Proposed fail-fast checks
             if (!File.Exists(botApiExePath)) {
-                Log.Warning("未找到 telegram-bot-api 可执行文件 {Path},跳过内置本地 Bot API 启动", botApiExePath);
-                return;
+                throw new FileNotFoundException("EnableLocalBotAPI is true but telegram-bot-api executable was not found.", botApiExePath);
             }
 
             if (string.IsNullOrWhiteSpace(Env.TelegramBotApiId) || string.IsNullOrWhiteSpace(Env.TelegramBotApiHash)) {
-                Log.Warning("EnableLocalBotAPI 为 true,但 TelegramBotApiId 或 TelegramBotApiHash 未配置,跳过内置本地 Bot API 启动");
-                return;
+                throw new InvalidOperationException("EnableLocalBotAPI is true but TelegramBotApiId or TelegramBotApiHash is not configured.");
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static async Task StartEmbeddedLocalBotApiAsync() {
string botApiExePath = Path.Combine(AppContext.BaseDirectory, "telegram-bot-api.exe");
if (!File.Exists(botApiExePath)) {
Log.Warning("未找到 telegram-bot-api 可执行文件 {Path},跳过内置本地 Bot API 启动", botApiExePath);
return;
}
if (string.IsNullOrWhiteSpace(Env.TelegramBotApiId) || string.IsNullOrWhiteSpace(Env.TelegramBotApiHash)) {
Log.Warning("EnableLocalBotAPI 为 true,但 TelegramBotApiId 或 TelegramBotApiHash 未配置,跳过内置本地 Bot API 启动");
return;
}
private static async Task StartEmbeddedLocalBotApiAsync() {
string botApiExePath = Path.Combine(AppContext.BaseDirectory, "telegram-bot-api.exe");
if (!File.Exists(botApiExePath)) {
throw new FileNotFoundException("EnableLocalBotAPI is true but telegram-bot-api executable was not found.", botApiExePath);
}
if (string.IsNullOrWhiteSpace(Env.TelegramBotApiId) || string.IsNullOrWhiteSpace(Env.TelegramBotApiHash)) {
throw new InvalidOperationException("EnableLocalBotAPI is true but TelegramBotApiId or TelegramBotApiHash is not configured.");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TelegramSearchBot/AppBootstrap/GeneralBootstrap.cs` around lines 75 - 85, The
StartEmbeddedLocalBotApiAsync method currently returns silently when required
pieces are missing; instead fail fast so configuration errors are visible: when
Env.EnableLocalBotAPI is true and the bot API executable is missing
(botApiExePath) or TelegramBotApiId/TelegramBotApiHash are not configured, throw
a clear exception (e.g., InvalidOperationException) with a descriptive message
rather than returning; update StartEmbeddedLocalBotApiAsync to validate
Env.EnableLocalBotAPI/Env.BaseUrl and then raise on these missing prerequisites
so callers (and startup) fail immediately and do not continue with an unstarted
embedded API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant