Documentation
¶
Overview ¶
Package bot 实现 fairpeer 多渠道 IM bot 消息网关,支持 QQ、飞书、微信、Telegram。 架构参考 Hermes Agent 的 gateway/adapter/session 模式。
Index ¶
- func BuildSessionKey(src SessionSource) string
- func IsSlashBypass(text string) bool
- type Adapter
- type AllowlistConfig
- type BotGateway
- type ChannelConfig
- type ChatType
- type DesktopBridge
- type DesktopPendingInfo
- type DesktopSessionInfo
- type DesktopWatchRoute
- type GatewayConfig
- type InboundMessage
- type InlineKeyboard
- type InlineKeyboardButton
- type InlineKeyboardRow
- type InteractiveCard
- type InteractiveCardElement
- type MessageHandler
- type NetdevActionResult
- type NetdevBridge
- type NetdevEvidenceView
- type NetdevFindingDetail
- type NetdevFindingSummary
- type NetdevProposalSummary
- type OutboundMessage
- type Platform
- type RecentChat
- type SendResult
- type SessionManager
- func (sm *SessionManager) ActiveCount() int
- func (sm *SessionManager) ForceRelease(key string)
- func (sm *SessionManager) IsActive(key string) bool
- func (sm *SessionManager) Release(key string) *InboundMessage
- func (sm *SessionManager) TryAcquire(key string, msg InboundMessage) (acquired bool, merged bool)
- type SessionSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildSessionKey ¶
func BuildSessionKey(src SessionSource) string
BuildSessionKey 根据 Hermes 模式生成稳定的 session key:
- DM:按 chat 隔离(同一 DM 会话共享历史)
- 群聊:按 user 隔离(每人独立会话)
- thread:共享(thread 内所有人共享上下文)
Types ¶
type Adapter ¶
type Adapter interface {
// Platform 返回平台标识。
Platform() Platform
// Start 启动适配器,连接平台 gateway。
Start(ctx context.Context) error
// Stop 优雅关闭适配器。
Stop() error
// Send 发送一条出站消息。
Send(ctx context.Context, msg OutboundMessage) (SendResult, error)
// SendTyping 发送"正在输入"状态。
SendTyping(ctx context.Context, chatID string) error
// Messages 返回入站消息通道。
Messages() <-chan InboundMessage
// Name 返回适配器实例名(用于日志)。
Name() string
}
Adapter 是平台适配器接口,每个平台实现一个。
type AllowlistConfig ¶
type AllowlistConfig struct {
Enabled bool
AllowAll bool
Mode string // "open"(自动加入)| "review"(需审批)
Users map[Platform][]string
Groups map[Platform][]string
}
AllowlistConfig 控制哪些用户/群可以使用 bot。
type BotGateway ¶
type BotGateway struct {
// contains filtered or unexported fields
}
BotGateway 是 fairpeer bot 消息网关,管理 Controller 生命周期、session 并发、 事件渲染和平台适配器。
func NewGateway ¶
func NewGateway(cfg GatewayConfig, adapters map[Platform]Adapter, logger *slog.Logger) *BotGateway
NewGateway 创建一个新的 BotGateway。
func (*BotGateway) Push ¶
func (gw *BotGateway) Push(ctx context.Context, dest, text string) error
Push sends a text message to a specific chat on a given platform, independent of the inbound-message flow. Used by the scheduler and calendar reminder engine to deliver results to IM (OutputMode="im").
dest formats:
- "platform:chatID" — e.g. "feishu:oc_xxx", "weixin:wxid_xxx". ChatType is left empty; feishu/weixin route by ChatID alone.
- "platform:chatType:chatID" — e.g. "qq:group:xxxx". Needed for QQ, whose send URL is chosen by ChatType (dm/group/guild/direct). Omitting chatType for QQ defaults to dm, which fails for group/channel IDs.
No-op (returns nil) if the platform adapter isn't connected — a scheduled push shouldn't fail the task run just because IM is offline.
func (*BotGateway) RecentChats ¶
func (gw *BotGateway) RecentChats() []RecentChat
RecentChats returns a snapshot of recently-seen chats, newest first. Used by the desktop layer to populate the IM-target picker in the task form.
type ChannelConfig ¶
ChannelConfig overrides gateway defaults for one IM channel.
type DesktopBridge ¶ added in v0.2.0
type DesktopBridge interface {
// Sessions 枚举当前所有桌面 live 会话。
Sessions() []DesktopSessionInfo
// SetWatch 订阅/退订当前聊天的桌面事件推送(审批请求、任务完成/出错)。
SetWatch(route DesktopWatchRoute, enable bool) error
// Watching 返回该聊天当前是否在订阅。
Watching(route DesktopWatchRoute) bool
// Approve 应答任意桌面会话的待审批项,返回用户可读的结果文案。
Approve(approvalID string, allow bool) (string, error)
// AskQuestions 返回某个待回答 ask 的问题列表(用于把 IM 文本解析成选项)。
AskQuestions(askID string) ([]event.AskQuestion, bool)
// Answer 应答任意桌面会话的待回答 ask,返回用户可读的结果文案。
Answer(askID string, answers []event.AskAnswer) (string, error)
}
DesktopBridge 由桌面端进程实现,让 bot 聊天获得对整个桌面端的上帝视角: 全局会话清单、事件订阅、以及对任意桌面 live 会话的远程审批/问答。
审批应答与桌面 UI 是"先到者赢"(controller 侧幂等,重复应答被静默忽略), Approve/Answer 的返回文案应体现"以先到者为准"。
type DesktopPendingInfo ¶ added in v0.2.0
DesktopPendingInfo 是一条待处理的审批或问答的摘要。
type DesktopSessionInfo ¶ added in v0.2.0
type DesktopSessionInfo struct {
TabID string
Label string
Workspace string
Topic string
Ready bool
Running bool
PendingPrompt bool
// Pending 列出该会话当前待处理的审批/问答,便于用户在推送丢失时仍能
// 用 /desktop approve|answer <id> 处理。
Pending []DesktopPendingInfo
}
DesktopSessionInfo 是一个桌面 live 会话(tab)的快照,用于 /desktop status。
type DesktopWatchRoute ¶ added in v0.2.0
DesktopWatchRoute 标识一个订阅了桌面事件的 bot 聊天。
func (DesktopWatchRoute) Key ¶ added in v0.2.0
func (r DesktopWatchRoute) Key() string
Key 返回订阅表的稳定键。
type GatewayConfig ¶
type GatewayConfig struct {
Model string
MaxSteps int
WorkspaceRoot string
Channels map[Platform]ChannelConfig
Allowlist AllowlistConfig
Enabled map[Platform]bool
Debounce time.Duration
// SessionIdleTimeout controls how long a bot session can sit idle (no
// incoming messages) before its controller is closed to reclaim memory.
// A busy bot serving many users/-groups would otherwise keep every
// controller alive forever. Default 30m; 0 disables reaping.
SessionIdleTimeout time.Duration
// AllowlistSaver 当新用户被自动加入白名单时调用,用于持久化。
// 参数为更新后的完整 AllowlistConfig。nil 表示不持久化。
AllowlistSaver func(AllowlistConfig)
// OnTurnFinished 在一轮对话结束后调用(无论成功或出错),用于让上层(desktop)
// 把对话方的会话来源(platform/chatType/chatID/userID)和本次会话的本地 transcript
// 路径回写到 BotConnection 的 SessionMappings —— 否则只有手动「测试连接」才记录
// remoteID、而 SessionID(本地话题)永远为空,UI 显示「等待首条消息」。传整个
// SessionSource 而非单个 remoteID,是为了让 chatType/chatID 也带上:同一人在不同
// 群里的对话才能分开。放在 turn 结束后是因为 sessionPath 要等首轮 RunTurn 才确定
// (prewarm 时为空)。nil 表示不回写。
OnTurnFinished func(src SessionSource, sessionPath string)
// Netdev,非 nil 时启用 /netdev 系列命令(发现列表 / 证据详情)——
// 运维告警与早报推送的回话侧(FDE 的耳朵)。
Netdev NetdevBridge
// Desktop,非 nil 时启用 /desktop 系列命令(远程观察 + 审批桌面 live 会话)。
// 由桌面端进程注入;独立 bot 进程(无桌面)保持 nil,/desktop 会提示不可用。
Desktop DesktopBridge
}
GatewayConfig 是 BotGateway 的配置。
type InboundMessage ¶
type InboundMessage struct {
Platform Platform `json:"platform"`
ChatType ChatType `json:"chat_type"`
ChatID string `json:"chat_id"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Text string `json:"text"`
MessageID string `json:"message_id"`
ThreadID string `json:"thread_id,omitempty"`
MediaURLs []string `json:"media_urls,omitempty"`
Raw any `json:"-"`
}
InboundMessage 是从任一平台收到的入站消息。
func (InboundMessage) Session ¶
func (m InboundMessage) Session() SessionSource
Session derives the SessionSource from this message.
type InlineKeyboard ¶
type InlineKeyboard struct {
Rows []InlineKeyboardRow `json:"rows"`
}
InlineKeyboard 是内联键盘(用于 QQ 审批)。
type InlineKeyboardButton ¶
type InlineKeyboardButton struct {
ID string `json:"id"`
Label string `json:"label"`
Style int `json:"style,omitempty"` // 0=default, 1=primary, 2=danger
CallbackID string `json:"callback_id,omitempty"`
}
InlineKeyboardButton 是一个按钮。
type InlineKeyboardRow ¶
type InlineKeyboardRow struct {
Buttons []InlineKeyboardButton `json:"buttons"`
}
InlineKeyboardRow 是一行按钮。
type InteractiveCard ¶
type InteractiveCard struct {
Header string `json:"header"`
Elements []InteractiveCardElement `json:"elements"`
}
InteractiveCard 是交互式卡片(用于飞书审批/问答)。
type InteractiveCardElement ¶
type InteractiveCardElement struct {
Tag string `json:"tag"`
Content string `json:"content,omitempty"`
Extra map[string]any `json:"extra,omitempty"`
}
InteractiveCardElement 是卡片内元素。
type MessageHandler ¶
type MessageHandler func(ctx context.Context, msg InboundMessage)
MessageHandler 是 BotGateway 处理入站消息的回调。
type NetdevActionResult ¶ added in v0.2.0
NetdevActionResult reports a mutating IM command's outcome in human text.
type NetdevBridge ¶ added in v0.2.0
type NetdevBridge interface {
// NetdevActiveFindings lists unresolved findings, newest first.
NetdevActiveFindings() []NetdevFindingSummary
// NetdevFindingByID renders one finding (NotFound=true when absent).
NetdevFindingByID(id string) NetdevFindingDetail
// NetdevAckFinding acknowledges one finding from IM (completion-spec §5.3:
// 收到告警后可操作——ack 不解决,只标记已被人看过).
NetdevAckFinding(id string) NetdevActionResult
// NetdevProposals lists proposals pending a human decision.
NetdevProposals() []NetdevProposalSummary
// NetdevProposalApprove / NetdevProposalReject run the SAME guarded path
// as the desktop approval UI (group policy + change window + audit).
NetdevProposalApprove(id string) NetdevActionResult
NetdevProposalReject(id, reason string) NetdevActionResult
}
NetdevBridge is the netdev-side surface the desktop host injects.
type NetdevEvidenceView ¶ added in v0.2.0
NetdevEvidenceView is one evidence excerpt.
type NetdevFindingDetail ¶ added in v0.2.0
type NetdevFindingDetail struct {
ID string
Severity string
Title string
Devices []string
Detail string
Status string
Evidence []NetdevEvidenceView
NotFound bool
}
NetdevFindingDetail is one finding's full text for 「详情」.
type NetdevFindingSummary ¶ added in v0.2.0
type NetdevFindingSummary struct {
ID string
Severity string
Title string
Devices []string
Status string
}
NetdevFindingSummary is one row of 「发现」.
type NetdevProposalSummary ¶ added in v0.2.0
NetdevProposalSummary is one row of 「变更」 for the IM command surface.
type OutboundMessage ¶
type OutboundMessage struct {
ChatID string `json:"chat_id"`
ChatType ChatType `json:"chat_type,omitempty"`
Text string `json:"text,omitempty"`
MediaURLs []string `json:"media_urls,omitempty"`
ReplyToMsgID string `json:"reply_to_msg_id,omitempty"`
Keyboard *InlineKeyboard `json:"keyboard,omitempty"`
Card *InteractiveCard `json:"card,omitempty"`
}
OutboundMessage 是发送到平台的消息。
type RecentChat ¶
type RecentChat struct {
Platform Platform `json:"platform"`
ChatType ChatType `json:"chatType"`
ChatID string `json:"chatId"`
UserName string `json:"userName"`
LastSeen int64 `json:"lastSeen"` // unix seconds
}
RecentChat is one entry in the gateway's "recently seen chats" ring buffer, surfaced to the frontend so the user can pick an IM destination for scheduled tasks and calendar reminders without hand-typing a chatID. UserName is the best available display name (private chat = the user's name; group chat may be empty when the platform doesn't expose group names).
type SendResult ¶
type SendResult struct {
MessageID string `json:"message_id,omitempty"`
Err error `json:"err,omitempty"`
}
SendResult 是发送消息的结果。
type SessionManager ¶
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager 管理 session 级别的并发控制:同一 session 同时只跑一个任务。
func NewSessionManager ¶
func NewSessionManager(debounce time.Duration) *SessionManager
NewSessionManager 创建一个新的 session 管理器。debounce 是消息合并窗口。
func (*SessionManager) ActiveCount ¶
func (sm *SessionManager) ActiveCount() int
ActiveCount 返回当前活跃 session 数。
func (*SessionManager) ForceRelease ¶
func (sm *SessionManager) ForceRelease(key string)
ForceRelease 强制释放 session(用于 session 关闭或错误恢复)。
func (*SessionManager) IsActive ¶
func (sm *SessionManager) IsActive(key string) bool
IsActive 返回 session 是否有正在运行的任务。
func (*SessionManager) Release ¶
func (sm *SessionManager) Release(key string) *InboundMessage
Release 释放 session 锁,返回等待队列中的下一条消息(合并后)。
func (*SessionManager) TryAcquire ¶
func (sm *SessionManager) TryAcquire(key string, msg InboundMessage) (acquired bool, merged bool)
TryAcquire 尝试获取 session 锁。如果 session 正忙且消息非绕过命令,返回 false。 返回 (acquired, merged) — merged 为 true 表示消息已合并到等待队列。
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package feishu 实现飞书自建应用 Bot 适配器。
|
Package feishu 实现飞书自建应用 Bot 适配器。 |
|
Package qq 实现 QQ 官方 Bot API v2 适配器。
|
Package qq 实现 QQ 官方 Bot API v2 适配器。 |
|
Package telegram 实现 Telegram Bot API 适配器。
|
Package telegram 实现 Telegram Bot API 适配器。 |
|
Package weixin 实现微信 iLink Bot 适配器。
|
Package weixin 实现微信 iLink Bot 适配器。 |