Documentation
¶
Overview ¶
Package slack talks to the Slack Web API for the PR notifier: posting, updating, and deleting messages, adding emoji reactions, and composing the blocks of the notification.
Index ¶
- type APIError
- type Block
- type Button
- type ChannelInfo
- type Client
- func (c *Client) AddReaction(ctx context.Context, channel, ts, name string) error
- func (c *Client) AuthTest(ctx context.Context) (userID string, scopes []string, err error)
- func (c *Client) ConversationsInfo(ctx context.Context, channel string) (ChannelInfo, error)
- func (c *Client) DeleteMessage(ctx context.Context, channel, ts string) error
- func (c *Client) GetReactions(ctx context.Context, channel, ts string) ([]Reaction, error)
- func (c *Client) PostMessage(ctx context.Context, channel string, msg Message) (string, error)
- func (c *Client) PostReply(ctx context.Context, channel, threadTS string, msg Message) (string, error)
- func (c *Client) UpdateMessage(ctx context.Context, channel, ts string, msg Message) error
- func (c *Client) UpdateMessageRawBlocks(ctx context.Context, channel, ts string, blocks []json.RawMessage, ...) error
- type Composer
- func (c *Composer) BotMessage(pr PRDetails, mentions []string, bot string, security bool) Message
- func (c *Composer) NewMessage(pr PRDetails, mentions []string, newPREmoji string) Message
- func (c *Composer) ReviewedByMarker(userIDs []string) Block
- func (c *Composer) ReviewingMarker(userID string, since time.Time) Block
- func (c *Composer) StuckDigestList(prs []StuckPR) Message
- func (c *Composer) StuckDigestParent(mentions []string, count int) Message
- func (c *Composer) UpdatedMessage(pr PRDetails, merged bool, emoji string) Message
- type Message
- type Option
- type PRDetails
- type Reaction
- type StuckPR
- type TextObject
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Block ¶
type Block struct {
Type string `json:"type"`
Text *TextObject `json:"text,omitempty"`
Elements []TextObject `json:"elements,omitempty"`
Buttons []Button `json:"-"`
}
Block is the narrow subset of Block Kit the notifier emits: a "section" (Text set), a "context" line (Elements set), or an "actions" row (Buttons set). Exactly one shape is populated per block. Section and context marshal through the struct tags below; an "actions" block marshals its Buttons as button elements via MarshalJSON.
func (Block) MarshalJSON ¶
MarshalJSON keeps section/context blocks byte-for-byte as their struct tags would render, and emits an "actions" block as {"type":"actions","elements": [{"type":"button",...}]} — Block Kit puts buttons under elements, but with a different element shape than a context line, so the two can't share the field.
type Button ¶
Button is an interactive Block Kit button. Text is rendered into a plain_text object; ActionID identifies the button to the interactions endpoint; Value is the opaque payload it carries back on click; Style is Slack's button style ("primary"/"danger", empty for default). URL, when set, makes Slack open the link in the clicker's browser in addition to delivering the interaction — so the click both records the review and sends the reviewer to the PR page.
type ChannelInfo ¶
ChannelInfo is the subset of conversations.info we need for validation.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a thin Slack Web API client covering only the endpoints needed by the notifier. It is safe for concurrent use.
func NewClient ¶
NewClient builds a Client. The httpClient is used as-is; callers should configure timeouts on it.
func (*Client) AddReaction ¶
AddReaction adds a reaction emoji to a message. "already_reacted" is treated as success — it means the reaction we wanted is already there.
func (*Client) AuthTest ¶
AuthTest returns the bot's user_id and the OAuth scopes Slack reports as granted to the token. Scopes are read from the X-OAuth-Scopes response header (comma-separated) and used by validation to verify required scopes are present.
func (*Client) ConversationsInfo ¶
ConversationsInfo returns metadata about a channel, including whether the bot is a member. The channel argument must be a Slack channel ID.
func (*Client) DeleteMessage ¶
DeleteMessage removes an existing message by ts.
func (*Client) GetReactions ¶
GetReactions returns the reactions attached to a message, or an empty slice if none are present.
func (*Client) PostMessage ¶
PostMessage posts a new message to channel and returns its ts. The Block Kit blocks render in-channel; msg.Fallback is sent as the top-level text Slack uses for the push preview and screen readers.
func (*Client) PostReply ¶
func (c *Client) PostReply(ctx context.Context, channel, threadTS string, msg Message) (string, error)
PostReply posts a message as a reply in the thread rooted at threadTS and returns its ts. It is PostMessage plus a thread_ts, kept separate so the webhook path stays a plain top-level post.
func (*Client) UpdateMessage ¶
UpdateMessage edits an existing message by ts, replacing both its blocks and the top-level text fallback.
func (*Client) UpdateMessageRawBlocks ¶
func (c *Client) UpdateMessageRawBlocks(ctx context.Context, channel, ts string, blocks []json.RawMessage, fallback string) error
UpdateMessageRawBlocks edits a message in place, sending blocks verbatim. Callers pass the message's existing blocks (as echoed back by Slack in the interaction payload) plus any additions, so the original rendering is preserved without re-composing it. fallback is the top-level text.
type Composer ¶
type Composer struct {
// contains filtered or unexported fields
}
Composer renders Slack-formatted notification messages.
func NewComposer ¶
NewComposer returns a Composer that prefixes new-PR messages with the given reaction-style emoji name (without colons).
func (*Composer) BotMessage ¶
BotMessage renders the compact notification for a PR opened by a dependency bot. bot is the lowercase bot name ("dependabot" / "renovate"). When security is true it uses the rotating-light advisory template; otherwise the package routine-bump template. It stays deliberately compact — a single section, no context line — so bot bumps read as a one-liner. Mentions follow the same empty-list rule as NewMessage; the PR author is omitted because the bot name carries it.
func (*Composer) NewMessage ¶
NewMessage renders the initial notification for a PR: a headline section with the new-PR emoji, any mentions, and the linked title, plus a muted context line carrying repo, author, and the localized open time. Mentions stay in the section because Slack only reliably notifies on a mention in a section/ top-level text — a context block renders the mention as gray text but does not ping. When the mentions list is empty the prefix is omitted entirely so the message has no stranded ", ". newPREmoji is the per-repo reaction emoji name (without colons). If empty, falls back to the composer's default emoji.
func (*Composer) ReviewedByMarker ¶
ReviewedByMarker renders the muted "reviewed by <@U…>, <@U…>" context line appended to a closed/merged PR message, listing everyone who reviewed it. The caller passes a non-empty, deduped list of Slack user IDs.
func (*Composer) ReviewingMarker ¶
ReviewingMarker renders the small context line appended to a PR message when a reviewer starts: ":eye: <@U…> reviewing · since <localized time>". Multiple markers accumulate on a message as more people review the same PR.
func (*Composer) StuckDigestList ¶
StuckDigestList renders the thread reply for a channel's stuck-PR digest: one line per stuck PR, with no headline (mentions and count live on the parent). The caller must pass a non-empty prs slice; an empty channel is skipped upstream.
A busy channel can list more PRs than fit in one Block Kit section (Slack caps section text at 3000 chars), so the lines are packed into successive section blocks, each kept under maxSectionChars.
func (*Composer) StuckDigestParent ¶
StuckDigestParent renders the static parent of a channel's stuck-PR digest: a headline carrying the channel's mentions (so the post actually notifies) and the PR count. The PR list itself is posted as a thread reply via StuckDigestList — keeping the channel feed to one quiet line per channel. Mentions follow the same empty-list rule as NewMessage.
func (*Composer) UpdatedMessage ¶
UpdatedMessage renders the closed-PR decoration. Block Kit cannot wrap the whole prior message string the way the legacy plain-text format did, so the message is rebuilt from PR details: the title is struck through inside the section, the leading emoji is swapped to the merged/closed reaction emoji, and a [Merged]/[Closed] label is prepended. The context line is preserved.
type Message ¶
Message is a composed Slack message: the Block Kit blocks rendered in the channel plus a plain-text Fallback. Slack uses Fallback for the mobile push preview and for screen readers — it does not read interior block text for either — so every Message carries one.
type Option ¶
type Option func(*Client)
Option configures Client construction.
func WithBaseURL ¶
WithBaseURL overrides the Slack API base URL — used in tests against an httptest.Server. The URL must NOT have a trailing slash.
type PRDetails ¶
type PRDetails struct {
Repository string
Number int
Title string
URL string
Author string
// CreatedAt is the PR's open time, rendered as a localized date token in
// the context line. The zero value omits the "opened …" clause.
CreatedAt time.Time
}
PRDetails is the subset of PR information the Composer needs to render a notification. It is detached from any HTTP payload type so the composer stays a pure function of its inputs.
type Reaction ¶
type Reaction struct {
Name string `json:"name"`
Count int `json:"count"`
Users []string `json:"users"`
}
Reaction is one entry from reactions.get.
type StuckPR ¶
StuckPR is one entry in a stuck-PR digest: a PR that has seen no activity since before today. The PR title is intentionally absent — the store does not keep it — so the digest links by repository and number.
type TextObject ¶
TextObject is a Block Kit text object. Sections/contexts emit mrkdwn; button labels emit plain_text.