feat(controller): add init message for bot pipes
chore: move softwareInfo from main package to a individual global package
This commit is contained in:
@@ -88,6 +88,9 @@ func LoadConfig(configPath string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply default message templates if not specified.
|
// Apply default message templates if not specified.
|
||||||
|
if cfg.ControllerMessage.BotStarted == "" {
|
||||||
|
cfg.ControllerMessage.BotStarted = "Nukumizu Alert Bot Started\nVersion: {{ softwareVersion }}.{{ softwareBuildVer }}.{{ softwareCommitHash }}.{{ softwareBuildType }}\nDeveloper: {{ softwareDeveloper }}\nTime: {{ time }}"
|
||||||
|
}
|
||||||
if cfg.ControllerMessage.ServerStatusChanged == "" {
|
if cfg.ControllerMessage.ServerStatusChanged == "" {
|
||||||
cfg.ControllerMessage.ServerStatusChanged = "Server Status Changed Alert\n{{ serverName }} - {{ upStatus }}\nEvent: {{ event }}\nServer Name: {{ serverName }}\nMessage: {{ message }}\nTime: {{ time }}"
|
cfg.ControllerMessage.ServerStatusChanged = "Server Status Changed Alert\n{{ serverName }} - {{ upStatus }}\nEvent: {{ event }}\nServer Name: {{ serverName }}\nMessage: {{ message }}\nTime: {{ time }}"
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -9,10 +9,10 @@ type SystemConfig struct {
|
|||||||
|
|
||||||
// DebugConfig holds debug-level configuration.
|
// DebugConfig holds debug-level configuration.
|
||||||
type DebugConfig struct {
|
type DebugConfig struct {
|
||||||
ShowNapcatMsg bool `json:"showNapcatMsg"`
|
ShowNapcatMsg bool `json:"showNapcatMsg"`
|
||||||
ShowNapcatAction bool `json:"showNapcatAction"`
|
ShowNapcatAction bool `json:"showNapcatAction"`
|
||||||
ShowTelegramMsg bool `json:"showTelegramMsg"`
|
ShowTelegramMsg bool `json:"showTelegramMsg"`
|
||||||
ShowTriggerCmdEcho bool `json:"showTriggerCmdEcho"`
|
ShowTriggerCmdEcho bool `json:"showTriggerCmdEcho"`
|
||||||
NapcatIgnoreSelfMsg bool `json:"napcatIgnoreSelfMsg"`
|
NapcatIgnoreSelfMsg bool `json:"napcatIgnoreSelfMsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +90,7 @@ type ControllerMethodConfig struct {
|
|||||||
|
|
||||||
// ControllerMessageConfig holds message templates for controller responses.
|
// ControllerMessageConfig holds message templates for controller responses.
|
||||||
type ControllerMessageConfig struct {
|
type ControllerMessageConfig struct {
|
||||||
|
BotStarted string `json:"BOT_STARTED"`
|
||||||
ServerStatusChanged string `json:"SERVER_STATUS_CHANGED"`
|
ServerStatusChanged string `json:"SERVER_STATUS_CHANGED"`
|
||||||
ServerList string `json:"SERVER_LIST"`
|
ServerList string `json:"SERVER_LIST"`
|
||||||
ServerExecuteResult string `json:"SERVER_EXECUTE_RESULT"`
|
ServerExecuteResult string `json:"SERVER_EXECUTE_RESULT"`
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
// SoftwareInfo holds build metadata.
|
||||||
|
type SoftwareInfoStr struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
Developer string
|
||||||
|
BuildVer int16
|
||||||
|
CommitHash string
|
||||||
|
Description string
|
||||||
|
BuildType string
|
||||||
|
BuildTime string
|
||||||
|
}
|
||||||
|
|
||||||
|
var SoftwareInfo = SoftwareInfoStr{
|
||||||
|
Name: "Nukumizu",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Developer: "Madobi Nanami",
|
||||||
|
BuildVer: 1,
|
||||||
|
CommitHash: "unknown",
|
||||||
|
Description: "Remote server monitoring and command execution subsystem for Komari",
|
||||||
|
BuildType: "Debug",
|
||||||
|
BuildTime: "unknown",
|
||||||
|
}
|
||||||
@@ -31,6 +31,15 @@ type Controller interface {
|
|||||||
SendExecuteResult(serverName, serverUUID, command, result string) error
|
SendExecuteResult(serverName, serverUUID, command, result string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BotController is implemented by controllers that act as chat bots and can
|
||||||
|
// deliver arbitrary messages, such as the bot initialization message on startup.
|
||||||
|
// Only bot-type pipes (QQ/NapCat, Telegram) implement it; notification-only
|
||||||
|
// pipes (email, ntfy, webhook) do not.
|
||||||
|
type BotController interface {
|
||||||
|
Controller
|
||||||
|
SendMessage(message string) error
|
||||||
|
}
|
||||||
|
|
||||||
// Manager manages all controller instances and routes events.
|
// Manager manages all controller instances and routes events.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
@@ -60,6 +69,56 @@ func (m *Manager) Register(c Controller) {
|
|||||||
postLog.Info("Controller registered: " + c.Name())
|
postLog.Info("Controller registered: " + c.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowBotInitMessage sends the bot initialization message to all enabled
|
||||||
|
// bot controllers (QQ/NapCat and Telegram). Notification-only pipes that do
|
||||||
|
// not implement BotController are skipped.
|
||||||
|
func (m *Manager) ShowBotInitMessage() {
|
||||||
|
m.mu.RLock()
|
||||||
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
params := template.BuildBotInitializationMsgParams()
|
||||||
|
message := template.Render(cfg.ControllerMessage.BotStarted, params)
|
||||||
|
|
||||||
|
for _, ctrl := range m.controllers {
|
||||||
|
if !ctrl.IsEnabled() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bot, ok := ctrl.(BotController)
|
||||||
|
if !ok {
|
||||||
|
continue // Notification-only pipe (email/ntfy/webhook), not a bot.
|
||||||
|
}
|
||||||
|
if err := bot.SendMessage(message); err != nil {
|
||||||
|
postLog.Warning(fmt.Sprintf("Controller %s failed to send init message: %v", bot.Name(), err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowBotServerList sends the server list to all enabled bot controllers. The
|
||||||
|
// message content is identical to the /list command (same template and
|
||||||
|
// parameters).
|
||||||
|
func (m *Manager) ShowBotServerList() {
|
||||||
|
m.mu.RLock()
|
||||||
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
params := template.BuildParamsFromServerList()
|
||||||
|
message := template.Render(cfg.ControllerMessage.ServerList, params)
|
||||||
|
|
||||||
|
for _, ctrl := range m.controllers {
|
||||||
|
if !ctrl.IsEnabled() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bot, ok := ctrl.(BotController)
|
||||||
|
if !ok {
|
||||||
|
continue // Notification-only pipe (email/ntfy/webhook), not a bot.
|
||||||
|
}
|
||||||
|
if err := bot.SendMessage(message); err != nil {
|
||||||
|
postLog.Warning(fmt.Sprintf("Controller %s failed to send server list: %v", bot.Name(), err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NotifyStatusChange sends a status change notification to all enabled controllers.
|
// NotifyStatusChange sends a status change notification to all enabled controllers.
|
||||||
func (m *Manager) NotifyStatusChange(change node.StatusChange) {
|
func (m *Manager) NotifyStatusChange(change node.StatusChange) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
|
|||||||
@@ -201,6 +201,22 @@ func (q *QQController) isSelfMessage(ev oneBotEvent) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessage sends an arbitrary message (e.g. the bot initialization message)
|
||||||
|
// to all QQ trusted groups and admins.
|
||||||
|
func (q *QQController) SendMessage(message string) error {
|
||||||
|
if !q.cfg.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, groupID := range q.cfg.TrustedGroups {
|
||||||
|
q.sendGroupMessage(groupID, message)
|
||||||
|
}
|
||||||
|
for _, adminID := range q.cfg.Admins {
|
||||||
|
q.sendPrivateMessage(adminID, message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SendStatusChange sends a status change notification via QQ.
|
// SendStatusChange sends a status change notification via QQ.
|
||||||
func (q *QQController) SendStatusChange(change node.StatusChange) error {
|
func (q *QQController) SendStatusChange(change node.StatusChange) error {
|
||||||
if !q.cfg.Enabled {
|
if !q.cfg.Enabled {
|
||||||
|
|||||||
@@ -92,6 +92,17 @@ func (t *TelegramController) processCommand(cmd controller.Command) string {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessage sends an arbitrary message (e.g. the bot initialization message)
|
||||||
|
// via Telegram. The Telegram Bot API integration is currently a stub, so this
|
||||||
|
// only logs the message for now.
|
||||||
|
func (t *TelegramController) SendMessage(message string) error {
|
||||||
|
if !t.cfg.Enabled || t.cfg.BotToken == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
postLog.Debug("Telegram init message: " + message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SendStatusChange sends a status change notification via Telegram.
|
// SendStatusChange sends a status change notification via Telegram.
|
||||||
func (t *TelegramController) SendStatusChange(change node.StatusChange) error {
|
func (t *TelegramController) SendStatusChange(change node.StatusChange) error {
|
||||||
if !t.cfg.Enabled || t.cfg.BotToken == "" {
|
if !t.cfg.Enabled || t.cfg.BotToken == "" {
|
||||||
|
|||||||
@@ -71,11 +71,12 @@ type StatusChangeCallback func(change StatusChange)
|
|||||||
// Tracker maintains the in-memory state of all monitored nodes.
|
// Tracker maintains the in-memory state of all monitored nodes.
|
||||||
// All methods are thread-safe.
|
// All methods are thread-safe.
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
nodes map[string]*Node // uuid → Node
|
nodes map[string]*Node // uuid → Node
|
||||||
uuidToName map[string]string // uuid → name (from node list)
|
uuidToName map[string]string // uuid → name (from node list)
|
||||||
onlineSet map[string]bool // which uuids are currently online
|
onlineSet map[string]bool // which uuids are currently online
|
||||||
callbacks []StatusChangeCallback
|
callbacks []StatusChangeCallback
|
||||||
|
onBootstrapDone []func()
|
||||||
|
|
||||||
// Initial-refresh gating: status-change notifications are suppressed until
|
// Initial-refresh gating: status-change notifications are suppressed until
|
||||||
// the initial status snapshot has been received, so the startup state is
|
// the initial status snapshot has been received, so the startup state is
|
||||||
@@ -250,13 +251,21 @@ func (t *Tracker) UpdateStatus(onlineUUIDs []string, reports map[string]Report)
|
|||||||
}
|
}
|
||||||
|
|
||||||
suppressChanges := !t.bootstrapDone || firstSnapshot
|
suppressChanges := !t.bootstrapDone || firstSnapshot
|
||||||
|
bootstrapDoneNow := false
|
||||||
if !t.bootstrapDone && t.refreshComplete(reports) {
|
if !t.bootstrapDone && t.refreshComplete(reports) {
|
||||||
t.bootstrapDone = true
|
t.bootstrapDone = true
|
||||||
|
bootstrapDoneNow = true
|
||||||
postLog.Info(fmt.Sprintf("Initial node status refresh complete (%d/%d nodes reported); status notifications enabled",
|
postLog.Info(fmt.Sprintf("Initial node status refresh complete (%d/%d nodes reported); status notifications enabled",
|
||||||
len(t.reportedUUIDs), len(t.knownUUIDs)))
|
len(t.reportedUUIDs), len(t.knownUUIDs)))
|
||||||
}
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
|
||||||
|
// Notify startup listeners (e.g. server list sender) once the initial
|
||||||
|
// refresh has finished.
|
||||||
|
if bootstrapDoneNow {
|
||||||
|
t.fireBootstrapDone()
|
||||||
|
}
|
||||||
|
|
||||||
// Fire callbacks for each change (outside the lock).
|
// Fire callbacks for each change (outside the lock).
|
||||||
for _, change := range changes {
|
for _, change := range changes {
|
||||||
if suppressChanges {
|
if suppressChanges {
|
||||||
@@ -301,6 +310,40 @@ func (t *Tracker) refreshComplete(currentReports map[string]Report) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnBootstrapDone registers a callback invoked once the initial status refresh
|
||||||
|
// completes (e.g. to send the startup server list). If the refresh has already
|
||||||
|
// completed, the callback is invoked immediately. Each callback fires exactly
|
||||||
|
// once.
|
||||||
|
func (t *Tracker) OnBootstrapDone(cb func()) {
|
||||||
|
t.mu.Lock()
|
||||||
|
t.onBootstrapDone = append(t.onBootstrapDone, cb)
|
||||||
|
if !t.bootstrapDone {
|
||||||
|
t.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Already complete — collect everything pending and fire outside the lock.
|
||||||
|
cbs := t.onBootstrapDone
|
||||||
|
t.onBootstrapDone = nil
|
||||||
|
t.mu.Unlock()
|
||||||
|
|
||||||
|
for _, c := range cbs {
|
||||||
|
c()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fireBootstrapDone notifies all registered callbacks that the initial status
|
||||||
|
// refresh is complete. Must be called with the lock NOT held.
|
||||||
|
func (t *Tracker) fireBootstrapDone() {
|
||||||
|
t.mu.Lock()
|
||||||
|
cbs := t.onBootstrapDone
|
||||||
|
t.onBootstrapDone = nil
|
||||||
|
t.mu.Unlock()
|
||||||
|
|
||||||
|
for _, cb := range cbs {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CompleteBootstrap force-completes the initial status refresh gate. It is a
|
// CompleteBootstrap force-completes the initial status refresh gate. It is a
|
||||||
// safety net so that notifications cannot be blocked forever when no status
|
// safety net so that notifications cannot be blocked forever when no status
|
||||||
// snapshot is ever received. It does not mark a snapshot as received, so the
|
// snapshot is ever received. It does not mark a snapshot as received, so the
|
||||||
@@ -308,11 +351,14 @@ func (t *Tracker) refreshComplete(currentReports map[string]Report) bool {
|
|||||||
// again is a no-op.
|
// again is a no-op.
|
||||||
func (t *Tracker) CompleteBootstrap() {
|
func (t *Tracker) CompleteBootstrap() {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
if t.bootstrapDone {
|
||||||
if !t.bootstrapDone {
|
t.mu.Unlock()
|
||||||
t.bootstrapDone = true
|
return
|
||||||
postLog.Info("Initial node status refresh timeout reached; status notifications enabled")
|
|
||||||
}
|
}
|
||||||
|
t.bootstrapDone = true
|
||||||
|
postLog.Info("Initial node status refresh timeout reached; status notifications enabled")
|
||||||
|
t.mu.Unlock()
|
||||||
|
t.fireBootstrapDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNode returns a copy of the node data for the given UUID.
|
// GetNode returns a copy of the node data for the given UUID.
|
||||||
|
|||||||
@@ -5,21 +5,43 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"nukumizu-backend/global"
|
||||||
"nukumizu-backend/internal/node"
|
"nukumizu-backend/internal/node"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Params holds all possible template parameters.
|
// Params holds all possible template parameters.
|
||||||
type Params struct {
|
type Params struct {
|
||||||
Time string
|
Time string
|
||||||
ServerName string
|
ServerName string
|
||||||
ServerUUID string
|
ServerUUID string
|
||||||
UpStatus string
|
UpStatus string
|
||||||
Event string
|
Event string
|
||||||
Message string
|
Message string
|
||||||
Command string
|
Command string
|
||||||
Result string
|
Result string
|
||||||
OnlineServers string // Pre-formatted multi-line list
|
OnlineServers string // Pre-formatted multi-line list
|
||||||
OfflineServers string // Pre-formatted multi-line list
|
OfflineServers string // Pre-formatted multi-line list
|
||||||
|
SoftwareVersion string
|
||||||
|
SoftwareBuildVer int16
|
||||||
|
SoftwareCommitHash string
|
||||||
|
SoftwareBuildType string
|
||||||
|
SoftwareBuildTime string
|
||||||
|
SoftwareDeveloper string
|
||||||
|
SoftwareDescription string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBotInitializationMsgParams creates template parameters for the bot initialization message.
|
||||||
|
func BuildBotInitializationMsgParams() Params {
|
||||||
|
return Params{
|
||||||
|
Time: time.Now().Format("2006-01-02T15:04:05.000000000-07:00"),
|
||||||
|
SoftwareVersion: global.SoftwareInfo.Version,
|
||||||
|
SoftwareBuildVer: global.SoftwareInfo.BuildVer,
|
||||||
|
SoftwareCommitHash: global.SoftwareInfo.CommitHash,
|
||||||
|
SoftwareBuildType: global.SoftwareInfo.BuildType,
|
||||||
|
SoftwareBuildTime: global.SoftwareInfo.BuildTime,
|
||||||
|
SoftwareDeveloper: global.SoftwareInfo.Developer,
|
||||||
|
SoftwareDescription: global.SoftwareInfo.Description,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildParamsFromStatusChange creates template parameters from a status change event.
|
// BuildParamsFromStatusChange creates template parameters from a status change event.
|
||||||
@@ -70,6 +92,13 @@ func BuildParamsFromExecResult(serverName, serverUUID, command, result string) P
|
|||||||
// - {{ result }} — command execution result
|
// - {{ result }} — command execution result
|
||||||
// - {{ list.onlineServers }} — multi-line online server list
|
// - {{ list.onlineServers }} — multi-line online server list
|
||||||
// - {{ list.offlineServers }} — multi-line offline server list
|
// - {{ list.offlineServers }} — multi-line offline server list
|
||||||
|
// - {{ softwareVersion }} — software version
|
||||||
|
// - {{ softwareBuildVer }} — software build version
|
||||||
|
// - {{ softwareCommitHash }} — software commit hash
|
||||||
|
// - {{ softwareBuildType }} — software build type (Debug/Release)
|
||||||
|
// - {{ softwareBuildTime }} — software build time
|
||||||
|
// - {{ softwareDeveloper }} — software developer
|
||||||
|
// - {{ softwareDescription }} — software description
|
||||||
func Render(tmpl string, params Params) string {
|
func Render(tmpl string, params Params) string {
|
||||||
result := tmpl
|
result := tmpl
|
||||||
|
|
||||||
@@ -83,6 +112,13 @@ func Render(tmpl string, params Params) string {
|
|||||||
result = strings.ReplaceAll(result, "{{ result }}", params.Result)
|
result = strings.ReplaceAll(result, "{{ result }}", params.Result)
|
||||||
result = strings.ReplaceAll(result, "{{ list.onlineServers }}", params.OnlineServers)
|
result = strings.ReplaceAll(result, "{{ list.onlineServers }}", params.OnlineServers)
|
||||||
result = strings.ReplaceAll(result, "{{ list.offlineServers }}", params.OfflineServers)
|
result = strings.ReplaceAll(result, "{{ list.offlineServers }}", params.OfflineServers)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareVersion }}", params.SoftwareVersion)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareBuildVer }}", fmt.Sprintf("%d", params.SoftwareBuildVer))
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareCommitHash }}", params.SoftwareCommitHash)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareBuildType }}", params.SoftwareBuildType)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareBuildTime }}", params.SoftwareBuildTime)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareDeveloper }}", params.SoftwareDeveloper)
|
||||||
|
result = strings.ReplaceAll(result, "{{ softwareDescription }}", params.SoftwareDescription)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"nukumizu-backend/config"
|
"nukumizu-backend/config"
|
||||||
"nukumizu-backend/database"
|
"nukumizu-backend/database"
|
||||||
|
"nukumizu-backend/global"
|
||||||
"nukumizu-backend/internal/controller"
|
"nukumizu-backend/internal/controller"
|
||||||
"nukumizu-backend/internal/controller/pipes"
|
"nukumizu-backend/internal/controller/pipes"
|
||||||
"nukumizu-backend/internal/controller/pipes/qq_napcat"
|
"nukumizu-backend/internal/controller/pipes/qq_napcat"
|
||||||
@@ -27,36 +28,17 @@ var (
|
|||||||
BuildTime string
|
BuildTime string
|
||||||
)
|
)
|
||||||
|
|
||||||
// SoftwareInfo holds build metadata.
|
|
||||||
type SoftwareInfo struct {
|
|
||||||
Name string
|
|
||||||
Version string
|
|
||||||
Developer string
|
|
||||||
BuildVer int16
|
|
||||||
CommitHash string
|
|
||||||
Description string
|
|
||||||
BuildType string
|
|
||||||
BuildTime string
|
|
||||||
}
|
|
||||||
|
|
||||||
var softwareInfo = SoftwareInfo{
|
|
||||||
Name: "Nukumizu",
|
|
||||||
Version: "0.1.0",
|
|
||||||
Developer: "Madobi Nanami",
|
|
||||||
BuildVer: 1,
|
|
||||||
CommitHash: CommitHash,
|
|
||||||
Description: "Remote server monitoring and command execution subsystem for Komari",
|
|
||||||
BuildType: "Debug",
|
|
||||||
BuildTime: BuildTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Set global software info.
|
||||||
|
global.SoftwareInfo.CommitHash = CommitHash
|
||||||
|
global.SoftwareInfo.BuildTime = BuildTime
|
||||||
|
|
||||||
// Parse CLI flags.
|
// Parse CLI flags.
|
||||||
configPath := flag.String("config", "config.json", "Path to configuration file")
|
configPath := flag.String("config", "config.json", "Path to configuration file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Log startup banner.
|
// Log startup banner.
|
||||||
postLog.Info(fmt.Sprintf("%s Ver.%s.%d.%s.%s Developed by %s at %s", softwareInfo.Name, softwareInfo.Version, softwareInfo.BuildVer, softwareInfo.BuildType, softwareInfo.CommitHash, softwareInfo.Developer, softwareInfo.BuildTime))
|
postLog.Info(fmt.Sprintf("%s Ver.%s.%d.%s.%s Developed by %s at %s", global.SoftwareInfo.Name, global.SoftwareInfo.Version, global.SoftwareInfo.BuildVer, global.SoftwareInfo.BuildType, global.SoftwareInfo.CommitHash, global.SoftwareInfo.Developer, global.SoftwareInfo.BuildTime))
|
||||||
|
|
||||||
// Load configuration.
|
// Load configuration.
|
||||||
cfg, err := config.LoadConfig(*configPath)
|
cfg, err := config.LoadConfig(*configPath)
|
||||||
@@ -119,6 +101,22 @@ func main() {
|
|||||||
// --- Initialize controllers ---
|
// --- Initialize controllers ---
|
||||||
initControllers()
|
initControllers()
|
||||||
|
|
||||||
|
// Send the bot initialization message to all enabled bot controllers
|
||||||
|
// (QQ_NapCat, Telegram), then the server list right after it once the
|
||||||
|
// initial status refresh has completed so the list reflects the real
|
||||||
|
// online/offline state.
|
||||||
|
if mgr := controller.GetManager(); mgr != nil {
|
||||||
|
mgr.ShowBotInitMessage()
|
||||||
|
|
||||||
|
if tracker := node.GetTracker(); tracker != nil {
|
||||||
|
tracker.OnBootstrapDone(func() {
|
||||||
|
if m := controller.GetManager(); m != nil {
|
||||||
|
m.ShowBotServerList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Start background tasks ---
|
// --- Start background tasks ---
|
||||||
startBackgroundTasks(cfg)
|
startBackgroundTasks(cfg)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user