messaging

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package messaging implements VORTEX's messaging integration layer (build plan M11): two-way bots over Telegram, WhatsApp, and Slack, plus a unified notification router and an AI provider gateway. All platform APIs are reached over the standard library's net/http — no SDKs.

This file implements the Telegram Bot API integration.

Index

Constants

View Source
const (
	ProviderClaude   = "claude"
	ProviderOpenAI   = "openai"
	ProviderOllama   = "ollama"
	ProviderDeepSeek = "deepseek"
	ProviderGemini   = "gemini"
)

Provider names.

View Source
const (
	ChannelTelegram = "telegram"
	ChannelWhatsApp = "whatsapp"
	ChannelSlack    = "slack"
	ChannelEmail    = "email"
)

Channel names.

View Source
const ApprovalTimeout = 10 * time.Minute

ApprovalTimeout is how long an approval request waits for a human response before it is treated as rejected.

Variables

View Source
var ErrBudgetExceeded = fmt.Errorf("messaging: daily AI budget exceeded")

ErrBudgetExceeded is returned when the daily cost budget is reached.

Functions

This section is empty.

Types

type AIGateway

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

AIGateway routes completion requests across providers in priority order, implementing agents.AIGateway. It tracks token cost and enforces a daily budget.

func NewAIGateway

func NewAIGateway(cfg AIGatewayConfig) (*AIGateway, error)

NewAIGateway builds the gateway. It requires at least one provider and sorts them by ascending priority.

func (*AIGateway) Complete

func (g *AIGateway) Complete(ctx context.Context, prompt, systemPrompt string) (string, error)

Complete tries each provider in priority order until one succeeds, returning the response text. It enforces the daily budget before calling out.

func (*AIGateway) Cost

func (g *AIGateway) Cost() float64

Cost returns the total USD spent today, rolling over (to 0) first if the day boundary has passed since the last activity.

func (*AIGateway) CostToday

func (g *AIGateway) CostToday() CostSnapshot

CostToday returns a snapshot of today's AI spend and budget.

func (*AIGateway) ProviderNames

func (g *AIGateway) ProviderNames() []string

ProviderNames returns the configured provider names in priority order, for startup logging.

func (*AIGateway) ResetDailyCost

func (g *AIGateway) ResetDailyCost()

ResetDailyCost zeroes today's spend (called at midnight by a supervisor).

type AIGatewayConfig

type AIGatewayConfig struct {
	Providers    []AIProvider
	DefaultModel string
	CostPerToken map[string]float64 // model → USD per token (rough)
	DailyBudget  float64            // USD; 0 = unlimited
	Client       *http.Client
	// contains filtered or unexported fields
}

AIGatewayConfig configures the gateway.

type AIProvider

type AIProvider struct {
	Name     string   // "claude" | "openai" | "ollama"
	APIKey   string   // from env (empty for ollama)
	Endpoint string   // override base URL; for ollama defaults to localhost:11434
	Models   []string // available models; Models[0] is used by default
	Priority int      // lower = preferred
}

AIProvider describes one upstream model provider.

type ApprovalManager

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

ApprovalManager bridges agent ApprovalRequests to a messaging channel (Telegram) and back: it sends an approve/reject prompt, blocks until the user taps a button or the timeout elapses, and resolves the agents.ApprovalFunc.

func NewApprovalManager

func NewApprovalManager(tg *TelegramBot, chatID int64, timeout time.Duration) *ApprovalManager

NewApprovalManager builds a manager that routes approvals to the given Telegram bot + chat. timeout <= 0 uses ApprovalTimeout.

func (*ApprovalManager) ApprovalFunc

func (m *ApprovalManager) ApprovalFunc() agents.ApprovalFunc

ApprovalFunc returns an agents.ApprovalFunc bound to this manager. It is wired into the coordinator so run_command (and other gated actions) request human sign-off before executing.

func (*ApprovalManager) Resolve

func (m *ApprovalManager) Resolve(callbackData string) bool

Resolve records an approve/reject decision for the approval with the given id (extracted from a Telegram callback_data "approve:<id>" / "reject:<id>"). It returns true if a pending approval matched.

type CallbackResolver

type CallbackResolver interface {
	Resolve(callbackData string) bool
}

CallbackResolver consumes an inline-button callback. It returns true if the callback was a recognised approval decision (and therefore should not be re-submitted to the agent runtime as a free-form directive). The ApprovalManager implements this.

type ClarifyQuestion

type ClarifyQuestion struct {
	Question string
	Options  []string
	Key      string
}

ClarifyQuestion is a structured clarifying question for Telegram buttons.

type CommandHooks

type CommandHooks struct {
	Status  func() string                                  // /status reply
	Routes  func() string                                  // /routes reply
	Cost    func() string                                  // /cost reply
	List    func(path string) string                       // /ls reply
	Approve func(sessionID string, ok bool) (string, bool) // /approve, /reject
	// ClarifySubmit submits a combined clarifying answer for a session (used when
	// all option buttons have been tapped).
	ClarifySubmit func(sessionID, answer string)
}

CommandHooks supply data for the bot's built-in slash commands. Each is optional; a nil hook yields a "not available" reply. They keep telegram.go decoupled from the api/agents packages (wired in start.go).

type CostSnapshot

type CostSnapshot struct {
	Provider        string  `json:"provider"`
	TotalUSD        float64 `json:"total_usd"`
	RequestsToday   int     `json:"requests_today"`
	DailyBudget     float64 `json:"daily_budget"`
	RemainingBudget float64 `json:"remaining_budget"`
	Free            bool    `json:"free"`
}

CostSnapshot summarises AI usage for the current day (for /api/ai/cost).

type NotificationConfig

type NotificationConfig struct {
	Rules         []NotificationRule
	Telegram      *TelegramBot
	WhatsApp      *WhatsAppBot
	Slack         *SlackBot
	DefaultChatID int64  // Telegram default recipient
	DefaultPhone  string // WhatsApp default recipient
}

NotificationConfig configures the router. Any bot may be nil (not configured).

type NotificationRule

type NotificationRule struct {
	Severity     Severity
	Channels     []string
	SilenceUntil time.Time
}

NotificationRule maps a severity to the channels it should reach.

type Router

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

Router dispatches notifications to configured channels per severity rules.

func NewRouter

func NewRouter(cfg NotificationConfig) *Router

NewRouter builds a router from cfg.

func (*Router) ConfiguredChannels

func (r *Router) ConfiguredChannels() []string

ConfiguredChannels returns the names of channels that have a backing bot, for startup logging.

func (*Router) Send

func (r *Router) Send(ctx context.Context, severity Severity, title, body string) error

Send dispatches a notification to every channel matching the severity rules, skipping silenced channels. It returns nil when there is nothing to send (no matching rule or no configured channel) and a combined error if any send fails.

func (*Router) SendFile

func (r *Router) SendFile(ctx context.Context, filename string, data []byte, caption string) error

SendFile delivers a file to channels that support file attachments (currently Telegram). It is a no-op for channels that don't.

func (*Router) Silence

func (r *Router) Silence(channel string, duration time.Duration)

Silence suppresses a channel for the given duration.

type Severity

type Severity string

Severity classifies a notification's importance.

const (
	SeverityInfo     Severity = "info"
	SeverityWarn     Severity = "warn"
	SeverityCritical Severity = "critical"
)

Severity levels.

func (Severity) String

func (s Severity) String() string

String renders a severity for logging.

type SlackBot

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

SlackBot is a Slack client + slash-command handler.

func NewSlackBot

func NewSlackBot(cfg SlackConfig) (*SlackBot, error)

NewSlackBot constructs the bot.

func (*SlackBot) HandleSlashCommand

func (s *SlackBot) HandleSlashCommand(runtime *agents.Runtime) http.Handler

HandleSlashCommand returns the http.Handler for POST /webhook/slack. It verifies the Slack signature, parses the slash command, and either submits to the runtime or performs a built-in action. It responds immediately with 200 and an ephemeral acknowledgement; longer results are delivered out of band.

func (*SlackBot) SendAlert

func (s *SlackBot) SendAlert(ctx context.Context, title, body, severity string) error

SendAlert posts a colour-coded attachment based on severity.

func (*SlackBot) SendMessage

func (s *SlackBot) SendMessage(ctx context.Context, text string) error

SendMessage posts text to the configured incoming webhook.

type SlackConfig

type SlackConfig struct {
	WebhookURL    string // incoming webhook for SendMessage/SendAlert
	SigningSecret string // verifies slash-command requests
	BotToken      string // bot token for Web API (future use)
	Client        *http.Client
	// contains filtered or unexported fields
}

SlackConfig configures the Slack integration (incoming webhook for outbound messages, slash commands for inbound). Secrets come from the environment.

type TelegramBot

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

TelegramBot is a Telegram Bot API client + webhook handler.

func NewTelegramBot

func NewTelegramBot(cfg TelegramConfig) (*TelegramBot, error)

NewTelegramBot constructs a bot. It returns an error if the token is empty.

func (*TelegramBot) HandleWebhook

func (t *TelegramBot) HandleWebhook(runtime *agents.Runtime) http.Handler

HandleWebhook returns an http.Handler that parses Telegram updates, validates the secret token and chat allow-list, submits the message text to the agent runtime, and replies with the response. Inline-button callbacks (approve/ reject) are acknowledged and their data submitted as a directive.

func (*TelegramBot) Poll

func (t *TelegramBot) Poll(ctx context.Context, runtime *agents.Runtime, interval time.Duration)

Poll runs long-poll mode: every interval it fetches new updates via getUpdates and dispatches them, until ctx is cancelled. No public URL is needed (for local testing). updateID tracks the offset across calls.

func (*TelegramBot) SendApprovalRequest

func (t *TelegramBot) SendApprovalRequest(ctx context.Context, chatID int64, description, approveAction, rejectAction string) error

SendApprovalRequest sends a message with inline approve/reject buttons. The approveAction/rejectAction strings are the callback_data delivered back when the user taps a button (used by the human-in-the-loop flow).

func (*TelegramBot) SendClarifyingQuestions

func (t *TelegramBot) SendClarifyingQuestions(ctx context.Context, chatID int64, session string, qs []ClarifyQuestion) error

SendClarifyingQuestions sends each structured question as a row of inline option buttons (callback_data "clarify:<session>:<key>:<value>"). Questions with no options are sent as plain text (the next text message is the answer). It registers a clarifySession so taps are collected until all are answered.

func (*TelegramBot) SendFile

func (t *TelegramBot) SendFile(ctx context.Context, chatID int64, filename string, data []byte, caption string) error

SendFile uploads a document (APK, PDF, report) to chatID with a caption, using a multipart form (sendDocument).

func (*TelegramBot) SendMessage

func (t *TelegramBot) SendMessage(ctx context.Context, chatID int64, text string) error

SendMessage sends a Markdown text message to chatID.

func (*TelegramBot) SetCallbackResolver

func (t *TelegramBot) SetCallbackResolver(r CallbackResolver)

SetCallbackResolver wires a resolver (e.g. the ApprovalManager) that gets first refusal on inline-button callbacks.

func (*TelegramBot) SetCommandHooks

func (t *TelegramBot) SetCommandHooks(h CommandHooks)

SetCommandHooks wires the built-in command data sources.

func (*TelegramBot) SetCommands

func (t *TelegramBot) SetCommands(ctx context.Context) error

SetCommands registers the bot's command menu (/setMyCommands).

func (*TelegramBot) SetWebhook

func (t *TelegramBot) SetWebhook(ctx context.Context, url string) error

SetWebhook registers the webhook URL with Telegram, including the secret token Telegram will echo back in the X-Telegram-Bot-Api-Secret-Token header.

type TelegramConfig

type TelegramConfig struct {
	Token       string  // bot token (from env)
	AllowedIDs  []int64 // allowed chat/user IDs; empty means allow none
	WebhookURL  string  // public webhook URL (optional)
	SecretToken string  // X-Telegram-Bot-Api-Secret-Token expected on webhooks
	// BaseURL overrides the Telegram API base (for tests). When empty the real
	// https://api.telegram.org/bot<token> is used.
	BaseURL string
	// Client overrides the HTTP client (for tests / custom timeouts).
	Client *http.Client
}

TelegramConfig configures the Telegram bot. The token comes from the environment (VORTEX_TELEGRAM_TOKEN), never from the config file.

type WhatsAppBot

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

WhatsAppBot is a WhatsApp Cloud API client + webhook handler.

func NewWhatsAppBot

func NewWhatsAppBot(cfg WhatsAppConfig) (*WhatsAppBot, error)

NewWhatsAppBot constructs the bot. It requires PhoneNumberID and AccessToken.

func (*WhatsAppBot) HandleWebhook

func (wb *WhatsAppBot) HandleWebhook(runtime *agents.Runtime) http.Handler

HandleWebhook returns the http.Handler for the WhatsApp webhook. GET performs Meta's verification handshake; POST verifies the HMAC signature, extracts inbound messages, submits them to the runtime, and replies.

func (*WhatsAppBot) SendApprovalRequest

func (wb *WhatsAppBot) SendApprovalRequest(ctx context.Context, to, description string) error

SendApprovalRequest sends an interactive button message (Approve / Reject) used by the human-in-the-loop flow.

func (*WhatsAppBot) SendImage

func (wb *WhatsAppBot) SendImage(ctx context.Context, to string, imageData []byte, caption string) error

SendImage uploads imageData and sends it as an image message with a caption.

func (*WhatsAppBot) SendText

func (wb *WhatsAppBot) SendText(ctx context.Context, to, text string) error

SendText sends a plain text message to the recipient phone number.

type WhatsAppConfig

type WhatsAppConfig struct {
	PhoneNumberID string // from env VORTEX_WA_PHONE_ID
	AccessToken   string // from env VORTEX_WA_TOKEN
	VerifyToken   string // webhook GET verification (hub.verify_token)
	AppSecret     string // for X-Hub-Signature-256 HMAC verification
	BaseURL       string // override Graph API base (tests)
	Client        *http.Client
}

WhatsAppConfig configures the WhatsApp Business (Meta Cloud API) bot. The phone-number ID and access token come from the environment, never config.

Jump to

Keyboard shortcuts

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