mattermost

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package mattermost is a thin, hand-written client for the Mattermost API v4 (Time = Mattermost v4). It deliberately avoids the official MM SDK to keep the dependency surface small and to tolerate Time's extensions/divergences.

The package is a leaf: it depends only on the stdlib and gorilla/websocket and MUST NOT import internal/bot, internal/config, or internal/storage. The transport adapter that maps wire types onto the bot's neutral envelope lives in internal/bot/transport_mattermost.go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Channel

type Channel struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	DisplayName string `json:"display_name"`
	Type        string `json:"type"`
}

Channel is the subset of /channels/{id} we read — the display name used to label a channel scope in the dashboard. Type is "D"/"O"/"P"/"G".

type Client

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

Client talks to the Mattermost v4 REST API over a dedicated, proxy-aware HTTP client. The same proxy is reused for the WebSocket dialer (ws.go).

func NewClient

func NewClient(ctx context.Context, cfg Config, logger *slog.Logger) (*Client, error)

NewClient builds the client and bootstraps the bot identity and message-size limit from the live server. It fails fast if /users/me is unreachable.

func (*Client) BotID

func (c *Client) BotID() string

BotID returns the bot account id (used to ignore the bot's own posts).

func (*Client) CreatePost

func (c *Client) CreatePost(ctx context.Context, r CreatePostReq) (*Post, error)

CreatePost sends a post and returns the created post (with its server id).

func (*Client) Events

func (c *Client) Events() <-chan PostedEvent

Events returns the channel of incoming "posted" events produced by Run.

func (*Client) GetChannel

func (c *Client) GetChannel(ctx context.Context, channelID string) (*Channel, error)

GetChannel fetches a channel by id, caching the result with a TTL — channel names change rarely, but the entry still expires so a rename is eventually reflected (and the cache cannot grow stale forever). Mirrors GetUser; used to label a channel scope by its display name.

func (*Client) GetFile

func (c *Client) GetFile(ctx context.Context, fileID string) ([]byte, error)

GetFile downloads the raw bytes of an attached file.

func (*Client) GetFileInfo

func (c *Client) GetFileInfo(ctx context.Context, fileID string) (*FileInfo, error)

GetFileInfo fetches metadata for an attached file (name, mime, size, …).

func (*Client) GetUser

func (c *Client) GetUser(ctx context.Context, userID string) (*User, error)

GetUser fetches a user's profile by id, caching the result with a TTL — the WS ingestion looks profiles up once per inbound post, but auth_service (the SSO access anchor) flips at migration time, so the cache MUST expire. On a stale or missing entry the profile is re-read. Concurrency-safe (the parallel tool path never calls this, but the cache is guarded anyway).

func (*Client) InvalidateUser

func (c *Client) InvalidateUser(userID string)

InvalidateUser drops the cached profile for userID so the next GetUser re-reads it from the server. The bot calls this when it denies a sender: an account that has just migrated to SSO then recovers on its very next message instead of waiting out the cache TTL.

func (*Client) MaxPostSize

func (c *Client) MaxPostSize() int

MaxPostSize returns the server's per-post size limit (read at startup).

func (*Client) Run

func (c *Client) Run(ctx context.Context)

Run connects, authenticates, and pumps "posted" events onto Events() until the context is cancelled. The WebSocket is live-only (no replay): on any drop it reconnects with exponential backoff. It closes the events channel exactly once, when ctx is done, so the ingestion consumer can range over it and exit cleanly.

func (*Client) SendTyping

func (c *Client) SendTyping(ctx context.Context, channelID string) error

SendTyping posts a best-effort typing indicator to a channel.

func (*Client) SetReaction

func (c *Client) SetReaction(ctx context.Context, postID, emojiName string) error

SetReaction adds an emoji reaction (by shortcode name) to a post.

func (*Client) UploadFile

func (c *Client) UploadFile(ctx context.Context, channelID, filename, mimeType string, data []byte) (string, error)

UploadFile uploads one file to a channel via POST /api/v4/files (multipart), returning the new file id to attach to a post. The MIME type is set on the form part when known so the server records the right content type.

type Config

type Config struct {
	ServerURL string // e.g. https://time.example.com
	BotToken  string // bot account token; sent as "Authorization: Bearer <token>"
	ProxyURL  string // explicit per-client proxy (HTTP proxy); "" = direct
	// ProfileCacheTTL bounds how long a cached user/channel profile is trusted
	// before it is re-read from the server. It MUST be finite: auth_service (the
	// SSO access anchor) flips at migration time, and an unbounded cache would
	// keep serving the pre-migration value for the life of the process. 0 selects
	// defaultProfileCacheTTL.
	ProfileCacheTTL time.Duration
}

Config holds the connection settings for a Mattermost/Time server.

type CreatePostReq

type CreatePostReq struct {
	ChannelID      string
	Message        string
	RootID         string
	FileIDs        []string
	IdempotencyKey string
}

CreatePostReq is the caller-facing post-creation request.

type Embed

type Embed struct {
	Type string `json:"type"`
	Data struct {
		PostID string `json:"post_id"`
		Post   struct {
			UserID string `json:"user_id"`
		} `json:"post"`
	} `json:"data"`
}

Embed is a post embed. For type=="quote", Data.Post is the quoted message.

type FileInfo

type FileInfo struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Extension string `json:"extension"`
	Size      int64  `json:"size"`
	MimeType  string `json:"mime_type"`
	Width     int    `json:"width"`
	Height    int    `json:"height"`
}

FileInfo is the subset of a Mattermost FileInfo we use for inbound attachments.

type Post

type Post struct {
	ID        string       `json:"id"`
	UserID    string       `json:"user_id"`
	ChannelID string       `json:"channel_id"`
	RootID    string       `json:"root_id"`
	Message   string       `json:"message"`
	Type      string       `json:"type"` // "" for user posts; non-empty for system posts
	CreateAt  int64        `json:"create_at"`
	FileIDs   []string     `json:"file_ids,omitempty"` // attached file ids
	Metadata  PostMetadata `json:"metadata,omitempty"` // carries embedded FileInfo for attachments
}

Post is the subset of a Mattermost post we read/write.

func (Post) QuotedAuthorID

func (p Post) QuotedAuthorID() string

QuotedAuthorID returns the author id of the first quoted post (reply-to), or "" if the post does not quote/reply to another message.

type PostMetadata

type PostMetadata struct {
	Files  []FileInfo `json:"files,omitempty"`
	Embeds []Embed    `json:"embeds,omitempty"`
}

PostMetadata carries enriched post data: attached file info and embeds. A reply that quotes another message carries a "quote" embed whose nested post identifies the quoted author — the signal for reply-to-bot gating (verified present in the live WS event, not just REST).

type PostedEvent

type PostedEvent struct {
	Post        Post
	ChannelType string   // "D" (DM) | "O" (open) | "P" (private) | "G" (group)
	Mentions    []string // user ids mentioned in the post (nil/empty if none)
}

PostedEvent is a parsed "posted" WebSocket event: the inner post plus the channel type (D/O/P/G) the event carried alongside it.

type User

type User struct {
	ID          string `json:"id"`
	Username    string `json:"username"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	Nickname    string `json:"nickname"`
	IsBot       bool   `json:"is_bot"`
	AuthService string `json:"auth_service"`
	AuthData    string `json:"auth_data"`
	Email       string `json:"email"`
}

User is the subset of /users/{id} we read (identity + display name + the federated-identity fields used for principal resolution).

AuthService is the trust anchor: "" means a local Mattermost account (never linked to a principal — isolation), a non-empty value (e.g. "saml") means an externally-authenticated account. AuthData carries the external subject (the AD login for SAML); it is the join key, lowercased. Email is stored as a principal attribute only and is NEVER used to link identities (a local account can self-claim an email in a trusted-looking domain).

Jump to

Keyboard shortcuts

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