From 7aebebf1938198eb9befd856373ac677fca880df Mon Sep 17 00:00:00 2001 From: sol Date: Sat, 7 Mar 2026 22:25:59 +0000 Subject: [PATCH] fix: plugin bot user + await plugin detection before session scan - Add EnsureBotUser on plugin activate (fixes 'Unable to find user' error) - Accept bot_user_id in create session request - Await plugin health check before starting session monitor (prevents race where sessions detect before plugin flag is set) - Plugin now creates custom_livestatus posts with proper bot user --- plugin/server/api.go | 15 ++++++++++++++- plugin/server/plugin.go | 22 ++++++++++++++++++++++ src/plugin-client.js | 15 +++++++++++++-- src/watcher-manager.js | 10 +++++++--- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/plugin/server/api.go b/plugin/server/api.go index 09e78d7..3b3e4e6 100644 --- a/plugin/server/api.go +++ b/plugin/server/api.go @@ -75,6 +75,7 @@ type CreateSessionRequest struct { ChannelID string `json:"channel_id"` RootID string `json:"root_id,omitempty"` AgentID string `json:"agent_id"` + BotUserID string `json:"bot_user_id,omitempty"` } // handleCreateSession creates a new custom_livestatus post and starts tracking. @@ -98,8 +99,20 @@ func (p *Plugin) handleCreateSession(w http.ResponseWriter, r *http.Request) { return } - // Create the custom post + // Create the custom post — UserId is required + // Use the bot user ID passed in the request, or fall back to plugin bot + userID := req.BotUserID + if userID == "" { + // Try to get plugin's own bot + userID = p.getBotUserID() + } + if userID == "" { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bot_user_id required (no plugin bot available)"}) + return + } + post := &model.Post{ + UserId: userID, ChannelId: req.ChannelID, RootId: req.RootID, Type: "custom_livestatus", diff --git a/plugin/server/plugin.go b/plugin/server/plugin.go index a27bd9d..8b3d35f 100644 --- a/plugin/server/plugin.go +++ b/plugin/server/plugin.go @@ -3,6 +3,7 @@ package main import ( "sync" + "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/plugin" ) @@ -18,16 +19,37 @@ type Plugin struct { // store wraps KV store operations for session persistence. store *Store + + // botUserID is the plugin's bot user ID (created on activation). + botUserID string } // OnActivate is called when the plugin is activated. func (p *Plugin) OnActivate() error { p.store = NewStore(p.API) + // Ensure plugin bot user exists + botID, appErr := p.API.EnsureBotUser(&model.Bot{ + Username: "livestatus", + DisplayName: "Live Status", + Description: "OpenClaw Live Status plugin bot", + }) + if appErr != nil { + p.API.LogWarn("Failed to ensure bot user", "error", appErr.Error()) + } else { + p.botUserID = botID + p.API.LogInfo("Plugin bot user ensured", "botUserID", botID) + } + p.API.LogInfo("OpenClaw Live Status plugin activated") return nil } +// getBotUserID returns the plugin's bot user ID. +func (p *Plugin) getBotUserID() string { + return p.botUserID +} + // OnDeactivate is called when the plugin is deactivated. func (p *Plugin) OnDeactivate() error { // Mark all active sessions as interrupted diff --git a/src/plugin-client.js b/src/plugin-client.js index 78178ed..3c32064 100644 --- a/src/plugin-client.js +++ b/src/plugin-client.js @@ -60,13 +60,24 @@ PluginClient.prototype.isHealthy = function () { * @param {string} agentId * @returns {Promise} post_id */ +/** + * @param {string} botUserId - Mattermost bot user ID to author the post + */ +PluginClient.prototype.setBotUserId = function (botUserId) { + this.botUserId = botUserId; +}; + PluginClient.prototype.createSession = function (sessionKey, channelId, rootId, agentId) { - return this._request('POST', '/api/v1/sessions', { + var body = { session_key: sessionKey, channel_id: channelId, root_id: rootId || '', agent_id: agentId, - }).then(function (data) { + }; + if (this.botUserId) { + body.bot_user_id = this.botUserId; + } + return this._request('POST', '/api/v1/sessions', body).then(function (data) { return data.post_id; }); }; diff --git a/src/watcher-manager.js b/src/watcher-manager.js index 28c9e66..2ce2210 100644 --- a/src/watcher-manager.js +++ b/src/watcher-manager.js @@ -171,13 +171,17 @@ async function startDaemon() { logger: logger.child({ module: 'plugin-client' }), }); - // Initial plugin detection - pluginClient.isHealthy().then(function (healthy) { + // Initial plugin detection (awaited before monitor starts — see below) + try { + var healthy = await pluginClient.isHealthy(); usePlugin = healthy; logger.info({ usePlugin, url: config.plugin.url }, healthy ? 'Plugin detected — using WebSocket rendering mode' : 'Plugin not available — using REST API fallback'); - }); + } catch (_detectErr) { + usePlugin = false; + logger.warn('Plugin detection failed — using REST API fallback'); + } // Periodic re-detection setInterval(function () {