feat: wire NOFXi Agent into Telegram bot

- Add NOFXiAgent interface and SetNOFXiAgent() in telegram/bot.go
- Telegram messages now route through the unified agent.Agent
  instead of the legacy telegram/agent package
- Language hint injected from Telegram config for proper i18n
- Legacy telegram/agent path preserved as fallback
- Agent created before Telegram bot starts in main.go
- Build verified, API tested
This commit is contained in:
shinchan-zhai
2026-03-23 02:39:02 +08:00
parent 0fa8c48b84
commit 4cba7edd19
2 changed files with 72 additions and 22 deletions

12
main.go
View File

@@ -142,10 +142,7 @@ func main() {
}
}()
// Start Telegram bot (if TELEGRAM_BOT_TOKEN is configured)
go telegram.Start(cfg, st, telegramReloadCh)
// Start NOFXi Agent (proactive intelligence layer)
// Start NOFXi Agent (proactive intelligence layer) — must be created BEFORE Telegram bot
nofxiAgent := nofxiagent.New(traderManager, st, nil, slog.Default())
// Try to get an AI client from existing trader configs
@@ -164,6 +161,13 @@ func main() {
agentWeb := nofxiagent.NewWebHandler(nofxiAgent, slog.Default())
server.RegisterAgentHandler(agentWeb)
// Wire NOFXi Agent into Telegram bot — messages route through the unified agent
telegram.SetNOFXiAgent(nofxiAgent)
logger.Info("🔗 NOFXi Agent connected to Telegram bot")
// Start Telegram bot (if token is configured)
go telegram.Start(cfg, st, telegramReloadCh)
logger.Info("🧠 NOFXi Agent started (sentinel + brain + scheduler + web:8900)")
// Wait for interrupt signal

View File

@@ -1,6 +1,7 @@
package telegram
import (
"context"
"nofx/api"
"nofx/config"
"nofx/logger"
@@ -17,6 +18,30 @@ import (
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
// NOFXiAgent is the interface that the new agent.Agent satisfies.
// When set, Telegram messages are routed through it instead of the legacy telegram/agent package.
type NOFXiAgent interface {
HandleMessage(ctx context.Context, userID int64, text string) (string, error)
}
var (
nofxiAgentMu sync.RWMutex
nofxiAgent NOFXiAgent
)
// SetNOFXiAgent registers the new NOFXi agent for Telegram message routing.
func SetNOFXiAgent(a NOFXiAgent) {
nofxiAgentMu.Lock()
defer nofxiAgentMu.Unlock()
nofxiAgent = a
}
func getNOFXiAgent() NOFXiAgent {
nofxiAgentMu.RLock()
defer nofxiAgentMu.RUnlock()
return nofxiAgent
}
// Start initializes and runs the Telegram bot in a blocking supervisor loop.
// Supports hot-reload: when a signal is sent on reloadCh, the bot restarts
// with the latest token (re-read from DB or env). Must be called as a goroutine from main.go.
@@ -208,25 +233,46 @@ func runBot(token string, cfg *config.Config, st *store.Store) bool {
placeholderID = sent.MessageID
}
var (
mu sync.Mutex
lastEdit time.Time
)
onChunk := func(accumulated string) {
if placeholderID == 0 {
return
}
mu.Lock()
defer mu.Unlock()
if accumulated != "⏳" && time.Since(lastEdit) < time.Second {
return
}
lastEdit = time.Now()
edit := tgbotapi.NewEditMessageText(chatID, placeholderID, accumulated)
bot.Send(edit) //nolint:errcheck
}
var reply string
reply := agents.Run(chatID, text, onChunk)
// Route through NOFXi Agent if available (new unified architecture),
// otherwise fall back to legacy telegram/agent manager.
if na := getNOFXiAgent(); na != nil {
lang := st.TelegramConfig().GetLanguage()
msg := text
if lang != "" {
msg = "[lang:" + lang + "] " + text
}
ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
resp, err := na.HandleMessage(ctx, chatID, msg)
cancel()
if err != nil {
logger.Errorf("NOFXi agent error: %v", err)
reply = "AI assistant temporarily unavailable. Please try again."
} else {
reply = resp
}
} else {
// Legacy path: stream via telegram/agent
var (
mu sync.Mutex
lastEdit time.Time
)
onChunk := func(accumulated string) {
if placeholderID == 0 {
return
}
mu.Lock()
defer mu.Unlock()
if accumulated != "⏳" && time.Since(lastEdit) < time.Second {
return
}
lastEdit = time.Now()
edit := tgbotapi.NewEditMessageText(chatID, placeholderID, accumulated)
bot.Send(edit) //nolint:errcheck
}
reply = agents.Run(chatID, text, onChunk)
}
if placeholderID != 0 {
edit := tgbotapi.NewEditMessageText(chatID, placeholderID, reply)