bot

package
v0.1.8 Latest Latest
Warning

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

Go to latest
Published: May 26, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package bot provides bot adapters for various IM platforms.

This package implements a unified interface for connecting to multiple chat platforms, including Discord, Telegram, Feishu (Lark), and DingTalk. Each adapter handles platform-specific connection logic, message formatting, and communication patterns.

Supported Platforms

  • Discord: WebSocket connection with real-time message handling
  • Telegram: Long polling for message updates
  • Feishu/Lark: WebSocket long connection for enterprise messaging
  • DingTalk: WebSocket long connection for enterprise messaging

Usage

To use a bot adapter:

  1. Create a bot instance using the New* function for your platform
  2. Call Start() with a message handler callback
  3. Send messages using SendMessage()
  4. Call Stop() when shutting down

Example:

discordBot := bot.NewDiscordBot(token, channelID)
err := discordBot.Start(func(msg bot.BotMessage) {
    fmt.Printf("Received: %s\n", msg.Content)
})
if err != nil {
    log.Fatal(err)
}
discordBot.SendMessage(channelID, "Hello, world!")
discordBot.Stop()

Thread Safety

All bot adapters are thread-safe and use internal mutexes to protect shared state. The message handler callback may be called concurrently from multiple goroutines.

Index

Constants

View Source
const (
	QQTokenURL   = "https://bots.qq.com/app/getAppAccessToken"
	QQAPIBase    = "https://api.sgroup.qq.com"
	QQGatewayURL = QQAPIBase + "/gateway"
)

QQ Bot API endpoints

View Source
const (
	OPDispatch       = 0  // Event dispatch
	OPHeartbeat      = 1  // Heartbeat
	OPIdentify       = 2  // Identify
	OPResume         = 6  // Resume
	OPReconnect      = 7  // Reconnect request
	OPInvalidSession = 9  // Invalid session
	OPHello          = 10 // Hello
	OPHeartbeatAck   = 11 // Heartbeat acknowledgement
)

WebSocket OP codes (https://bots.qq.com/docs/gateway/gateway-events)

View Source
const (
	MessageTypeUser = 1
	MessageTypeBot  = 2
)
View Source
const (
	MessageStateNew        = 0
	MessageStateGenerating = 1
	MessageStateFinish     = 2
)
View Source
const (
	MessageItemTypeText  = 1
	MessageItemTypeImage = 2
	MessageItemTypeVoice = 3
	MessageItemTypeFile  = 4
	MessageItemTypeVideo = 5
)
View Source
const (
	QRStatusWait      = "wait"
	QRStatusScaned    = "scaned"
	QRStatusConfirmed = "confirmed"
	QRStatusExpired   = "expired"
)
View Source
const (
	DefaultBaseURL        = "https://ilinkai.weixin.qq.com"
	DefaultBaseVersion    = "1.0.0"
	QRCodePollInterval    = 2 * time.Second
	APITimeout            = 15 * time.Second
	MaxMessageLength      = 2000
	MaxChunkLength        = 2000
	SessionExpiredErrCode = -14
)
View Source
const (
	IntentPublicMessages = 1 << 25 // Public message events (1 << 25)
)

Intents for subscribing to events

Variables

This section is empty.

Functions

func DefaultCredentialsPath added in v0.1.2

func DefaultCredentialsPath() string

func FormatQuoteBlock added in v0.1.8

func FormatQuoteBlock(q *QuotedMessage) string

FormatQuoteBlock formats a quoted message for CLI context injection.

func MaskSecret added in v0.1.8

func MaskSecret(s string) string

MaskSecret masks sensitive information for logging

Types

type ApiError added in v0.1.2

type ApiError struct {
	Status  int
	Code    int
	Message string
}

func (*ApiError) Error added in v0.1.2

func (e *ApiError) Error() string

func (*ApiError) IsSessionExpired added in v0.1.2

func (e *ApiError) IsSessionExpired() bool

type Attachment added in v0.1.8

type Attachment struct {
	Type     string // "image", "file", "video", "audio"
	FileName string // Original filename
	FilePath string // Local path after download
	FileKey  string // Platform-specific resource key
	MimeType string // Inferred MIME type
	Size     int64  // File size in bytes
}

Attachment represents a downloaded media file attached to a message.

type BotAdapter

type BotAdapter interface {
	// Start starts the bot, establishes connection and begins listening for messages
	Start(messageHandler func(BotMessage)) error

	// SendMessage sends a message to the IM platform
	// Adapter is responsible for:
	//   - Truncating to platform limits
	//   - Splitting long messages when necessary
	//   - Platform-specific formatting
	SendMessage(channel, message string) error

	// SupportsTypingIndicator returns true if the platform supports typing indicators
	SupportsTypingIndicator() bool

	// AddTypingIndicator adds a typing indicator to a message (if supported)
	// messageID is the ID of the user's message to react to
	// Returns true if successfully added
	AddTypingIndicator(messageID string) bool

	// RemoveTypingIndicator removes the typing indicator from a message
	RemoveTypingIndicator(messageID string) error

	// SetProxyManager sets the proxy manager for the bot
	SetProxyManager(mgr proxy.Manager)

	// Stop stops the bot and cleans up resources
	Stop() error
}

BotAdapter defines the interface for bot adapters

type BotMessage

type BotMessage struct {
	Platform    string // feishu/discord/telegram
	UserID      string // Unique user identifier (for permission control)
	Channel     string // Channel/session ID
	MessageID   string // Message ID (for typing indicator)
	Content     string // Message content
	Timestamp   time.Time
	ThreadID    string       // Optional: thread/topic ID
	QuoteID     string       // Optional: parent message ID (reply context)
	ChatType    string       // Optional: "p2p", "group", "topic"
	SenderName  string       // Optional: display name of sender
	MessageType string       // Optional: "text", "image", "file", "audio", "video", "post"
	Attachments []Attachment // Optional: downloaded media files
}

BotMessage represents a bot message structure

type C2CMessageData

type C2CMessageData struct {
	ID        string `json:"id"`
	Timestamp string `json:"timestamp"`
	Author    struct {
		UserOpenID string `json:"user_openid"`
	} `json:"author"`
	Content string `json:"content"`
}

C2CMessageData represents C2C (private chat) message data from WebSocket event

type ContentBlock added in v0.1.8

type ContentBlock struct {
	Type      ContentBlockType
	Title     string            // Block title (e.g., tool name)
	Content   string            // Markdown content
	Collapsed bool              // Whether to collapse by default
	Meta      map[string]string // Tool-specific metadata (command, file_path, etc.)
}

ContentBlock represents a logical piece of rich reply content.

type ContentBlockType added in v0.1.8

type ContentBlockType string

ContentBlockType enumerates the kinds of content blocks for rich replies.

const (
	ContentBlockText       ContentBlockType = "text"
	ContentBlockToolCall   ContentBlockType = "tool_call"
	ContentBlockToolResult ContentBlockType = "tool_result"
	ContentBlockThinking   ContentBlockType = "thinking"
	ContentBlockStatus     ContentBlockType = "status"
)

type Credentials added in v0.1.2

type Credentials struct {
	Token     string `json:"token"`
	BaseURL   string `json:"base_url"`
	AccountID string `json:"account_id"`
	UserID    string `json:"user_id"`
}

type Debounceable added in v0.1.8

type Debounceable interface {
	DebounceWindow() int // milliseconds; 0 = disabled
}

Debounceable is an optional interface for channels that benefit from message debouncing (coalescing rapid-fire messages).

type DefaultTypingIndicator

type DefaultTypingIndicator struct{}

DefaultTypingIndicator provides default empty implementations for typing indicator methods This can be embedded in bot adapters that don't support typing indicators

func (*DefaultTypingIndicator) AddTypingIndicator

func (d *DefaultTypingIndicator) AddTypingIndicator(messageID string) bool

AddTypingIndicator does nothing (not supported by default)

func (*DefaultTypingIndicator) RemoveTypingIndicator

func (d *DefaultTypingIndicator) RemoveTypingIndicator(messageID string) error

RemoveTypingIndicator does nothing (not supported by default)

func (*DefaultTypingIndicator) SupportsTypingIndicator

func (d *DefaultTypingIndicator) SupportsTypingIndicator() bool

SupportsTypingIndicator returns false (not supported by default)

type DingTalkBot

type DingTalkBot struct {
	DefaultTypingIndicator
	// contains filtered or unexported fields
}

DingTalkBot implements BotAdapter interface for DingTalk using WebSocket long connection

func NewDingTalkBot

func NewDingTalkBot(clientID, clientSecret string) *DingTalkBot

NewDingTalkBot creates a new DingTalk bot instance

func (*DingTalkBot) GetMessageHandler

func (d *DingTalkBot) GetMessageHandler() func(BotMessage)

GetMessageHandler gets the message handler in a thread-safe manner

func (*DingTalkBot) SendMessage

func (d *DingTalkBot) SendMessage(conversationID, message string) error

SendMessage sends a message to a DingTalk conversation

func (*DingTalkBot) SetMessageHandler

func (d *DingTalkBot) SetMessageHandler(handler func(BotMessage))

SetMessageHandler sets the message handler in a thread-safe manner

func (*DingTalkBot) SetProxyManager

func (d *DingTalkBot) SetProxyManager(mgr proxy.Manager)

SetProxyManager sets the proxy manager for the DingTalk bot

func (*DingTalkBot) Start

func (d *DingTalkBot) Start(messageHandler func(BotMessage)) error

Start establishes WebSocket long connection to DingTalk and begins listening for messages

func (*DingTalkBot) Stop

func (d *DingTalkBot) Stop() error

Stop closes the DingTalk WebSocket connection and cleans up resources

type DiscordBot

type DiscordBot struct {
	DefaultTypingIndicator
	// contains filtered or unexported fields
}

DiscordBot implements BotAdapter interface for Discord

func NewDiscordBot

func NewDiscordBot(token, channelID string) *DiscordBot

NewDiscordBot creates a new Discord bot instance

func (*DiscordBot) GetMessageHandler

func (d *DiscordBot) GetMessageHandler() func(BotMessage)

GetMessageHandler gets the message handler in a thread-safe manner

func (*DiscordBot) SendMessage

func (d *DiscordBot) SendMessage(channel, message string) error

SendMessage sends a message to a Discord channel

func (*DiscordBot) SetMessageHandler

func (d *DiscordBot) SetMessageHandler(handler func(BotMessage))

SetMessageHandler sets the message handler in a thread-safe manner

func (*DiscordBot) SetProxyManager

func (d *DiscordBot) SetProxyManager(mgr proxy.Manager)

SetProxyManager sets the proxy manager for the Discord bot

func (*DiscordBot) Start

func (d *DiscordBot) Start(messageHandler func(BotMessage)) error

Start establishes connection to Discord and begins listening for messages

func (*DiscordBot) Stop

func (d *DiscordBot) Stop() error

Stop closes the Discord connection and cleans up resources

type DiscordMessage

type DiscordMessage interface {
	ID() string
}

DiscordMessage represents a Discord message for our interface

type DiscordSessionInterface

type DiscordSessionInterface interface {
	AddHandler(handler interface{}) func()
	Open() error
	Close() error
	ChannelMessageSend(channelID string, content string, options ...discordgo.RequestOption) (*discordgo.Message, error)
}

DiscordSessionInterface defines the interface we need from discordgo.Session This allows us to mock it in tests without depending on concrete types

type GatewayPayload

type GatewayPayload struct {
	OP int         `json:"op"`
	D  interface{} `json:"d,omitempty"`
	S  *int        `json:"s,omitempty"`
	T  string      `json:"t,omitempty"`
}

GatewayPayload represents a WebSocket gateway message

type HelloData

type HelloData struct {
	HeartbeatInterval int `json:"heartbeat_interval"`
}

HelloData contains heartbeat_interval from OP Hello

type IdentifyData

type IdentifyData struct {
	Token   string `json:"token"`
	Intents int    `json:"intents"`
	Shard   []int  `json:"shard"`
}

IdentifyData contains identify payload

type MediaSupporter added in v0.1.8

type MediaSupporter interface {
	DownloadMedia(ctx context.Context, msg *BotMessage) error
}

MediaSupporter is an optional interface for channels that support media download.

type MentionPolicy added in v0.1.8

type MentionPolicy interface {
	ShouldRespond(msg BotMessage) bool
}

MentionPolicy is an optional interface for channels that need to filter messages based on mention rules (e.g. only respond to @bot in groups).

type PendingQueue added in v0.1.8

type PendingQueue struct {
	// contains filtered or unexported fields
}

PendingQueue coalesces messages per key using configurable debounce windows. When a message is added, the timer resets. When the window expires, all accumulated messages for that key are flushed to the handler in a single batch.

func NewPendingQueue added in v0.1.8

func NewPendingQueue(handler func([]BotMessage)) *PendingQueue

NewPendingQueue creates a new PendingQueue that batches messages per key.

func (*PendingQueue) Add added in v0.1.8

func (pq *PendingQueue) Add(key string, msg BotMessage, window time.Duration)

Add adds a message to the pending queue for the given key and resets the debounce timer. If the queue is stopped, the message is dropped.

func (*PendingQueue) Block added in v0.1.8

func (pq *PendingQueue) Block(key string)

Block stops the timer for the given key but keeps accumulated messages. Useful for holding messages while processing.

func (*PendingQueue) Stop added in v0.1.8

func (pq *PendingQueue) Stop()

Stop cleans up all pending timers. No further flushes will occur.

func (*PendingQueue) Unblock added in v0.1.8

func (pq *PendingQueue) Unblock(key string)

Unblock restarts the debounce timer for the given key using the stored window duration. If there are no pending messages, this is a no-op.

type ProcessPool added in v0.1.8

type ProcessPool struct {
	// contains filtered or unexported fields
}

ProcessPool limits concurrent CLI runs using a buffered channel as a counting semaphore. A nil or zero-cap pool is a no-op (unlimited concurrency).

func NewProcessPool added in v0.1.8

func NewProcessPool(maxRuns int) *ProcessPool

NewProcessPool creates a pool with the given capacity. Returns nil if maxRuns <= 0 (no limit).

func (*ProcessPool) Acquire added in v0.1.8

func (p *ProcessPool) Acquire()

Acquire blocks until a permit is available. No-op if the pool is nil.

func (*ProcessPool) Cap added in v0.1.8

func (p *ProcessPool) Cap() int

Cap returns the pool capacity, or 0 if unlimited (nil pool).

func (*ProcessPool) Release added in v0.1.8

func (p *ProcessPool) Release()

Release returns a permit to the pool. No-op if the pool is nil.

type QQBot

type QQBot struct {
	DefaultTypingIndicator
	// contains filtered or unexported fields
}

QQBot implements BotAdapter interface for QQ Bot Platform (QQ群机器人开放平台) Official API: https://bots.qq.com

func NewQQBot

func NewQQBot(appID, appSecret string) *QQBot

NewQQBot creates a new QQ bot instance

func (*QQBot) AddTypingIndicator

func (q *QQBot) AddTypingIndicator(messageID string) bool

AddTypingIndicator does nothing (not supported)

func (*QQBot) GetMessageHandler

func (q *QQBot) GetMessageHandler() func(BotMessage)

GetMessageHandler gets the message handler in a thread-safe manner

func (*QQBot) RemoveTypingIndicator

func (q *QQBot) RemoveTypingIndicator(messageID string) error

RemoveTypingIndicator does nothing (not supported)

func (*QQBot) SendMessage

func (q *QQBot) SendMessage(channel, message string) error

SendMessage sends a message to QQ (C2C private message)

func (*QQBot) SetMessageHandler

func (q *QQBot) SetMessageHandler(handler func(BotMessage))

SetMessageHandler sets the message handler in a thread-safe manner

func (*QQBot) SetProxyManager

func (q *QQBot) SetProxyManager(mgr proxy.Manager)

SetProxyManager sets the proxy manager for the QQ bot

func (*QQBot) Start

func (q *QQBot) Start(messageHandler func(BotMessage)) error

Start establishes connection to QQ gateway and begins listening for messages

func (*QQBot) Stop

func (q *QQBot) Stop() error

Stop stops the QQ bot and cleans up resources

func (*QQBot) SupportsTypingIndicator

func (q *QQBot) SupportsTypingIndicator() bool

SupportsTypingIndicator returns false (QQ doesn't support typing indicators)

type QQGatewayResponse

type QQGatewayResponse struct {
	URL string `json:"url"`
}

QQGatewayResponse represents the gateway URL response

type QQTokenResponse

type QQTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   string `json:"expires_in"` // API returns as string
}

QQTokenResponse represents the token response from QQ API

type Quotable added in v0.1.8

type Quotable interface {
	FetchQuotedMessage(ctx context.Context, channelID, messageID string) (*QuotedMessage, error)
}

Quotable is an optional interface for channels that support fetching quoted/referenced messages for context injection.

type QuotedMessage added in v0.1.8

type QuotedMessage struct {
	SenderID   string
	SenderName string
	Content    string
	Timestamp  time.Time
}

QuotedMessage represents a referenced message for context injection.

type Replyable added in v0.1.8

type Replyable interface {
	SendMessageWithReply(channel, message, replyToMessageID string) error
}

Replyable is an optional interface for channels that support sending replies that reference a specific message (visual thread).

type RichMessageHandle added in v0.1.8

type RichMessageHandle interface {
	Channel() string
	Update(blocks []ContentBlock) error // Incremental update during streaming
	Finish(blocks []ContentBlock) error // Final update, remove interactive elements
}

RichMessageHandle represents an active rich message being updated.

type RichMessageOptions added in v0.1.8

type RichMessageOptions struct {
	Title     string            // Optional card title
	StopText  string            // Stop button label (e.g., "Stop")
	StopData  string            // Stop button callback data
	ReplyToID string            // If set, reply to this message ID
	Meta      map[string]string // Channel-specific metadata
}

RichMessageOptions configures the creation of a rich message.

type RichMessenger added in v0.1.8

type RichMessenger interface {
	CreateRichMessage(channel string, opts RichMessageOptions) (RichMessageHandle, error)
}

RichMessenger is an optional interface for channels that support streaming rich replies (cards, embeds, etc.).

type SendMessageRequest

type SendMessageRequest struct {
	Content string `json:"content"`
	MsgType int    `json:"msg_type"`
	MsgID   string `json:"msg_id,omitempty"`
	MsgSeq  int    `json:"msg_seq,omitempty"`
}

SendMessageRequest represents the request payload for sending messages

type SendMessageResponse

type SendMessageResponse struct {
	ID string `json:"id"`
}

SendMessageResponse represents the response from sending a message

type TelegramBot

type TelegramBot struct {
	DefaultTypingIndicator
	// contains filtered or unexported fields
}

TelegramBot implements BotAdapter interface for Telegram using long polling

func NewTelegramBot

func NewTelegramBot(token string) *TelegramBot

NewTelegramBot creates a new Telegram bot instance

func (*TelegramBot) GetMessageHandler

func (t *TelegramBot) GetMessageHandler() func(BotMessage)

GetMessageHandler gets the message handler in a thread-safe manner

func (*TelegramBot) SendMessage

func (t *TelegramBot) SendMessage(chatID, message string) error

SendMessage sends a message to a Telegram chat

func (*TelegramBot) SetMessageHandler

func (t *TelegramBot) SetMessageHandler(handler func(BotMessage))

SetMessageHandler sets the message handler in a thread-safe manner

func (*TelegramBot) SetProxyManager

func (t *TelegramBot) SetProxyManager(mgr proxy.Manager)

SetProxyManager sets the proxy manager for the Telegram bot

func (*TelegramBot) Start

func (t *TelegramBot) Start(messageHandler func(BotMessage)) error

Start establishes long polling connection to Telegram and begins listening for messages

func (*TelegramBot) Stop

func (t *TelegramBot) Stop() error

Stop closes the Telegram long polling connection and cleans up resources

type Threadable added in v0.1.8

type Threadable interface {
	ThreadScope(channelID string, msg BotMessage) string
}

Threadable is an optional interface for channels that support thread/topic isolation within a chat.

type WeixinBot added in v0.1.2

type WeixinBot struct {
	DefaultTypingIndicator
	// contains filtered or unexported fields
}

func NewWeixinBot added in v0.1.2

func NewWeixinBot(baseURL, credentialsPath string) *WeixinBot

func (*WeixinBot) AddTypingIndicator added in v0.1.2

func (b *WeixinBot) AddTypingIndicator(messageID string) bool

func (*WeixinBot) RemoveTypingIndicator added in v0.1.2

func (b *WeixinBot) RemoveTypingIndicator(messageID string) error

func (*WeixinBot) SendMessage added in v0.1.2

func (b *WeixinBot) SendMessage(channel, message string) error

func (*WeixinBot) SetProxyManager added in v0.1.2

func (b *WeixinBot) SetProxyManager(mgr proxy.Manager)

func (*WeixinBot) Start added in v0.1.2

func (b *WeixinBot) Start(messageHandler func(BotMessage)) error

func (*WeixinBot) Stop added in v0.1.2

func (b *WeixinBot) Stop() error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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