package main import ( "bytes" "context" "encoding/json" "fmt" "net/http" "strings" "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) const discordRequestTimeout = 2 * time.Second type ( createChannelInviteReq struct { MaxAge int64 `json:"max_age"` MaxUses int64 `json:"max_uses"` Temporary bool `json:"temporary"` Unique bool `json:"unique"` // more unsupported params } createChannelInviteResp struct { Code string `json:"code"` // more unsupported params } ) func createInvite() (string, error) { ctx, cancel := context.WithTimeout(context.Background(), discordRequestTimeout) defer cancel() var ( body = new(bytes.Buffer) err error reqURL = fmt.Sprintf("https://discord.com/api/channels/%s/invites", cfg.ChannelID) ) if err = json.NewEncoder(body).Encode(createChannelInviteReq{ MaxAge: int64(cfg.ExpireIn / time.Second), MaxUses: cfg.Uses, Temporary: false, Unique: true, }); err != nil { return "", errors.Wrap(err, "encoding request body") } req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, body) if err != nil { return "", errors.Wrap(err, "creating request") } req.Header.Set("Authorization", strings.Join([]string{"Bot", cfg.DiscordBotToken}, " ")) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Audit-Log-Reason", fmt.Sprintf("twitch-bot-tools/dc-invite requested for %s", cfg.SendTo)) resp, err := http.DefaultClient.Do(req) if err != nil { return "", errors.Wrap(err, "executing request") } defer func() { if err := resp.Body.Close(); err != nil { logrus.WithError(err).Error("closing response body (leaked fd)") } }() if resp.StatusCode != http.StatusOK { return "", errors.Errorf("unexpected status %d", resp.StatusCode) } var invite createChannelInviteResp if err = json.NewDecoder(resp.Body).Decode(&invite); err != nil { return "", errors.Wrap(err, "decoding invite response") } return invite.Code, nil }