Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/HuaJiBot.NET.Adapter.OneBot/ForwardWebSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ internal class ForwardWebSocketClient
private readonly WebsocketClient _client;
internal readonly OneBotApi Api;

public bool IsConnected => _client.IsConnected;

public Task StartAsync() => _client.StartAsync();

public ValueTask ConnectAsync() => _client.ConnectAsync();
Expand Down
2 changes: 2 additions & 0 deletions src/HuaJiBot.NET.Adapter.OneBot/OneBotAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ protected override void ReconnectCore()

protected override Task SetupServiceAsyncCore() => Client.StartAsync();

public override bool IsConnected => Client.IsConnected;

public override string[] AllRobots => Client.QQ is not null ? [Client.QQ] : [];

public override async Task<string[]> SendGroupMessageAsync(
Expand Down
23 changes: 22 additions & 1 deletion src/HuaJiBot.NET.Plugin.PushPanel/PanelServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ internal sealed class PanelServer : IDisposable
private readonly string _token;
private readonly ConfigWrapper _config;
private readonly string _page;
private readonly Func<bool> _isConnected;
private readonly CancellationTokenSource _cts = new();

public PanelServer(string host, int port, string token, ConfigWrapper config)
public PanelServer(
string host,
int port,
string token,
ConfigWrapper config,
Func<bool>? isConnected = null
)
{
_token = token;
_config = config;
_isConnected = isConnected ?? (() => true);
_page = ReadPage();
_listener.Prefixes.Add($"http://{host}:{port}/");
}
Expand Down Expand Up @@ -81,6 +89,8 @@ private async Task HandleAsync(HttpListenerContext context)
{
if (method == "GET" && path is "/" or "/index.html")
return ServePage;
if (method == "GET" && path == "/healthz")
return ServeHealth;
if (path != "/api/rules")
return null;
return method switch
Expand All @@ -91,6 +101,17 @@ private async Task HandleAsync(HttpListenerContext context)
};
}

// Unauthenticated and public: reports connection state only, nothing from the config.
private Task ServeHealth(HttpListenerContext context)
{
var connected = _isConnected();
return WriteJson(
context.Response,
connected ? 200 : 503,
new { status = connected ? "ok" : "disconnected" }
);
}

private Task ServePage(HttpListenerContext context) =>
WriteText(context.Response, 200, "text/html; charset=utf-8", _page);

Expand Down
8 changes: 7 additions & 1 deletion src/HuaJiBot.NET.Plugin.PushPanel/PluginMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ protected override void Initialize()
}
try
{
_server = new PanelServer(Config.Host, Config.Port, Config.Token, Service.Config);
_server = new PanelServer(
Config.Host,
Config.Port,
Config.Token,
Service.Config,
() => Service.IsConnected
);
_server.Start();
Service.Log($"[PushPanel] http://{Config.Host}:{Config.Port}/");
}
Expand Down
24 changes: 24 additions & 0 deletions src/HuaJiBot.NET.UnitTest/PushPanelTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,30 @@ public async Task Panel_RequiresTokenAndSaves()
});
}

[Test]
public async Task Panel_HealthzReportsConnectionWithoutToken()
{
using var scope = new ConfigScope();
var connected = true;
var port = FreePort();
using var server = new PanelServer("127.0.0.1", port, "panel-secret", scope.Wrapper, () => connected);
server.Start();
using var http = new HttpClient();

var up = await http.GetAsync($"http://127.0.0.1:{port}/healthz");
connected = false;
var down = await http.GetAsync($"http://127.0.0.1:{port}/healthz");
var body = await down.Content.ReadAsStringAsync();

Assert.Multiple(() =>
{
Assert.That(up.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(down.StatusCode, Is.EqualTo(HttpStatusCode.ServiceUnavailable));
Assert.That(body, Does.Contain("disconnected"));
Assert.That(body, Does.Not.Contain("panel-secret"));
});
}

private static int FreePort()
{
var listener = new System.Net.Sockets.TcpListener(IPAddress.Loopback, 0);
Expand Down
1 change: 1 addition & 0 deletions src/HuaJiBot.NET/Bot/BotService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ private string AppendSourceInfo(object msg, string? file = null, int? line = nul
public Config.ConfigWrapper Config { get; internal set; } = null!;
public abstract IEvents Events { get; }
public abstract string[] AllRobots { get; }
public virtual bool IsConnected => AllRobots.Length > 0;

protected abstract void ReconnectCore();
protected abstract Task SetupServiceAsyncCore();
Expand Down
3 changes: 3 additions & 0 deletions src/HuaJiBot.NET/Interfaces/IPluginService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ Func<Task<SendingMessageBase[]>> fallback
void RecallMessage(string? robotId, string targetGroup, string msgId);
Task<string[]> FeedbackAt(string? robotId, string targetGroup, string msgId, string text);
string[] AllRobots { get; }

/// <summary>Whether the adapter is currently connected to its platform.</summary>
bool IsConnected { get; }
ConfigWrapper Config { get; }
ILogger Logger { get; }
IReadOnlyDictionary<string, IEnumerable<AgentFunctionInfo>> ExportFunctions { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/HuaJiBot.NET/Websocket/WebsocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public WebsocketClient(
_client.MessageReceived += OnMessageReceived;
}

public bool IsConnected => _client.Connected;

public async Task StartAsync()
{
await ConnectAsync();
Expand Down
Loading