Documentation
¶
Overview ¶
Package wgrok implements an ngrok clone using the Webex API as a message bus.
Index ¶
- Constants
- Variables
- func Chunk(payload string, maxSize int) ([]string, error)
- func Compress(data string) (string, error)
- func Decompress(data string) (string, error)
- func Decrypt(data string, key []byte) (string, error)
- func Encrypt(data string, key []byte) (string, error)
- func ExtractCards(message map[string]interface{}) []interface{}
- func FormatEcho(to, from, flags, payload string) string
- func FormatFlags(compressed, encrypted bool, chunkSeq, chunkTotal int) string
- func FormatResponse(to, from, flags, payload string) string
- func GetAttachmentAction(token, actionID string, client *http.Client) (map[string]interface{}, error)
- func GetLogger(debug bool, module string) wmh.Logger
- func GetMessage(token, messageID string, client *http.Client) (map[string]interface{}, error)
- func IsEcho(text string) bool
- func IsPause(text string) bool
- func IsResume(text string) bool
- func ParseEcho(text string) (to, from, flags, payload string, err error)
- func ParseFlags(flags string) (compressed, encrypted bool, chunkSeq, chunkTotal int, err error)
- func ParseResponse(text string) (to, from, flags, payload string, err error)
- func PlatformSendCard(platform, token, target, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func PlatformSendCardToRoom(platform, token, roomID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func PlatformSendMessage(platform, token, target, text string, client *http.Client) (map[string]interface{}, error)
- func PlatformSendMessageToRoom(platform, token, roomID, text string, client *http.Client) (map[string]interface{}, error)
- func SendCard(token, toEmail, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func SendCardToRoom(token, roomID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func SendDiscordCard(token, channelID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func SendDiscordMessage(token, channelID, text string, client *http.Client) (map[string]interface{}, error)
- func SendIRCCard(connStr, target, text string, card interface{}) (map[string]interface{}, error)
- func SendIRCMessage(connStr, target, text string) (map[string]interface{}, error)
- func SendMessage(token, toEmail, text string, client *http.Client) (map[string]interface{}, error)
- func SendMessageToRoom(token, roomID, text string, client *http.Client) (map[string]interface{}, error)
- func SendSlackCard(token, channel, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
- func SendSlackMessage(token, channel, text string, client *http.Client) (map[string]interface{}, error)
- func StripBotMention(text, html string) string
- type Allowlist
- type BotConfig
- type CardAttachment
- type ControlHandler
- type DefaultSlackHTTPClient
- type DiscordListener
- type IRCParams
- type IncomingMessage
- type IrcConnection
- type IrcListener
- type MessageCallback
- type MessageHandler
- type NdjsonLogger
- type PlatformListener
- type ReceiverConfig
- type SenderConfig
- type SimpleHTTPClient
- type SlackListener
- type WebexListener
- type WgrokReceiver
- type WgrokRouterBot
- type WgrokSender
- func (s *WgrokSender) Pause(notify bool) error
- func (s *WgrokSender) Resume(notify bool) error
- func (s *WgrokSender) Send(payload string, card interface{}) (map[string]interface{}, error)
- func (s *WgrokSender) SendWithOptions(payload string, card interface{}, compress bool) (map[string]interface{}, error)
Constants ¶
const ( WebexAPIBase = "https://webexapis.com/v1" AdaptiveCardContentType = "application/vnd.microsoft.card.adaptive" MaxRetries = 3 )
const (
DiscordAPIBase = "https://discord.com/api/v10"
)
const EchoPrefix = "./echo:"
const PauseCmd = "./pause"
const ResumeCmd = "./resume"
const (
SlackAPIBase = "https://slack.com/api"
)
Variables ¶
var ( WebexMessagesURL = WebexAPIBase + "/messages" WebexAttachmentActionsURL = WebexAPIBase + "/attachment/actions" )
Package-level URLs, overridable in tests.
var ( DiscordChannelMessagesURL = func(channelID string) string { return DiscordAPIBase + "/channels/" + channelID + "/messages" } )
Package-level URL, overridable in tests.
var PlatformLimits = map[string]int{
"webex": 7439,
"slack": 4000,
"discord": 2000,
"irc": 400,
}
PlatformLimits defines message size limits per platform.
var (
SlackPostMessageURL = SlackAPIBase + "/chat.postMessage"
)
Package-level URL, overridable in tests.
Functions ¶
func Chunk ¶
Chunk splits payload into raw chunks of at most maxSize bytes each. Returns just the raw chunk data (no N/T: prefix).
func Decompress ¶
Decompress attempts to base64-decode and gzip-decompress data. If decompression fails, returns an error (fail-closed).
func Decrypt ¶
Decrypt decrypts data encrypted with Encrypt. Expects base64(IV || ciphertext_with_tag).
func Encrypt ¶
Encrypt encrypts data using AES-256-GCM with a random 12-byte IV. Returns base64(IV || ciphertext_with_tag).
func ExtractCards ¶
func ExtractCards(message map[string]interface{}) []interface{}
ExtractCards extracts adaptive card content from a message's attachments.
func FormatEcho ¶
FormatEcho formats an outgoing echo message: ./echo:{to}:{from}:{flags}:{payload}
func FormatFlags ¶
FormatFlags formats a flags string from components. If chunkSeq is 0, no chunking. Otherwise format is "N/T" or "zN/T" if compressed, "eN/T" if encrypted, "zeN/T" if both.
func FormatResponse ¶
FormatResponse formats a response message: {to}:{from}:{flags}:{payload}
func GetAttachmentAction ¶
func GetAttachmentAction(token, actionID string, client *http.Client) (map[string]interface{}, error)
GetAttachmentAction fetches an attachment action (card submission) by ID.
func GetLogger ¶
GetLogger returns an NdjsonLogger if debug is true, otherwise a minLevelWgrokLogger that only outputs WARN/ERROR.
func GetMessage ¶
GetMessage fetches full message details by ID (includes attachments).
func ParseFlags ¶
ParseFlags parses a flags string and returns (compressed, encrypted, chunkSeq, chunkTotal). Format: "-" for no flags, "z" for compressed, "e" for encrypted, "N/T" for chunk N of T. Combinations: "z", "e", "ze", "1/3", "z1/3", "e1/3", "ze1/3" etc. If chunkSeq is 0, there is no chunking.
func ParseResponse ¶
ParseResponse parses a response message, returning (to, from, flags, payload).
func PlatformSendCard ¶
func PlatformSendCard(platform, token, target, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
PlatformSendCard sends a message with card/rich content via the specified platform.
func PlatformSendCardToRoom ¶
func PlatformSendCardToRoom(platform, token, roomID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
PlatformSendCardToRoom sends a message with card/rich content to a room via the specified platform.
func PlatformSendMessage ¶
func PlatformSendMessage(platform, token, target, text string, client *http.Client) (map[string]interface{}, error)
PlatformSendMessage sends a text message via the specified platform.
func PlatformSendMessageToRoom ¶
func PlatformSendMessageToRoom(platform, token, roomID, text string, client *http.Client) (map[string]interface{}, error)
PlatformSendMessageToRoom sends a text message to a room via the specified platform.
func SendCard ¶
func SendCard(token, toEmail, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
SendCard sends a Webex message with an adaptive card attachment.
func SendCardToRoom ¶
func SendCardToRoom(token, roomID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
SendCardToRoom sends a Webex message with an adaptive card attachment to a room by room ID.
func SendDiscordCard ¶
func SendDiscordCard(token, channelID, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
SendDiscordCard sends a Discord message with an embed.
func SendDiscordMessage ¶
func SendDiscordMessage(token, channelID, text string, client *http.Client) (map[string]interface{}, error)
SendDiscordMessage sends a text-only Discord message to a channel.
func SendIRCCard ¶
SendIRCCard sends a message via IRC. Cards are not supported - only text is sent.
func SendIRCMessage ¶
SendIRCMessage sends a message via IRC (not implemented - would require persistent connection). Returns a map indicating the message was queued for sending.
func SendMessage ¶
SendMessage sends a text-only Webex message to a person by email.
func SendMessageToRoom ¶
func SendMessageToRoom(token, roomID, text string, client *http.Client) (map[string]interface{}, error)
SendMessageToRoom sends a text-only Webex message to a room by room ID.
func SendSlackCard ¶
func SendSlackCard(token, channel, text string, card interface{}, client *http.Client) (map[string]interface{}, error)
SendSlackCard sends a Slack message with Block Kit blocks.
func SendSlackMessage ¶
func SendSlackMessage(token, channel, text string, client *http.Client) (map[string]interface{}, error)
SendSlackMessage sends a text-only Slack message to a channel.
func StripBotMention ¶
StripBotMention strips a bot display name prefix from text using spark-mention tags in HTML.
Types ¶
type Allowlist ¶
type Allowlist struct {
// contains filtered or unexported fields
}
Allowlist validates email addresses against a list of allowed patterns.
Supported formats:
- "domain.tld" → matches *@domain.tld (bare domain)
- "*@domain.tld" → wildcard prefix match
- "user@domain.tld" → exact match (case-insensitive)
Patterns with [, ], or ? are rejected (dangerous).
func NewAllowlist ¶
NewAllowlist creates an Allowlist from the given patterns.
type BotConfig ¶
type BotConfig struct {
WebexToken string
Domains []string
Routes map[string]string
PlatformTokens map[string][]string
WebhookPort *int
WebhookSecret *string
Debug bool
}
BotConfig holds configuration for WgrokRouterBot.
func BotConfigFromEnv ¶
BotConfigFromEnv loads a BotConfig from environment variables.
type CardAttachment ¶
type CardAttachment struct {
ContentType string `json:"contentType"`
Content interface{} `json:"content"`
}
CardAttachment represents a Webex message card attachment.
type ControlHandler ¶
type ControlHandler func(cmd string)
ControlHandler is the callback type for control messages (pause/resume). It receives the control command name.
type DefaultSlackHTTPClient ¶
type DefaultSlackHTTPClient struct{}
DefaultSlackHTTPClient is the default HTTP client for Slack API calls.
type DiscordListener ¶
type DiscordListener struct {
// contains filtered or unexported fields
}
DiscordListener listens for Discord messages via Gateway WebSocket.
func NewDiscordListener ¶
func NewDiscordListener(token string, logger wmh.Logger) *DiscordListener
NewDiscordListener creates a new Discord listener.
func (*DiscordListener) Connect ¶
func (l *DiscordListener) Connect(ctx context.Context) error
Connect establishes the WebSocket connection to Discord Gateway.
func (*DiscordListener) Disconnect ¶
func (l *DiscordListener) Disconnect(ctx context.Context) error
Disconnect closes the connection to Discord.
func (*DiscordListener) OnMessage ¶
func (l *DiscordListener) OnMessage(callback MessageCallback)
OnMessage registers a callback for incoming messages.
type IRCParams ¶
IRCParams holds parsed IRC connection string components.
func ParseIRCConnectionString ¶
ParseIRCConnectionString parses an IRC connection string into components. Format: nick:password@server:port/channel Example: wgrok-bot:pass@irc.libera.chat:6697/#wgrok
type IncomingMessage ¶
type IncomingMessage struct {
Sender string
Text string
HTML string
RoomID string
RoomType string
MsgID string
Platform string
Cards []interface{}
}
IncomingMessage is the normalized incoming message from any platform.
type IrcConnection ¶
type IrcConnection struct {
// contains filtered or unexported fields
}
IrcConnection manages a persistent TCP/TLS connection to an IRC server.
func NewIrcConnection ¶
func NewIrcConnection(connStr string) (*IrcConnection, error)
NewIrcConnection creates a new IRC connection handler.
func (*IrcConnection) Connect ¶
func (ic *IrcConnection) Connect() error
Connect establishes a TLS connection to the IRC server.
func (*IrcConnection) Disconnect ¶
func (ic *IrcConnection) Disconnect() error
Disconnect closes the IRC connection.
type IrcListener ¶
type IrcListener struct {
// contains filtered or unexported fields
}
IrcListener listens for IRC messages via persistent TCP/TLS connection.
func NewIrcListener ¶
func NewIrcListener(connStr string, logger wmh.Logger) *IrcListener
NewIrcListener creates a new IRC listener.
func (*IrcListener) Connect ¶
func (l *IrcListener) Connect(ctx context.Context) error
Connect establishes the connection to IRC.
func (*IrcListener) Disconnect ¶
func (l *IrcListener) Disconnect(ctx context.Context) error
Disconnect closes the connection to IRC.
func (*IrcListener) OnMessage ¶
func (l *IrcListener) OnMessage(callback MessageCallback)
OnMessage registers a callback for incoming messages.
type MessageCallback ¶
type MessageCallback func(IncomingMessage)
MessageCallback is the callback type for received messages.
type MessageHandler ¶
MessageHandler is the callback type for received messages. It receives the slug, payload, cards, and fromSlug (sender identifier).
type NdjsonLogger ¶
type NdjsonLogger struct {
Module string
}
NdjsonLogger emits NDJSON log lines to stderr.
func (*NdjsonLogger) Debug ¶
func (l *NdjsonLogger) Debug(msg string, _ ...any)
func (*NdjsonLogger) Error ¶
func (l *NdjsonLogger) Error(msg string, _ ...any)
func (*NdjsonLogger) Info ¶
func (l *NdjsonLogger) Info(msg string, _ ...any)
func (*NdjsonLogger) Warn ¶
func (l *NdjsonLogger) Warn(msg string, _ ...any)
type PlatformListener ¶
type PlatformListener interface {
OnMessage(callback MessageCallback)
Connect(ctx context.Context) error
Disconnect(ctx context.Context) error
}
PlatformListener defines the interface for platform-specific listeners.
func CreateListener ¶
func CreateListener(platform, token string, logger wmh.Logger) (PlatformListener, error)
CreateListener creates the right listener for a platform. For IRC, the token parameter should be the connection string (nick:password@server:port/channel).
type ReceiverConfig ¶
type ReceiverConfig struct {
WebexToken string
Slug string
Domains []string
Platform string
Debug bool
EncryptKey []byte // nil if not set; must be 32 bytes for AES-256
}
ReceiverConfig holds configuration for WgrokReceiver.
func ReceiverConfigFromEnv ¶
func ReceiverConfigFromEnv() (*ReceiverConfig, error)
ReceiverConfigFromEnv loads a ReceiverConfig from environment variables.
type SenderConfig ¶
type SenderConfig struct {
WebexToken string
Target string
Slug string
Domains []string
Platform string
Debug bool
EncryptKey []byte // nil if not set; must be 32 bytes for AES-256
}
SenderConfig holds configuration for WgrokSender.
func SenderConfigFromEnv ¶
func SenderConfigFromEnv() (*SenderConfig, error)
SenderConfigFromEnv loads a SenderConfig from environment variables.
type SimpleHTTPClient ¶
type SimpleHTTPClient interface {
PostJSON(url, token string, body interface{}) (map[string]interface{}, error)
}
SimpleHTTPClient interface for dependency injection.
type SlackListener ¶
type SlackListener struct {
// contains filtered or unexported fields
}
SlackListener listens for Slack messages via Socket Mode WebSocket.
func NewSlackListener ¶
func NewSlackListener(token string, logger wmh.Logger) *SlackListener
NewSlackListener creates a new Slack listener.
func (*SlackListener) Connect ¶
func (l *SlackListener) Connect(ctx context.Context) error
Connect establishes the WebSocket connection to Slack Socket Mode.
func (*SlackListener) Disconnect ¶
func (l *SlackListener) Disconnect(ctx context.Context) error
Disconnect closes the connection to Slack.
func (*SlackListener) OnMessage ¶
func (l *SlackListener) OnMessage(callback MessageCallback)
OnMessage registers a callback for incoming messages.
type WebexListener ¶
type WebexListener struct {
// contains filtered or unexported fields
}
WebexListener wraps the webex-message-handler and normalizes messages.
func NewWebexListener ¶
func NewWebexListener(token string, logger wmh.Logger) *WebexListener
NewWebexListener creates a new Webex listener.
func (*WebexListener) Connect ¶
func (l *WebexListener) Connect(ctx context.Context) error
Connect establishes the connection to Webex.
func (*WebexListener) Disconnect ¶
func (l *WebexListener) Disconnect(ctx context.Context) error
Disconnect closes the connection to Webex.
func (*WebexListener) OnMessage ¶
func (l *WebexListener) OnMessage(callback MessageCallback)
OnMessage registers a callback for incoming messages.
type WgrokReceiver ¶
type WgrokReceiver struct {
OnControl ControlHandler
// contains filtered or unexported fields
}
WgrokReceiver listens for response messages, matches slug, invokes handler callback.
func NewReceiver ¶
func NewReceiver(config *ReceiverConfig, handler MessageHandler) *WgrokReceiver
NewReceiver creates a new WgrokReceiver.
func (*WgrokReceiver) FetchAction ¶
func (r *WgrokReceiver) FetchAction(actionID string) (map[string]interface{}, error)
FetchAction fetches an attachment action (card form submission) by ID.
func (*WgrokReceiver) Listen ¶
func (r *WgrokReceiver) Listen(ctx context.Context) error
Listen connects to the configured platform and listens for response messages matching the configured slug. Blocks until ctx is cancelled or Stop is called.
func (*WgrokReceiver) Stop ¶
func (r *WgrokReceiver) Stop(ctx context.Context)
Stop disconnects the receiver.
type WgrokRouterBot ¶
type WgrokRouterBot struct {
// contains filtered or unexported fields
}
WgrokRouterBot listens for messages, validates allowlist, strips prefix, relays back.
func NewRouterBot ¶
func NewRouterBot(config *BotConfig) *WgrokRouterBot
NewRouterBot creates a new WgrokRouterBot.
func (*WgrokRouterBot) Pause ¶
func (b *WgrokRouterBot) Pause() error
Pause sends a pause control message to all Mode C route targets.
func (*WgrokRouterBot) Resume ¶
func (b *WgrokRouterBot) Resume() error
Resume sends a resume control message to all Mode C route targets.
func (*WgrokRouterBot) Run ¶
func (b *WgrokRouterBot) Run(ctx context.Context) error
Run connects to configured platforms and listens for messages. Blocks until ctx is cancelled or Stop is called.
func (*WgrokRouterBot) Stop ¶
func (b *WgrokRouterBot) Stop(ctx context.Context)
Stop disconnects the router bot.
type WgrokSender ¶
type WgrokSender struct {
// contains filtered or unexported fields
}
WgrokSender wraps payloads in the echo protocol and sends via Webex.
func NewSender ¶
func NewSender(config *SenderConfig) *WgrokSender
NewSender creates a new WgrokSender.
func (*WgrokSender) Pause ¶
func (s *WgrokSender) Pause(notify bool) error
Pause pauses message delivery. If notify is true, sends a pause control message to the target.
func (*WgrokSender) Resume ¶
func (s *WgrokSender) Resume(notify bool) error
Resume resumes message delivery. If notify is true, sends a resume control message to the target. Then flushes any buffered messages.
func (*WgrokSender) Send ¶
func (s *WgrokSender) Send(payload string, card interface{}) (map[string]interface{}, error)
Send formats payload as an echo message and sends to the configured target. If card is non-nil, it is attached as an adaptive card. If compress is true, the payload is gzip+base64 encoded before sending.
func (*WgrokSender) SendWithOptions ¶
func (s *WgrokSender) SendWithOptions(payload string, card interface{}, compress bool) (map[string]interface{}, error)
SendWithOptions is like Send but with an explicit compress flag.