Finish MessageService
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
parent
1908dedd27
commit
ac2fe30647
4 changed files with 371 additions and 2 deletions
172
message.go
172
message.go
|
@ -2,10 +2,13 @@ package reddit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/go-querystring/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageService handles communication with the message
|
// MessageService handles communication with the message
|
||||||
|
@ -16,6 +19,106 @@ type MessageService struct {
|
||||||
client *Client
|
client *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Message is a message.
|
||||||
|
type Message struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
FullID string `json:"name"`
|
||||||
|
Created *Timestamp `json:"created_utc"`
|
||||||
|
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Text string `json:"body"`
|
||||||
|
ParentID string `json:"parent_id"`
|
||||||
|
|
||||||
|
Author string `json:"author"`
|
||||||
|
To string `json:"dest"`
|
||||||
|
|
||||||
|
IsComment bool `json:"was_comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages is a list of messages.
|
||||||
|
type Messages struct {
|
||||||
|
Messages []*Message `json:"messages"`
|
||||||
|
After string `json:"after"`
|
||||||
|
Before string `json:"before"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rootInboxListing struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Data inboxListing `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type inboxListing struct {
|
||||||
|
Things inboxThings `json:"children"`
|
||||||
|
After string `json:"after"`
|
||||||
|
Before string `json:"before"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The returned JSON for comments is a bit different.
|
||||||
|
// It looks for like the Message struct.
|
||||||
|
type inboxThings struct {
|
||||||
|
Comments []*Message
|
||||||
|
Messages []*Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes or clears the inbox.
|
||||||
|
func (t *inboxThings) init() {
|
||||||
|
t.Comments = make([]*Message, 0)
|
||||||
|
t.Messages = make([]*Message, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (t *inboxThings) UnmarshalJSON(b []byte) error {
|
||||||
|
t.init()
|
||||||
|
|
||||||
|
var things []thing
|
||||||
|
if err := json.Unmarshal(b, &things); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, thing := range things {
|
||||||
|
switch thing.Kind {
|
||||||
|
case kindComment:
|
||||||
|
v := new(Message)
|
||||||
|
if err := json.Unmarshal(thing.Data, v); err == nil {
|
||||||
|
t.Comments = append(t.Comments, v)
|
||||||
|
}
|
||||||
|
case kindMessage:
|
||||||
|
v := new(Message)
|
||||||
|
if err := json.Unmarshal(thing.Data, v); err == nil {
|
||||||
|
t.Messages = append(t.Messages, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *rootInboxListing) getComments() *Messages {
|
||||||
|
return &Messages{
|
||||||
|
Messages: l.Data.Things.Comments,
|
||||||
|
After: l.Data.After,
|
||||||
|
Before: l.Data.Before,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *rootInboxListing) getMessages() *Messages {
|
||||||
|
return &Messages{
|
||||||
|
Messages: l.Data.Things.Messages,
|
||||||
|
After: l.Data.After,
|
||||||
|
Before: l.Data.Before,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageRequest represents a request to send a message.
|
||||||
|
type SendMessageRequest struct {
|
||||||
|
// Username, or /r/name for that subreddit's moderators.
|
||||||
|
To string `url:"to"`
|
||||||
|
Subject string `url:"subject"`
|
||||||
|
Text string `url:"text"`
|
||||||
|
// Optional. If specified, the message will look like it came from the subreddit.
|
||||||
|
FromSubreddit string `url:"from_sr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ReadAll marks all messages/comments as read. It queues up the task on Reddit's end.
|
// ReadAll marks all messages/comments as read. It queues up the task on Reddit's end.
|
||||||
// A successful response returns 202 to acknowledge acceptance of the request.
|
// A successful response returns 202 to acknowledge acceptance of the request.
|
||||||
// This endpoint is heavily rate limited.
|
// This endpoint is heavily rate limited.
|
||||||
|
@ -136,3 +239,72 @@ func (s *MessageService) Delete(ctx context.Context, id string) (*Response, erro
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
return s.client.Do(ctx, req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send sends a message.
|
||||||
|
func (s *MessageService) Send(ctx context.Context, sendRequest *SendMessageRequest) (*Response, error) {
|
||||||
|
if sendRequest == nil {
|
||||||
|
return nil, errors.New("sendRequest: cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "api/compose"
|
||||||
|
|
||||||
|
form, err := query.Values(sendRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
form.Set("api_type", "json")
|
||||||
|
|
||||||
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inbox returns comments and messages that appear in your inbox, respectively.
|
||||||
|
func (s *MessageService) Inbox(ctx context.Context, opts *ListOptions) (*Messages, *Messages, *Response, error) {
|
||||||
|
root, resp, err := s.inbox(ctx, "message/inbox", opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, resp, err
|
||||||
|
}
|
||||||
|
return root.getComments(), root.getMessages(), resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InboxUnread returns unread comments and messages that appear in your inbox, respectively.
|
||||||
|
func (s *MessageService) InboxUnread(ctx context.Context, opts *ListOptions) (*Messages, *Messages, *Response, error) {
|
||||||
|
root, resp, err := s.inbox(ctx, "message/unread", opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, resp, err
|
||||||
|
}
|
||||||
|
return root.getComments(), root.getMessages(), resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent returns messages that you've sent.
|
||||||
|
func (s *MessageService) Sent(ctx context.Context, opts *ListOptions) (*Messages, *Response, error) {
|
||||||
|
root, resp, err := s.inbox(ctx, "message/sent", opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.getMessages(), resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessageService) inbox(ctx context.Context, path string, opts *ListOptions) (*rootInboxListing, *Response, error) {
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(rootInboxListing)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, nil
|
||||||
|
}
|
||||||
|
|
128
message_test.go
128
message_test.go
|
@ -1,13 +1,57 @@
|
||||||
package reddit
|
package reddit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var expectedCommentMessages = &Messages{
|
||||||
|
Messages: []*Message{
|
||||||
|
{
|
||||||
|
ID: "g1xi2m9",
|
||||||
|
FullID: "t1_g1xi2m9",
|
||||||
|
Created: &Timestamp{time.Date(2020, 8, 18, 0, 24, 13, 0, time.UTC)},
|
||||||
|
|
||||||
|
Subject: "post reply",
|
||||||
|
Text: "u/testuser2 hello",
|
||||||
|
ParentID: "t3_hs03f3",
|
||||||
|
|
||||||
|
Author: "testuser1",
|
||||||
|
To: "testuser2",
|
||||||
|
|
||||||
|
IsComment: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
After: "",
|
||||||
|
Before: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedMessages = &Messages{
|
||||||
|
Messages: []*Message{
|
||||||
|
{
|
||||||
|
ID: "qwki97",
|
||||||
|
FullID: "t4_qwki97",
|
||||||
|
Created: &Timestamp{time.Date(2020, 8, 18, 0, 16, 53, 0, time.UTC)},
|
||||||
|
|
||||||
|
Subject: "re: test",
|
||||||
|
Text: "test",
|
||||||
|
ParentID: "t4_qwki4m",
|
||||||
|
|
||||||
|
Author: "testuser1",
|
||||||
|
To: "testuser2",
|
||||||
|
|
||||||
|
IsComment: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
After: "",
|
||||||
|
Before: "",
|
||||||
|
}
|
||||||
|
|
||||||
func TestMessageService_ReadAll(t *testing.T) {
|
func TestMessageService_ReadAll(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
@ -147,3 +191,87 @@ func TestMessageService_Delete(t *testing.T) {
|
||||||
_, err := client.Message.Delete(ctx, "test")
|
_, err := client.Message.Delete(ctx, "test")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMessageService_Send(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
mux.HandleFunc("/api/compose", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(t, http.MethodPost, r.Method)
|
||||||
|
|
||||||
|
form := url.Values{}
|
||||||
|
form.Set("api_type", "json")
|
||||||
|
form.Set("to", "test")
|
||||||
|
form.Set("subject", "test subject")
|
||||||
|
form.Set("text", "test text")
|
||||||
|
form.Set("from_sr", "hello world")
|
||||||
|
|
||||||
|
err := r.ParseForm()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, form, r.Form)
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := client.Message.Send(ctx, nil)
|
||||||
|
require.EqualError(t, err, "sendRequest: cannot be nil")
|
||||||
|
|
||||||
|
_, err = client.Message.Send(ctx, &SendMessageRequest{
|
||||||
|
To: "test",
|
||||||
|
Subject: "test subject",
|
||||||
|
Text: "test text",
|
||||||
|
FromSubreddit: "hello world",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageService_Inbox(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
blob, err := readFileContents("testdata/message/inbox.json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mux.HandleFunc("/message/inbox", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(t, http.MethodGet, r.Method)
|
||||||
|
fmt.Fprint(w, blob)
|
||||||
|
})
|
||||||
|
|
||||||
|
comments, messages, _, err := client.Message.Inbox(ctx, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedCommentMessages, comments)
|
||||||
|
require.Equal(t, expectedMessages, messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageService_InboxUnread(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
blob, err := readFileContents("testdata/message/inbox.json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mux.HandleFunc("/message/unread", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(t, http.MethodGet, r.Method)
|
||||||
|
fmt.Fprint(w, blob)
|
||||||
|
})
|
||||||
|
|
||||||
|
comments, messages, _, err := client.Message.InboxUnread(ctx, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedCommentMessages, comments)
|
||||||
|
require.Equal(t, expectedMessages, messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageService_Sent(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
blob, err := readFileContents("testdata/message/inbox.json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mux.HandleFunc("/message/sent", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(t, http.MethodGet, r.Method)
|
||||||
|
fmt.Fprint(w, blob)
|
||||||
|
})
|
||||||
|
|
||||||
|
messages, _, err := client.Message.Sent(ctx, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedMessages, messages)
|
||||||
|
}
|
||||||
|
|
70
testdata/message/inbox.json
vendored
Normal file
70
testdata/message/inbox.json
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"kind": "Listing",
|
||||||
|
"data": {
|
||||||
|
"modhash": null,
|
||||||
|
"dist": 2,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"kind": "t1",
|
||||||
|
"data": {
|
||||||
|
"first_message": null,
|
||||||
|
"first_message_name": null,
|
||||||
|
"subreddit": "helloworldtestt",
|
||||||
|
"likes": null,
|
||||||
|
"replies": "",
|
||||||
|
"id": "g1xi2m9",
|
||||||
|
"subject": "post reply",
|
||||||
|
"associated_awarding_id": null,
|
||||||
|
"score": 1,
|
||||||
|
"author": "testuser1",
|
||||||
|
"num_comments": 17,
|
||||||
|
"parent_id": "t3_hs03f3",
|
||||||
|
"subreddit_name_prefixed": "r/helloworldtestt",
|
||||||
|
"new": false,
|
||||||
|
"type": "post_reply",
|
||||||
|
"body": "u/testuser2 hello",
|
||||||
|
"link_title": "post 1",
|
||||||
|
"dest": "testuser2",
|
||||||
|
"was_comment": true,
|
||||||
|
"body_html": "<!-- SC_OFF --><div class=\"md\"><p><a href=\"/u/testuser2\">u/testuser2</a> hello</p>\n</div><!-- SC_ON -->",
|
||||||
|
"name": "t1_g1xi2m9",
|
||||||
|
"created": 1597739053.0,
|
||||||
|
"created_utc": 1597710253.0,
|
||||||
|
"context": "/r/helloworldtestt/comments/hs03f3/post_1/g1xi2m9/?context=3",
|
||||||
|
"distinguished": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "t4",
|
||||||
|
"data": {
|
||||||
|
"first_message": 1626823824,
|
||||||
|
"first_message_name": "t4_qwkhao",
|
||||||
|
"subreddit": null,
|
||||||
|
"likes": null,
|
||||||
|
"replies": "",
|
||||||
|
"id": "qwki97",
|
||||||
|
"subject": "re: test",
|
||||||
|
"associated_awarding_id": null,
|
||||||
|
"score": 0,
|
||||||
|
"author": "testuser1",
|
||||||
|
"num_comments": null,
|
||||||
|
"parent_id": "t4_qwki4m",
|
||||||
|
"subreddit_name_prefixed": null,
|
||||||
|
"new": false,
|
||||||
|
"type": "unknown",
|
||||||
|
"body": "test",
|
||||||
|
"dest": "testuser2",
|
||||||
|
"body_html": "<!-- SC_OFF --><div class=\"md\"><p>test</p>\n</div><!-- SC_ON -->",
|
||||||
|
"was_comment": false,
|
||||||
|
"name": "t4_qwki97",
|
||||||
|
"created": 1597738613.0,
|
||||||
|
"created_utc": 1597709813.0,
|
||||||
|
"context": "",
|
||||||
|
"distinguished": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"after": "",
|
||||||
|
"before": null
|
||||||
|
}
|
||||||
|
}
|
|
@ -106,7 +106,6 @@ func (t *things) UnmarshalJSON(b []byte) error {
|
||||||
if err := json.Unmarshal(thing.Data, v); err == nil {
|
if err := json.Unmarshal(thing.Data, v); err == nil {
|
||||||
t.Posts = append(t.Posts, v)
|
t.Posts = append(t.Posts, v)
|
||||||
}
|
}
|
||||||
case kindMessage:
|
|
||||||
case kindSubreddit:
|
case kindSubreddit:
|
||||||
v := new(Subreddit)
|
v := new(Subreddit)
|
||||||
if err := json.Unmarshal(thing.Data, v); err == nil {
|
if err := json.Unmarshal(thing.Data, v); err == nil {
|
||||||
|
@ -389,7 +388,7 @@ type Posts struct {
|
||||||
Before string `json:"before"`
|
Before string `json:"before"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModActions is a list of moderator action.
|
// ModActions is a list of moderator actions.
|
||||||
type ModActions struct {
|
type ModActions struct {
|
||||||
ModActions []*ModAction `json:"moderator_actions"`
|
ModActions []*ModAction `json:"moderator_actions"`
|
||||||
After string `json:"after"`
|
After string `json:"after"`
|
||||||
|
|
Loading…
Reference in a new issue