diff --git a/api/webhook/webhook.go b/api/webhook/webhook.go index dff7f72..464e9db 100644 --- a/api/webhook/webhook.go +++ b/api/webhook/webhook.go @@ -6,6 +6,7 @@ import ( "context" "mime/multipart" "net/url" + "regexp" "strconv" "github.com/pkg/errors" @@ -22,6 +23,23 @@ import ( // TODO: if there's ever an Arikawa v3, then a new Client abstraction could be // made that wraps around Session being an interface. Just a food for thought. +var webhookURLRe = regexp.MustCompile(`https://discord(?:app)?.com/api/webhooks/(\d+)/(.+)`) + +// ParseURL parses the given Discord webhook URL. +func ParseURL(webhookURL string) (id discord.WebhookID, token string, err error) { + matches := webhookURLRe.FindStringSubmatch(webhookURL) + if matches == nil { + return 0, "", errors.New("invalid webhook URL") + } + + idInt, err := strconv.ParseUint(matches[1], 10, 64) + if err != nil { + return 0, "", errors.Wrap(err, "failed to parse webhook ID") + } + + return discord.WebhookID(idInt), matches[2], nil +} + // Session keeps a single webhook session. It is referenced by other webhook // clients using the same session. type Session struct { @@ -45,6 +63,11 @@ func (s *Session) OnResponse(r httpdriver.Request, resp httpdriver.Response) err return s.Limiter.Release(r.GetPath(), httpdriver.OptHeader(resp)) } +// Client creates a new Webhook API client from the session. +func (s *Session) Client() *Client { + return &Client{httputil.NewClient(), s} +} + // Client is the client used to interact with a webhook. type Client struct { // Client is the httputil.Client used to call Discord's API. @@ -78,6 +101,16 @@ func NewCustom(id discord.WebhookID, token string, hcl *httputil.Client) *Client } } +// NewFromURL creates a new webhook client using the passed webhook URL. It +// uses its own rate limiter. +func NewFromURL(url string) (*Client, error) { + id, token, err := ParseURL(url) + if err != nil { + return nil, err + } + return New(id, token), nil +} + // FromAPI creates a new client that shares the same internal HTTP client with // the one in the API's. This is often useful for bots that need webhook // interaction, since the rate limiter is shared.