Skip to content

Commit

Permalink
mapping: Interactions (#23)
Browse files Browse the repository at this point in the history
* mapping: Interactions

This commit should contain all the
https://discord.com/developers/docs/interactions/receiving-and-responding
mapped

* fix tests

* use API v9

* remove unecessary path.Clean
  • Loading branch information
PL Pery authored Jan 13, 2022
1 parent 1256161 commit 0370526
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 21 deletions.
2 changes: 1 addition & 1 deletion commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (m *Mux) GetCommands(options ...func(*CommandsOpt)) ([]Command, error) {
r.Append("commands")

var commands []Command
_, err := rest.DoJson(m.Client, r.Get(m.authorize, rest.JSON), &commands)
_, err := rest.DoJSON(m.Client, r.Get(m.authorize, rest.JSON), &commands)
if err != nil {
return nil, err
}
Expand Down
154 changes: 154 additions & 0 deletions interactions-api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package corde

import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"

"github.com/Karitham/corde/internal/rest"
)

// returns the body and its content-type
func toBody(i *InteractionRespData) (*bytes.Buffer, string) {
body := new(bytes.Buffer)
contentType := "application/json"

payloadJSON := &bytes.Buffer{}
err := json.NewEncoder(payloadJSON).Encode(i)
if err != nil {
return nil, ""
}

if len(i.Attachments) < 1 {
payloadJSON.WriteTo(body)
return body, contentType
}

mw := multipart.NewWriter(body)
defer mw.Close()

contentType = mw.FormDataContentType()
mw.WriteField("payload_json", payloadJSON.String())

for i, f := range i.Attachments {
if f.ID == 0 {
f.ID = Snowflake(i)
}

ff, CFerr := mw.CreateFormFile(fmt.Sprintf("files[%d]", i), f.Filename)
if CFerr != nil {
return body, contentType
}

if _, CopyErr := io.Copy(ff, f.Body); CopyErr != nil {
return body, contentType
}
}
return body, contentType
}

// GetOriginalInteraction returns the original response to an Interaction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response
func (m *Mux) GetOriginalInteraction(token string) (*InteractionRespData, error) {
data := &InteractionRespData{}
_, err := rest.DoJSON(m.Client, rest.Req("/webhooks", m.AppID, token, "messages/@original").Get(m.authorize), data)
if err != nil {
return nil, err
}

return data, nil
}

// EditOriginalInteraction to edit your initial response to an Interaction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response
func (m *Mux) EditOriginalInteraction(token string, data InteractionResponder) error {
body, contentType := toBody(data.InteractionRespData())

_, err := m.Client.Do(
rest.Req("/webhooks", m.AppID, token, "messages/@original").
AnyBody(body).Patch(m.authorize, rest.ContentType(contentType)),
)
if err != nil {
return err
}
return nil
}

// DeleteOriginalInteraction to delete your initial response to an Interaction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response
func (m *Mux) DeleteOriginalInteraction(token string) error {
_, err := m.Client.Do(
rest.Req("/webhooks", m.AppID, token, "messages/@original").
Delete(m.authorize),
)
if err != nil {
return err
}

return nil
}

// FollowUpInteraction follows up a response to an Interaction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#followup-messages
func (m *Mux) FollowUpInteraction(token string, data InteractionResponder) error {
body, contentType := toBody(data.InteractionRespData())

_, err := m.Client.Do(
rest.Req("/webhooks", m.AppID, token).
AnyBody(body).Post(m.authorize, rest.ContentType(contentType)),
)
if err != nil {
return err
}
return nil
}

// GetFollowUpInteraction returns the response to a FollowUpInteraction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message
func (m *Mux) GetFollowUpInteraction(token string, messageID Snowflake) (*InteractionRespData, error) {
data := &InteractionRespData{}
_, err := rest.DoJSON(m.Client, rest.Req("/webhooks", m.AppID, token, "messages", messageID).Get(m.authorize), data)
if err != nil {
return nil, err
}

return data, nil
}

// EditFollowUpInteraction to edit a response to a FollowUpInteraction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message
func (m *Mux) EditFollowUpInteraction(token string, messageID Snowflake, data InteractionResponder) error {
body, contentType := toBody(data.InteractionRespData())

_, err := m.Client.Do(
rest.Req("/webhooks", m.AppID, token, "messages", messageID).
AnyBody(body).Patch(m.authorize, rest.ContentType(contentType)),
)
if err != nil {
return err
}
return nil
}

// DeleteFollowUpInteraction to delete a response to a FollowUpInteraction
//
// https://discord.com/developers/docs/interactions/receiving-and-responding#delete-followup-message
func (m *Mux) DeleteFollowUpInteraction(token string, messageID Snowflake) error {
_, err := m.Client.Do(
rest.Req("/webhooks", m.AppID, token, "messages", messageID).
Delete(m.authorize),
)
if err != nil {
return err
}

return nil
}
10 changes: 9 additions & 1 deletion internal/rest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"net/http"
)

func DoJson(c *http.Client, r *http.Request, v any) (*http.Response, error) {
// DoJSON executes a request and decodes the response into the given interface
// It already calls `Close()` on the body
func DoJSON(c *http.Client, r *http.Request, v any) (*http.Response, error) {
resp, err := c.Do(r)
if err != nil {
return nil, err
Expand All @@ -18,3 +20,9 @@ func DoJson(c *http.Client, r *http.Request, v any) (*http.Response, error) {

return resp, nil
}

func ContentType(contentType string) func(*http.Request) {
return func(r *http.Request) {
r.Header.Set("content-type", contentType)
}
}
6 changes: 5 additions & 1 deletion internal/rest/reqBuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Request struct {
body io.Reader
}

var API = "https://discord.com/api/v8"
var API = "https://discord.com/api/v9"

func Req(paths ...any) *Request {
r := &Request{
Expand Down Expand Up @@ -69,6 +69,10 @@ func (r *Request) Delete(opts ...func(*http.Request)) *http.Request {
return r.new(http.MethodDelete, r.body, opts...)
}

func (r *Request) Patch(opts ...func(*http.Request)) *http.Request {
return r.new(http.MethodPatch, r.body, opts...)
}

func JSON(r *http.Request) {
r.Header.Set("content-type", "application/json")
}
Expand Down
13 changes: 6 additions & 7 deletions owmock/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"log"
"net/http"
"reflect"
"strconv"
"testing"
"time"

"github.com/matryer/is"
Expand Down Expand Up @@ -44,8 +44,6 @@ func req(c Doer, method string, url string, buf *bytes.Buffer, privK ed25519.Pri
if err = json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
return nil, err
}

log.Println(string(respBody))
return respBody, nil
}

Expand Down Expand Up @@ -103,15 +101,16 @@ func (r *Requester) Post(body string) (json.RawMessage, error) {
}

// PostExpect posts a payload and expects a response with the given body
func (r *Requester) PostExpect(t is.T, body any, expectV any) error {
func (r *Requester) PostExpect(t *testing.T, body string, expectV any) error {
is := is.New(t)
resp, err := r.PostJSON(body)
resp, err := r.Post(body)
is.NoErr(err)

typ := reflect.TypeOf(expectV)
typ := reflect.TypeOf(expectV).Elem()
respV := reflect.New(typ).Interface()

err = json.Unmarshal(resp, respV)
b, _ := resp.MarshalJSON()
err = json.Unmarshal(b, respV)
is.NoErr(err)

is.Equal(respV, expectV)
Expand Down
3 changes: 2 additions & 1 deletion responder.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func (r *Responder) respond(i intResponse) {
mw := multipart.NewWriter(r.w)
defer mw.Close()

r.w.Header().Set("Content-Type", mw.FormDataContentType())
contentType := mw.FormDataContentType()
r.w.Header().Set("content-type", contentType)
mw.WriteField("payload_json", payloadJSON.String())

for i, f := range i.Data.Attachments {
Expand Down
9 changes: 1 addition & 8 deletions sample-component_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package corde_test

import (
"encoding/json"
"net/http/httptest"
"testing"

Expand Down Expand Up @@ -29,14 +28,8 @@ func TestComponentInteraction(t *testing.T) {
}

s := httptest.NewServer(mux.Handler())
respPost, err := owmock.NewWithClient(s.URL, s.Client()).Post(SampleComponent)
err := owmock.NewWithClient(s.URL, s.Client()).PostExpect(t, SampleComponent, expect)
assert.NoErr(err)

respV := &owmock.InteractionResponse{}
err = json.Unmarshal(respPost, respV)
assert.NoErr(err)

assert.Equal(expect, respV)
}

const SampleComponent = `{
Expand Down
4 changes: 2 additions & 2 deletions users.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import (
// Me returns the current user
func (m *Mux) Me() (User, error) {
var user User
_, err := rest.DoJson(m.Client, rest.Req("/users/@me").Get(m.authorize), &user)
_, err := rest.DoJSON(m.Client, rest.Req("/users/@me").Get(m.authorize), &user)
return user, err
}

// GetUser returns a user by id
func (m *Mux) GetUser(id Snowflake) (User, error) {
var user User
_, err := rest.DoJson(m.Client, rest.Req("/users/", id).Get(m.authorize), &user)
_, err := rest.DoJSON(m.Client, rest.Req("/users/", id).Get(m.authorize), &user)
return user, err
}

0 comments on commit 0370526

Please sign in to comment.