github.com/diamondburned/arikawa@v1.3.14/webhook/webhook.go (about) 1 // Package webhook provides means to interact with webhooks directly and not 2 // through the bot API. 3 package webhook 4 5 import ( 6 "mime/multipart" 7 "net/url" 8 "strconv" 9 10 "github.com/pkg/errors" 11 12 "github.com/diamondburned/arikawa/api" 13 "github.com/diamondburned/arikawa/discord" 14 "github.com/diamondburned/arikawa/utils/httputil" 15 "github.com/diamondburned/arikawa/utils/json" 16 ) 17 18 // DefaultHTTPClient is the httputil.Client used in the helper methods. 19 var DefaultHTTPClient = httputil.NewClient() 20 21 // Client is the client used to interact with a webhook. 22 type Client struct { 23 // Client is the httputil.Client used to call Discord's API. 24 *httputil.Client 25 // ID is the id of the webhook. 26 ID discord.WebhookID 27 // Token is the token of the webhook. 28 Token string 29 } 30 31 // NewClient creates a new Client using the passed token and id. 32 func NewClient(id discord.WebhookID, token string) *Client { 33 return NewCustomClient(id, token, httputil.NewClient()) 34 } 35 36 // NewCustomClient creates a new Client creates a new Client using the passed 37 // token and id and makes API calls using the passed httputil.Client 38 func NewCustomClient(id discord.WebhookID, token string, c *httputil.Client) *Client { 39 return &Client{ 40 Client: c, 41 ID: id, 42 Token: token, 43 } 44 } 45 46 // Get gets the webhook. 47 func (c *Client) Get() (*discord.Webhook, error) { 48 var w *discord.Webhook 49 return w, c.RequestJSON(&w, "GET", api.EndpointWebhooks+c.ID.String()+"/"+c.Token) 50 } 51 52 // Modify modifies the webhook. 53 func (c *Client) Modify(data api.ModifyWebhookData) (*discord.Webhook, error) { 54 var w *discord.Webhook 55 return w, c.RequestJSON( 56 &w, "PATCH", 57 api.EndpointWebhooks+c.ID.String()+"/"+c.Token, 58 httputil.WithJSONBody(data), 59 ) 60 } 61 62 // Delete deletes a webhook permanently. 63 func (c *Client) Delete() error { 64 return c.FastRequest("DELETE", api.EndpointWebhooks+c.ID.String()+"/"+c.Token) 65 } 66 67 // Execute sends a message to the webhook, but doesn't wait for the message to 68 // get created. This is generally faster, but only applicable if no further 69 // interaction is required. 70 func (c *Client) Execute(data api.ExecuteWebhookData) (err error) { 71 _, err = c.execute(data, false) 72 return 73 } 74 75 // ExecuteAndWait executes the webhook, and waits for the generated 76 // discord.Message to be returned. 77 func (c *Client) ExecuteAndWait(data api.ExecuteWebhookData) (*discord.Message, error) { 78 return c.execute(data, true) 79 } 80 81 func (c *Client) execute(data api.ExecuteWebhookData, wait bool) (*discord.Message, error) { 82 if data.Content == "" && len(data.Embeds) == 0 && len(data.Files) == 0 { 83 return nil, api.ErrEmptyMessage 84 } 85 86 if data.AllowedMentions != nil { 87 if err := data.AllowedMentions.Verify(); err != nil { 88 return nil, errors.Wrap(err, "allowedMentions error") 89 } 90 } 91 92 for i, embed := range data.Embeds { 93 if err := embed.Validate(); err != nil { 94 return nil, errors.Wrap(err, "embed error at "+strconv.Itoa(i)) 95 } 96 } 97 98 var param = url.Values{} 99 if wait { 100 param.Set("wait", "true") 101 } 102 103 var URL = api.EndpointWebhooks + c.ID.String() + "/" + c.Token + "?" + param.Encode() 104 var msg *discord.Message 105 106 if len(data.Files) == 0 { 107 // No files, so no need for streaming. 108 return msg, c.RequestJSON(&msg, "POST", URL, 109 httputil.WithJSONBody(data)) 110 } 111 112 writer := func(mw *multipart.Writer) error { 113 return data.WriteMultipart(mw) 114 } 115 116 resp, err := c.MeanwhileMultipart(writer, "POST", URL) 117 if err != nil { 118 return nil, err 119 } 120 121 var body = resp.GetBody() 122 defer body.Close() 123 124 if !wait { 125 // Since we didn't tell Discord to wait, we have nothing to parse. 126 return nil, nil 127 } 128 129 return msg, json.DecodeStream(body, &msg) 130 } 131 132 // Get is a shortcut for NewCustomClient(token, id, DefaultHTTPClient).Get(). 133 func Get(id discord.WebhookID, token string) (*discord.Webhook, error) { 134 return NewCustomClient(id, token, DefaultHTTPClient).Get() 135 } 136 137 // Modify is a shortcut for 138 // NewCustomClient(token, id, DefaultHTTPClient).Modify(data). 139 func Modify( 140 id discord.WebhookID, token string, data api.ModifyWebhookData) (*discord.Webhook, error) { 141 142 return NewCustomClient(id, token, DefaultHTTPClient).Modify(data) 143 } 144 145 // Delete is a shortcut for 146 // NewCustomClient(token, id, DefaultHTTPClient).Delete(). 147 func Delete(id discord.WebhookID, token string) error { 148 return NewCustomClient(id, token, DefaultHTTPClient).Delete() 149 } 150 151 // Execute is a shortcut for 152 // NewCustomClient(token, id, DefaultHTTPClient).Execute(data). 153 func Execute(id discord.WebhookID, token string, data api.ExecuteWebhookData) error { 154 return NewCustomClient(id, token, DefaultHTTPClient).Execute(data) 155 } 156 157 // ExecuteAndWait is a shortcut for 158 // NewCustomClient(token, id, DefaultHTTPClient).ExecuteAndWait(data). 159 func ExecuteAndWait( 160 id discord.WebhookID, token string, data api.ExecuteWebhookData) (*discord.Message, error) { 161 162 return NewCustomClient(id, token, DefaultHTTPClient).ExecuteAndWait(data) 163 }