Documentation
¶
Overview ¶
Package slackclient wraps the slack-go library with token management, Socket Mode event handling, and a simplified Web API interface.
Index ¶
- func ParseMutedFromAllNotificationsPrefs(raw string) []string
- type ChannelSectionUpserted
- type Client
- func (c *Client) AddReaction(ctx context.Context, channelID, ts, emoji string) error
- func (c *Client) Connect(ctx context.Context) error
- func (c *Client) EditMessage(ctx context.Context, channelID, ts, text string) (string, error)
- func (c *Client) EndDND(ctx context.Context) error
- func (c *Client) EndSnooze(ctx context.Context) (*slack.DNDStatus, error)
- func (c *Client) GetAllPublicChannels(ctx context.Context) ([]slack.Channel, error)
- func (c *Client) GetChannelSections(ctx context.Context) ([]SidebarSection, error)
- func (c *Client) GetChannelSectionsRaw(ctx context.Context) ([]byte, error)
- func (c *Client) GetChannels(ctx context.Context) ([]slack.Channel, error)
- func (c *Client) GetDNDInfo(ctx context.Context, userID string) (*slack.DNDStatus, error)
- func (c *Client) GetHistory(ctx context.Context, channelID string, limit int, oldest string) ([]slack.Message, error)
- func (c *Client) GetHistorySince(ctx context.Context, channelID, oldest string, maxTotal int) (HistorySinceResult, error)
- func (c *Client) GetMutedChannels(ctx context.Context) ([]string, error)
- func (c *Client) GetMutedChannelsRaw(ctx context.Context) ([]byte, error)
- func (c *Client) GetOlderHistory(ctx context.Context, channelID string, limit int, latest string) ([]slack.Message, error)
- func (c *Client) GetPermalink(ctx context.Context, channelID, ts string) (string, error)
- func (c *Client) GetReplies(ctx context.Context, channelID, threadTS string) ([]slack.Message, error)
- func (c *Client) GetUnreadCounts() ([]UnreadInfo, ThreadsAggregate, error)
- func (c *Client) GetUserPresence(ctx context.Context, userID string) (*slack.UserPresence, error)
- func (c *Client) GetUserProfile(userID string) (*slack.User, error)
- func (c *Client) GetUsers(ctx context.Context) ([]slack.User, error)
- func (c *Client) JoinChannel(ctx context.Context, channelID string) error
- func (c *Client) ListCustomEmoji(ctx context.Context) (map[string]string, error)
- func (c *Client) ListThreadSubscriptions(ctx context.Context) ([]ThreadSubscriptionView, error)
- func (c *Client) MarkChannel(ctx context.Context, channelID, ts string) error
- func (c *Client) MarkChannelUnread(ctx context.Context, channelID, ts string) error
- func (c *Client) MarkThread(ctx context.Context, channelID, threadTS, ts string) error
- func (c *Client) MarkThreadUnread(ctx context.Context, channelID, threadTS, ts string) error
- func (c *Client) RemoveMessage(ctx context.Context, channelID, ts string) error
- func (c *Client) RemoveReaction(ctx context.Context, channelID, ts, emoji string) error
- func (c *Client) SendMessage(ctx context.Context, channelID, text string) (string, string, error)
- func (c *Client) SendReply(ctx context.Context, channelID, threadTS, text string) (string, string, error)
- func (c *Client) SendTyping(channelID string) error
- func (c *Client) SetSnooze(ctx context.Context, minutes int) (*slack.DNDStatus, error)
- func (c *Client) SetUserPresence(ctx context.Context, presence string) error
- func (c *Client) StartWebSocket(handler EventHandler) error
- func (c *Client) StopWebSocket() error
- func (c *Client) SubscribePresence(userIDs []string) error
- func (c *Client) TeamID() string
- func (c *Client) UploadFile(ctx context.Context, channelID, threadTS, filename string, r io.Reader, ...) (*slack.FileSummary, error)
- func (c *Client) UserID() string
- func (c *Client) WsDone() <-chan struct{}
- type ConnectionManager
- type EventHandler
- type HistorySinceResult
- type SidebarSection
- type SlackAPI
- type ThreadSubscription
- type ThreadSubscriptionView
- type ThreadsAggregate
- type Token
- type TokenStore
- type UnreadInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ParseMutedFromAllNotificationsPrefs ¶ added in v0.7.2
ParseMutedFromAllNotificationsPrefs decodes the JSON-string value of the `all_notifications_prefs` pref and returns the channel IDs where channels[id].muted == true. The pref's value is itself a JSON-encoded string (Slack quirk), so callers should pass the raw string contents directly. Returns an empty slice on any decode failure — mute is best-effort UI sugar, not safety-critical.
Types ¶
type ChannelSectionUpserted ¶ added in v0.7.0
type ChannelSectionUpserted struct {
ID string
Name string
Type string
Emoji string
Next string
LastUpdate int64
IsRedacted bool
}
ChannelSectionUpserted carries the data from a channel_section_upserted WS event into the EventHandler.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the slack-go library, providing RTM connectivity and a simplified Web API surface for the service layer. Uses browser cookie auth (xoxc token + d cookie).
func NewClient ¶
NewClient creates a new Slack client using browser cookie auth. xoxcToken is the xoxc-... token from the browser. dCookie is the value of the 'd' cookie from slack.com.
func (*Client) AddReaction ¶
AddReaction adds an emoji reaction to a message.
func (*Client) Connect ¶
Connect authenticates with Slack, populates the team/user IDs, and discovers the workspace-specific API base URL from auth.test's response.
On enterprise grid workspaces, every API request must hit the grid-prefixed host (e.g. "https://hackclub.enterprise.slack.com/api/...") rather than the canonical "https://slack.com/api/..." — otherwise the requests are routed to a different team or fail outright. The official browser client learns the right host the same way: the URL field on auth.test's response carries it.
If we own the inner slack-go client (NewClient set httpClient), rebuild it with the discovered API URL via slack.OptionAPIURL so subsequent slack-go calls also target the workspace host. If a test injected a mock api directly, leave it alone.
func (*Client) EditMessage ¶
EditMessage updates an existing message's text. Returns the converted mrkdwn text that was sent (callers may use it for optimistic display, but the message-changed WS echo is the authoritative source of truth for the displayed body).
func (*Client) EndSnooze ¶ added in v0.3.1
EndSnooze ends the current snooze window. Does NOT end admin-scheduled DND.
func (*Client) GetAllPublicChannels ¶
GetAllPublicChannels retrieves all public channels in the workspace via conversations.list, including ones the user is NOT a member of. This is used to populate the channel finder so users can join / switch to public channels they haven't joined yet.
Note: this is significantly slower than GetChannels for large workspaces (potentially thousands of channels). Callers should run it in the background after the joined-channel list is loaded.
func (*Client) GetChannelSections ¶
func (c *Client) GetChannelSections(ctx context.Context) ([]SidebarSection, error)
GetChannelSections calls users.channelSections.list and returns the fully-paginated section list. Loops on the top-level cursor until the server reports no more sections. Per-section channel_ids_page pagination is NOT followed here — see ListSectionChannels.
This endpoint is undocumented; may break if Slack changes the API.
func (*Client) GetChannelSectionsRaw ¶ added in v0.7.0
GetChannelSectionsRaw calls users.channelSections.list with no cursor and returns the raw JSON response body. Diagnostic only (--dump-sections).
func (*Client) GetChannels ¶
GetChannels retrieves conversations the user is a member of (channels, DMs, group DMs), paginating automatically. Uses users.conversations which returns only joined channels — much faster than conversations.list for large workspaces.
func (*Client) GetDNDInfo ¶ added in v0.3.1
GetDNDInfo fetches DND/snooze status for a user.
func (*Client) GetHistory ¶
func (c *Client) GetHistory(ctx context.Context, channelID string, limit int, oldest string) ([]slack.Message, error)
GetHistory retrieves message history for a channel. If oldest is set, returns messages newer than that timestamp.
func (*Client) GetHistorySince ¶ added in v0.7.6
func (c *Client) GetHistorySince(ctx context.Context, channelID, oldest string, maxTotal int) (HistorySinceResult, error)
GetHistorySince fetches all messages newer than `oldest` for the given channel, paginating through next_cursor up to a hard ceiling of maxTotal messages. Slack returns messages newest-first per page; pagination via next_cursor walks toward older pages within the (oldest, latest] window. When maxTotal is hit, the result's Capped field is set to true so callers can decide whether to advance a watermark (don't) or record a gap.
If oldest == "", behaves like a single GetHistory call (no pagination) and returns at most maxTotal messages from the latest page, with Capped reflecting whether HasMore was true. This matches the "first-sync channel: just give me the latest page" pattern.
func (*Client) GetMutedChannels ¶ added in v0.7.2
GetMutedChannels fetches the authenticated user's mute set by reading users.prefs.get and parsing the per-channel notification prefs blob. Returns the IDs of channels the user has muted.
Slack does NOT ship a flat `muted_channels` pref anymore (it used to, and is still documented as such in some places, but live browser-protocol responses no longer include it). Mute state lives inside the JSON-encoded `all_notifications_prefs` string under channels[id].muted=true. After this initial fetch, pref_change WS events for `all_notifications_prefs` keep the set fresh.
users.prefs.get is undocumented but is the same call the official browser client uses; may break if Slack changes the API. Returns an empty slice (not nil) when the user has no muted channels.
func (*Client) GetMutedChannelsRaw ¶ added in v0.7.2
GetMutedChannelsRaw returns the raw users.prefs.get response body. Diagnostic only (--dump-prefs).
func (*Client) GetOlderHistory ¶
func (c *Client) GetOlderHistory(ctx context.Context, channelID string, limit int, latest string) ([]slack.Message, error)
GetOlderHistory retrieves messages older than the given timestamp.
func (*Client) GetPermalink ¶ added in v0.2.0
GetPermalink returns the Slack permalink for a message. For a thread reply, pass the reply's ts; Slack returns a thread-aware URL with thread_ts and cid query parameters.
func (*Client) GetReplies ¶
func (c *Client) GetReplies(ctx context.Context, channelID, threadTS string) ([]slack.Message, error)
GetReplies retrieves all replies in a thread. The first message in the returned slice is the parent message.
func (*Client) GetUnreadCounts ¶
func (c *Client) GetUnreadCounts() ([]UnreadInfo, ThreadsAggregate, error)
GetUnreadCounts fetches unread counts for all channels using Slack's internal client.counts API (available with xoxc browser tokens). Also returns the threads aggregate (HasUnreads / counts) for the workspace; the threads block in client.counts is the only place Slack tells us whether per-thread unreads exist without us having to hit subscriptions.thread.* directly.
func (*Client) GetUserPresence ¶ added in v0.3.1
GetUserPresence fetches a user's current presence ("active" or "away"). Pass the authenticated user's ID to read your own state.
func (*Client) GetUserProfile ¶
GetUserProfile fetches a single user's profile by ID.
func (*Client) JoinChannel ¶
JoinChannel joins a public channel via conversations.join. Returns nil on success. Idempotent: joining a channel you're already in is a no-op on Slack's side and returns no error here.
func (*Client) ListCustomEmoji ¶
ListCustomEmoji fetches the workspace's custom emoji list via Slack's emoji.list API. Returns a map of emoji name -> URL or "alias:targetname". The map is empty if the workspace has no custom emojis.
func (*Client) ListThreadSubscriptions ¶ added in v0.7.6
func (c *Client) ListThreadSubscriptions(ctx context.Context) ([]ThreadSubscriptionView, error)
ListThreadSubscriptions fetches the workspace's full subscribed- threads list via Slack's internal subscriptions.thread.getView endpoint (the same call the official web client makes when bootstrapping its Threads view). Paginates via the `current_ts` form field (set to the previous response's max_ts), terminated by has_more=false. Stops at listThreadSubscriptionsHardCap items.
Items where root_msg.subscribed is false are filtered out — defensive, since the live endpoint hasn't been observed returning them.
Returns (nil, err) on network failure or ok=false JSON. The caller (the reconnect backfill phase) treats any error as "subscriptions unavailable" and surfaces the UI banner.
func (*Client) MarkChannel ¶
MarkChannel marks a channel as read up to the given timestamp.
func (*Client) MarkChannelUnread ¶ added in v0.6.4
MarkChannelUnread rolls the channel's read watermark backward to ts, effectively making the message at ts and every newer message in the channel unread again. Pass ts == "" to mark the entire channel unread (Slack's "0" sentinel).
func (*Client) MarkThread ¶ added in v0.6.0
MarkThread marks a thread as read up to the given timestamp using Slack's undocumented subscriptions.thread.mark endpoint (the same call the official web client makes when you view a thread). channelID is the parent channel, threadTS is the parent message ts, and ts is the latest reply ts the user has now seen (use threadTS itself when there are no replies). Best-effort: the endpoint is undocumented and may break if Slack changes its API.
func (*Client) MarkThreadUnread ¶ added in v0.6.4
MarkThreadUnread marks a thread as unread starting at ts using Slack's subscriptions.thread.mark endpoint with read=0. Mirrors MarkThread but flips the read flag. channelID is the parent channel, threadTS is the parent message ts, and ts is the reply that should become the new "first unread" boundary (use threadTS to mark the entire thread unread when there are no replies). Best-effort.
func (*Client) RemoveMessage ¶
RemoveMessage deletes a message from the channel.
func (*Client) RemoveReaction ¶
RemoveReaction removes an emoji reaction from a message.
func (*Client) SendMessage ¶
SendMessage posts a new message to the specified channel. Returns the timestamp and the converted mrkdwn text actually sent (callers use this for optimistic display so it matches what other Slack clients will render).
func (*Client) SendReply ¶
func (c *Client) SendReply(ctx context.Context, channelID, threadTS, text string) (string, string, error)
SendReply posts a threaded reply to the specified message. Returns the timestamp and the converted mrkdwn text actually sent.
func (*Client) SendTyping ¶
SendTyping sends a typing indicator to the given channel via WebSocket.
func (*Client) SetUserPresence ¶ added in v0.3.1
SetUserPresence sets the authenticated user's presence. Accepts "auto" (let Slack determine activity) or "away" (force away). Note the write vocabulary differs from the read side — GetUserPresence and the presence_change WebSocket event return "active" or "away".
func (*Client) StartWebSocket ¶
func (c *Client) StartWebSocket(handler EventHandler) error
StartWebSocket connects to Slack's internal WebSocket using the xoxc token and d cookie, matching the protocol used by the browser client. Events are dispatched to the provided handler in a goroutine. Call this after Connect.
func (*Client) StopWebSocket ¶
StopWebSocket disconnects the WebSocket connection.
func (*Client) SubscribePresence ¶ added in v0.3.1
SubscribePresence asks Slack to deliver presence_change events for the given user IDs. Sent over the existing WebSocket connection. Slack only emits presence_change for users you've explicitly subscribed to (the authenticated user is typically auto-subscribed at connect, but the explicit subscription is reliable across servers).
func (*Client) TeamID ¶
TeamID returns the authenticated workspace's team ID. Empty before Connect is called.
func (*Client) UploadFile ¶ added in v0.4.0
func (c *Client) UploadFile( ctx context.Context, channelID, threadTS, filename string, r io.Reader, size int64, caption string, ) (*slack.FileSummary, error)
UploadFile uploads a single file to a channel (and optional thread) using Slack's V2 external-upload flow. The slack-go library's UploadFileContext (named for the underlying file.upload.v2 API) handles the three internal steps: getUploadURLExternal -> PUT -> completeUploadExternal.
caption, when non-empty, is attached as the file's initial_comment. For multi-file batches the caller should set caption on the LAST file only (Slack groups files completed in one share into one message; sequential single-file uploads can't be grouped).
size is int64 (matching os.FileInfo.Size()) and is narrowed to int for slack-go. Callers must enforce a reasonable upper bound; this wrapper does not.
type ConnectionManager ¶
type ConnectionManager struct {
// contains filtered or unexported fields
}
ConnectionManager manages the WebSocket connection lifecycle with automatic reconnection using exponential backoff.
func NewConnectionManager ¶
func NewConnectionManager(client *Client, handler EventHandler) *ConnectionManager
NewConnectionManager creates a new connection manager.
func (*ConnectionManager) Run ¶
func (cm *ConnectionManager) Run(ctx context.Context)
Run starts the connection loop. It connects, waits for disconnect, and reconnects with exponential backoff. Blocks until ctx is cancelled.
func (*ConnectionManager) Stop ¶
func (cm *ConnectionManager) Stop()
Stop cancels the connection loop and closes the WebSocket.
type EventHandler ¶
type EventHandler interface {
// OnMessage delivers a new or edited message. subtype mirrors
// Slack's `subtype` field; "" for normal messages, "bot_message"
// for bot posts, "thread_broadcast" for thread replies that the
// author also sent to the main channel. files carries any file
// attachments on the message (empty for plain text messages).
OnMessage(channelID, userID, ts, text, threadTS, subtype string, edited bool, files []slack.File, blocks slack.Blocks, attachments []slack.Attachment)
OnMessageDeleted(channelID, ts string)
OnReactionAdded(channelID, ts, userID, emoji string)
OnReactionRemoved(channelID, ts, userID, emoji string)
OnPresenceChange(userID, presence string)
OnUserTyping(channelID, userID string)
OnConnect()
OnDisconnect()
OnSelfPresenceChange(presence string)
OnDNDChange(enabled bool, endUnix int64)
// OnChannelMarked is delivered when Slack pushes a channel_marked /
// im_marked / group_marked / mpim_marked event (read state changed
// in another client, or via slk's own MarkChannel/MarkChannelUnread
// echoing back). ts is the new last_read watermark; unreadCount is
// the canonical workspace-side unread count for the channel (use to
// drive the sidebar badge).
OnChannelMarked(channelID, ts string, unreadCount int)
// OnThreadMarked is delivered when Slack pushes a thread_marked
// event. read indicates whether the thread is now read (true) or
// unread (false). ts is the new boundary within the thread.
OnThreadMarked(channelID, threadTS, ts string, read bool)
// OnThreadSubscriptionChanged is delivered for thread_subscribed and
// thread_unsubscribed WS events. active=true on subscribe,
// active=false on unsubscribe. lastRead is the per-thread last_read ts
// the server reports — pass-through to thread_subscriptions.last_read.
// The payload shape is identical to thread_marked.subscription, so
// implementations can share state-update logic with OnThreadMarked
// (this handler is the persistence-only path; OnThreadMarked also
// drives the UI's read-state side effects).
OnThreadSubscriptionChanged(channelID, threadTS, lastRead string, active bool)
// OnConversationOpened is delivered when a new or previously-closed
// conversation becomes visible to the user mid-session: mpim_open,
// im_created, group_joined, or channel_joined. The full slack.Channel
// payload is forwarded so the receiver can construct a sidebar item
// without an extra conversations.info round-trip.
OnConversationOpened(channel slack.Channel)
// OnChannelSectionUpserted is called for channel_section_upserted
// WS events: section create, rename, reorder, or emoji change.
OnChannelSectionUpserted(ev ChannelSectionUpserted)
// OnChannelSectionDeleted is called for channel_section_deleted.
OnChannelSectionDeleted(sectionID string)
// OnChannelSectionChannelsUpserted is called for
// channel_sections_channels_upserted: one or more channels added
// to the named section. A channel previously in another section
// is implicitly moved.
OnChannelSectionChannelsUpserted(sectionID string, channelIDs []string)
// OnChannelSectionChannelsRemoved is called for
// channel_sections_channels_removed.
OnChannelSectionChannelsRemoved(sectionID string, channelIDs []string)
// OnPrefChange is called for pref_change WS events. Slack ships these
// for every user-pref mutation (mute/unmute, highlight words,
// notifications, etc.); receivers are expected to dispatch on `name`
// and ignore prefs they don't care about. value is the new pref
// value as a string — for list-shaped prefs like muted_channels,
// Slack ships the full updated list, comma-separated.
OnPrefChange(name, value string)
}
EventHandler processes real-time events from Slack.
type HistorySinceResult ¶ added in v0.7.11
GetHistorySince fetches all messages newer than `oldest` in the channel, paginating forward via response_metadata.next_cursor. Stops when has_more is false or when the cumulative message count reaches maxTotal (a hard cap that protects against runaway backfills after very long disconnects in busy channels).
Returns messages in the order Slack delivered them (newest-first per page, oldest page first since pagination walks forward through time). Callers that need oldest-first order should reverse the slice.
HistorySinceResult bundles the messages fetched by GetHistorySince with a "did we get everything?" flag. Capped == true means the caller hit maxTotal before the API ran out of pages, so there are still older-than-cap messages between (oldest, latest-fetched-ts) the caller didn't get. Callers that advance a sync watermark MUST gate the advance on Capped == false.
type SidebarSection ¶ added in v0.7.0
type SidebarSection struct {
ID string
Name string
Type string // standard | channels | direct_messages | recent_apps | stars | slack_connect | salesforce_records | agents
Emoji string
Next string // next_channel_section_id; "" = tail
LastUpdate int64 // unix seconds
IsRedacted bool
// ChannelIDs is the membership of this section. Populated from
// channel_ids_page on REST decode (first page only); follow-up
// pagination calls or WS deltas extend it.
ChannelIDs []string
// ChannelsCount is total membership reported by the server,
// even when ChannelIDs holds only the first page. Cursor is
// non-empty when more pages remain.
ChannelsCount int
ChannelsCursor string
}
SidebarSection represents one entry in the user's Slack sidebar section list. Both the REST endpoint (users.channelSections.list) and the WebSocket events use this model after normalization; REST and WS use different field names for the same data, so the decoders translate into this canonical shape.
func (*SidebarSection) UnmarshalJSON ¶ added in v0.7.0
func (s *SidebarSection) UnmarshalJSON(data []byte) error
UnmarshalJSON for SidebarSection accepts the REST envelope and normalizes into the canonical struct.
type SlackAPI ¶
type SlackAPI interface {
GetConversations(params *slack.GetConversationsParameters) ([]slack.Channel, string, error)
GetConversationsForUser(params *slack.GetConversationsForUserParameters) ([]slack.Channel, string, error)
GetConversationHistory(params *slack.GetConversationHistoryParameters) (*slack.GetConversationHistoryResponse, error)
GetConversationReplies(params *slack.GetConversationRepliesParameters) ([]slack.Message, bool, string, error)
GetUsersContext(ctx context.Context, options ...slack.GetUsersOption) ([]slack.User, error)
GetUserInfo(user string) (*slack.User, error)
GetEmoji() (map[string]string, error)
PostMessage(channelID string, options ...slack.MsgOption) (string, string, error)
UpdateMessage(channelID, timestamp string, options ...slack.MsgOption) (string, string, string, error)
DeleteMessage(channelID, timestamp string) (string, string, error)
AddReaction(name string, item slack.ItemRef) error
RemoveReaction(name string, item slack.ItemRef) error
GetPermalinkContext(ctx context.Context, params *slack.PermalinkParameters) (string, error)
AuthTest() (*slack.AuthTestResponse, error)
JoinConversation(channelID string) (*slack.Channel, string, []string, error)
SetUserPresenceContext(ctx context.Context, presence string) error
GetUserPresenceContext(ctx context.Context, user string) (*slack.UserPresence, error)
SetSnoozeContext(ctx context.Context, minutes int) (*slack.DNDStatus, error)
EndSnoozeContext(ctx context.Context) (*slack.DNDStatus, error)
EndDNDContext(ctx context.Context) error
GetDNDInfoContext(ctx context.Context, user *string, options ...slack.ParamOption) (*slack.DNDStatus, error)
UploadFileContext(ctx context.Context, params slack.UploadFileParameters) (*slack.FileSummary, error)
}
SlackAPI defines the subset of the Slack API we use. This interface enables mocking in tests.
type ThreadSubscription ¶ added in v0.7.6
ThreadSubscription is the slk-side projection of one subscribed thread returned by subscriptions.thread.getView. The five fields here map cleanly onto cache.ThreadSubscription. The caller in cmd/slk/reconnect_backfill.go does the adapter cast.
type ThreadSubscriptionView ¶ added in v0.7.6
type ThreadSubscriptionView struct {
Subscription ThreadSubscription
RootMessage slack.Message
}
ThreadSubscriptionView is one item from subscriptions.thread.getView. It carries both the subscription-state projection (Subscription) and the full parent message Slack ships inside root_msg (RootMessage). The subscription-phase backfiller uses Subscription to upsert the thread_subscriptions row and RootMessage to upsert the parent into the messages cache, eliminating the need for a follow-up conversations.replies fetch when the parent isn't already cached.
type ThreadsAggregate ¶ added in v0.6.0
ThreadsAggregate captures Slack's server-side notion of whether the user has any unread thread activity at all, plus the mention/unread counts. The local SQLite cache has no per-thread read state, so the threads-list "Unread" flag is computed by a heuristic that can produce false positives (e.g., a thread reply was read in another Slack client but the parent channel's last_read_ts predates it). HasUnreads from this struct is the authoritative signal that lets us suppress those false positives on startup.
type Token ¶
type Token struct {
AccessToken string `json:"access_token"` // xoxc-... token
Cookie string `json:"cookie"` // d cookie value
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
}
Token holds browser session credentials for a single Slack workspace.
type TokenStore ¶
type TokenStore struct {
// contains filtered or unexported fields
}
TokenStore persists Slack tokens as JSON files in a directory, one file per workspace ({teamID}.json).
func NewTokenStore ¶
func NewTokenStore(dir string) *TokenStore
NewTokenStore creates a TokenStore that reads/writes to the given directory.
func (*TokenStore) Delete ¶
func (s *TokenStore) Delete(teamID string) error
Delete removes a token file for the given team ID. Returns nil if the file does not exist.
func (*TokenStore) List ¶
func (s *TokenStore) List() ([]Token, error)
List returns all tokens stored on disk. Corrupted token files are silently skipped.
func (*TokenStore) Load ¶
func (s *TokenStore) Load(teamID string) (Token, error)
Load reads a token for the given team ID from disk. Returns an error if the token file does not exist.
func (*TokenStore) Save ¶
func (s *TokenStore) Save(token Token) error
Save writes a token to disk, creating the directory if needed. File permissions are restricted to owner-only (0600).
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mrkdwn translates CommonMark-style markdown in the compose box into Slack's wire formats: a mrkdwn fallback string (for the chat.postMessage `text` field and notifications) and a rich_text block (for the `blocks` array).
|
Package mrkdwn translates CommonMark-style markdown in the compose box into Slack's wire formats: a mrkdwn fallback string (for the chat.postMessage `text` field and notifications) and a rich_text block (for the `blocks` array). |