bot

package
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package bot 实现 fairpeer 多渠道 IM bot 消息网关,支持 QQ、飞书、微信、Telegram。 架构参考 Hermes Agent 的 gateway/adapter/session 模式。

Index

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 内所有人共享上下文)

func IsSlashBypass

func IsSlashBypass(text string) bool

IsSlashBypass 判断消息是否为绕过队列的斜杠命令。

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.

func (*BotGateway) Start

func (gw *BotGateway) Start(ctx context.Context) error

Start 启动所有已启用的平台适配器并开始处理消息。

func (*BotGateway) Stop

func (gw *BotGateway) Stop()

Stop 停止所有适配器并关闭所有 session。

type ChannelConfig

type ChannelConfig struct {
	Model         string
	WorkspaceRoot string
}

ChannelConfig overrides gateway defaults for one IM channel.

type ChatType

type ChatType string

ChatType 标识会话类型。

const (
	ChatDM     ChatType = "dm"
	ChatGroup  ChatType = "group"
	ChatGuild  ChatType = "guild"
	ChatDirect ChatType = "direct"
	ChatThread ChatType = "thread"
)

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)
	// 把对话方的远端 ID(如飞书 open_id)和本次会话的本地 transcript 路径回写到
	// BotConnection 的 SessionMappings —— 否则只有手动「测试连接」才记录 remoteID、
	// 而 SessionID(本地话题)永远为空,UI 显示「等待首条消息」。放在 turn 结束后
	// 是因为 sessionPath 要等首轮 RunTurn 才确定(prewarm 时为空)。nil 表示不回写。
	OnTurnFinished func(plat Platform, remoteID, sessionPath string)
}

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 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 Platform

type Platform string

Platform 标识 IM 平台。

const (
	PlatformQQ       Platform = "qq"
	PlatformFeishu   Platform = "feishu"
	PlatformWeixin   Platform = "weixin"
	PlatformTelegram Platform = "telegram"
)

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 表示消息已合并到等待队列。

type SessionSource

type SessionSource struct {
	Platform Platform `json:"platform"`
	ChatType ChatType `json:"chat_type"`
	ChatID   string   `json:"chat_id"`
	UserID   string   `json:"user_id"`
	ThreadID string   `json:"thread_id,omitempty"`
}

SessionSource 是会话的复合标识,用于生成稳定的 session key。

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 适配器。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL