Documentation
¶
Overview ¶
Package github is the GitHub adapter for botbooter. It receives issue and PR comments as issue_comment webhook events over an inbound HTTP server and replies by creating issue comments through the GitHub REST API (go-github). It implements core.Adapter. Optional Config callbacks (OnPullRequest, OnPush) additionally route pull_request and push deliveries on the same webhook endpoint; unset, those deliveries are acked and dropped.
Like the WhatsApp and Teams adapters, Connect binds a listener and serves until the run context is canceled; Disconnect shuts it down and drains in-flight dispatch. Bind a local Addr, put a TLS-terminating proxy in front, and register the public HTTPS URL as the repository or App webhook URL (content type application/json, events: issue_comment plus pull_request and/or push when the matching callback is set, with a secret).
Implementation split (mirrors Teams): github.go (config, auth wiring, accessors), server.go (webhook lifecycle), send.go (replies), message.go (payload mapping), reactions.go (opt-in polled reaction ingress — GitHub sends no webhook for reactions).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrAmbiguousAuth = errors.New("github: configure either Token or AppID/InstallationID/PrivateKey, not both")
ErrAmbiguousAuth is returned by New when both PAT and App auth are configured.
var ErrBadChannelID = errors.New(`github: channel ID must be "owner/repo#number"`)
ErrBadChannelID is returned by Send when channelID is not "owner/repo#number".
var ErrBadReactionConfig = errors.New("github: invalid reaction polling config")
ErrBadReactionConfig is returned by New when a reaction-polling Config field is malformed: a ReactionPollRepos entry that is not "owner/name" or the wildcard "owner/*", or a negative ReactionPollInterval.
var ErrMissingConfig = errors.New("github: missing required config field")
ErrMissingConfig is returned by New when a required Config field is empty.
Functions ¶
func Addr ¶
Addr returns the address the bot's webhook listener is bound to (host:port), or "" if b is not a GitHub bot or is not currently connected. It lets a caller that passed cfg.Addr ":0" discover the OS-assigned port.
Types ¶
type Config ¶
type Config struct {
// Token is a personal access token (classic or fine-grained) for PAT mode.
// The bot posts comments as the token's user.
Token string
// AppID is the GitHub App ID for App mode.
AppID int64
// InstallationID is the App installation to act as; it is also visible in
// webhook payloads and on the installation settings page.
InstallationID int64
// PrivateKey is the App's RSA private key, PEM-encoded.
PrivateKey []byte
// WebhookSecret verifies the X-Hub-Signature-256 HMAC on inbound webhook
// requests. Required: without it the endpoint would accept spoofed payloads.
WebhookSecret string
// Addr is the local TCP address the webhook server binds, e.g. ":8080". A
// bare port ("8080") is accepted as shorthand for ":8080".
Addr string
// Path is the webhook route; it defaults to /webhook.
Path string
// OnPullRequest, when set, receives pull_request webhook deliveries whose
// action is "opened", "reopened" or "synchronize" — the deliveries that
// create or change a PR's reviewable content. Other actions, and PRs
// authored by any bot or by this bot's own account, are acked and dropped,
// mirroring the comment path's reply-loop filter. The callback runs on a
// dispatch goroutine covered by Disconnect's drain (same contract as
// message dispatch), so it should hand long work off and return promptly.
// Nil (the default) keeps the previous behavior: pull_request deliveries
// are acked and dropped. The webhook must also be subscribed to the
// pull_request event, or GitHub never delivers one.
OnPullRequest func(ctx context.Context, event *gogithub.PullRequestEvent)
// OnPush, when set, receives push webhook deliveries, unfiltered — ref
// filtering (e.g. default branch only) is the callback's job. Same
// goroutine and drain contract as OnPullRequest. Nil (the default) acks
// and drops; the webhook must also be subscribed to the push event.
OnPush func(ctx context.Context, event *gogithub.PushEvent)
// HTTPClient is the base client for outbound GitHub API calls; a default
// client with a 30s timeout is used when nil. In App mode only its
// Transport (http.DefaultTransport when nil) and Timeout are used — the
// Transport becomes the inner transport of the ghinstallation
// token-refreshing transport, and other fields (Jar, CheckRedirect) are
// ignored.
HTTPClient *http.Client
// ReactionPollRepos lists repositories whose newest issue comments are
// polled for emoji reactions, because GitHub sends no webhook for them.
// An entry is either explicit ("owner/name") or a wildcard ("owner/*"):
// every repository of that owner the credentials can see, minus archived
// ones — the authenticated user's own repos (private included) in PAT
// mode when the owner is the bot itself, that owner's public repos
// otherwise, and the installation's repos in App mode. Wildcards are
// re-resolved every ~10 poll cycles, so new repositories are picked up
// without a restart. Empty (the default) disables polling and OnReaction
// never fires. The poller also requires an OnReaction handler registered
// before the bot connects — with repos listed but no handler, no poller
// starts and no API requests are spent. Coverage is deliberately partial
// — only reactions on each repo's newest comments are seen — and each
// polled repo costs at least one API request per poll cycle (worst case
// ~11, when every window comment's reaction count changed). Duplicate
// entries collapse to one.
ReactionPollRepos []string
// ReactionPollInterval is the delay between reaction poll cycles; it
// defaults to 30 seconds and is also the reaction delivery latency.
// When the polled repo count at this interval would exceed the poller's
// API request budget (3000 requests/hour), the adapter logs a warning and
// automatically raises the effective interval to fit — unless
// ReactionPollNoAutoInterval is set. Reaction dedup across cycles is
// in-process only: a restart forgets what was handled, and only reactions
// added while connected are dispatched, so reactions added while the bot
// was down are missed.
ReactionPollInterval time.Duration
// ReactionPollNoAutoInterval disables the automatic raising of the
// effective poll interval when the polled repo count would exceed the API
// request budget. The over-budget warning is still logged; the configured
// ReactionPollInterval is honored and the rate limit may be exhausted.
ReactionPollNoAutoInterval bool
}
Config configures a GitHub bot. Exactly one auth mode must be set: Token (PAT mode) or the AppID/InstallationID/PrivateKey triple (App mode).
type Message ¶
type Message struct {
Event *gogithub.IssueCommentEvent
}
Message is the typed raw payload stored in core.Message.Raw for GitHub bots. Consumers can distinguish PR comments from issue comments via Event.GetIssue().IsPullRequest().
type ReactionPayload ¶
type ReactionPayload struct {
Reaction *gogithub.Reaction
Comment *gogithub.IssueComment
}
ReactionPayload is the typed raw payload stored in core.Reaction.Raw for GitHub bots: the reaction and the issue comment it was added to. There is no webhook event to carry — GitHub sends none for reactions; the adapter discovered the reaction by polling (see Config.ReactionPollRepos).
func RawReaction ¶
func RawReaction(r *core.Reaction) (*ReactionPayload, bool)
RawReaction returns the typed reaction payload carried on r, reporting whether r originated from GitHub. Reaction.GetContent gives the bare REST content name ("+1", "hooray") behind the unicode Emoji.