2020-08-14 20:06:00 -04:00
|
|
|
package reddit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-08-17 21:13:33 -04:00
|
|
|
"encoding/json"
|
2020-08-14 20:06:00 -04:00
|
|
|
"errors"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
2020-08-17 21:13:33 -04:00
|
|
|
|
|
|
|
"github.com/google/go-querystring/query"
|
2020-08-14 20:06:00 -04:00
|
|
|
)
|
|
|
|
|
2020-08-16 22:22:13 -04:00
|
|
|
// MessageService handles communication with the message
|
2020-08-14 20:06:00 -04:00
|
|
|
// related methods of the Reddit API.
|
|
|
|
//
|
|
|
|
// Reddit API docs: https://www.reddit.com/dev/api/#section_messages
|
2020-08-16 22:22:13 -04:00
|
|
|
type MessageService struct {
|
2020-08-14 20:06:00 -04:00
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
2020-08-17 21:13:33 -04:00
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
2020-08-30 21:25:01 -04:00
|
|
|
type inboxThing struct {
|
|
|
|
Kind string `json:"kind"`
|
|
|
|
Data *Message `json:"data"`
|
|
|
|
}
|
|
|
|
|
2020-08-29 14:45:45 -04:00
|
|
|
type inboxListing struct {
|
|
|
|
inboxThings
|
|
|
|
after string
|
|
|
|
before string
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
2020-08-29 14:45:45 -04:00
|
|
|
func (l *inboxListing) After() string {
|
|
|
|
return l.after
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
2020-08-29 14:45:45 -04:00
|
|
|
func (l *inboxListing) Before() string {
|
|
|
|
return l.before
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (l *inboxListing) UnmarshalJSON(b []byte) error {
|
|
|
|
root := new(struct {
|
|
|
|
Data struct {
|
|
|
|
Things inboxThings `json:"children"`
|
|
|
|
After string `json:"after"`
|
|
|
|
Before string `json:"before"`
|
|
|
|
} `json:"data"`
|
|
|
|
})
|
|
|
|
|
|
|
|
err := json.Unmarshal(b, root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
l.inboxThings = root.Data.Things
|
|
|
|
l.after = root.Data.After
|
|
|
|
l.before = root.Data.Before
|
|
|
|
|
|
|
|
return nil
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type inboxThings struct {
|
|
|
|
Comments []*Message
|
|
|
|
Messages []*Message
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (t *inboxThings) UnmarshalJSON(b []byte) error {
|
2020-08-30 21:25:01 -04:00
|
|
|
var things []inboxThing
|
2020-08-17 21:13:33 -04:00
|
|
|
if err := json.Unmarshal(b, &things); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-30 21:25:01 -04:00
|
|
|
t.add(things...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *inboxThings) add(things ...inboxThing) {
|
2020-08-17 21:13:33 -04:00
|
|
|
for _, thing := range things {
|
|
|
|
switch thing.Kind {
|
|
|
|
case kindComment:
|
2020-08-30 21:25:01 -04:00
|
|
|
t.Comments = append(t.Comments, thing.Data)
|
2020-08-17 21:13:33 -04:00
|
|
|
case kindMessage:
|
2020-08-30 21:25:01 -04:00
|
|
|
t.Messages = append(t.Messages, thing.Data)
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
2020-08-14 20:06:00 -04:00
|
|
|
// 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.
|
|
|
|
// This endpoint is heavily rate limited.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) ReadAll(ctx context.Context) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
path := "api/read_all_messages"
|
|
|
|
|
|
|
|
req, err := s.client.NewRequest(http.MethodPost, path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read marks a message/comment as read via its full ID.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Read(ctx context.Context, ids ...string) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
if len(ids) == 0 {
|
|
|
|
return nil, errors.New("must provide at least 1 id")
|
|
|
|
}
|
|
|
|
|
|
|
|
path := "api/read_message"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", strings.Join(ids, ","))
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unread marks a message/comment as unread via its full ID.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Unread(ctx context.Context, ids ...string) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
if len(ids) == 0 {
|
|
|
|
return nil, errors.New("must provide at least 1 id")
|
|
|
|
}
|
|
|
|
|
|
|
|
path := "api/unread_message"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", strings.Join(ids, ","))
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Block the author of a post, comment or message via its full ID.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Block(ctx context.Context, id string) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
path := "api/block"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", id)
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Collapse messages.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Collapse(ctx context.Context, ids ...string) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
if len(ids) == 0 {
|
|
|
|
return nil, errors.New("must provide at least 1 id")
|
|
|
|
}
|
|
|
|
|
|
|
|
path := "api/collapse_message"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", strings.Join(ids, ","))
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Uncollapse messages.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Uncollapse(ctx context.Context, ids ...string) (*Response, error) {
|
2020-08-14 20:06:00 -04:00
|
|
|
if len(ids) == 0 {
|
|
|
|
return nil, errors.New("must provide at least 1 id")
|
|
|
|
}
|
|
|
|
|
|
|
|
path := "api/uncollapse_message"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", strings.Join(ids, ","))
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
2020-08-16 22:22:13 -04:00
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Delete a message.
|
2020-08-16 22:22:13 -04:00
|
|
|
func (s *MessageService) Delete(ctx context.Context, id string) (*Response, error) {
|
|
|
|
path := "api/del_msg"
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("id", id)
|
|
|
|
|
|
|
|
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
2020-08-17 21:13:33 -04:00
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Send a message.
|
2020-08-17 21:13:33 -04:00
|
|
|
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.
|
2020-08-29 14:45:45 -04:00
|
|
|
func (s *MessageService) Inbox(ctx context.Context, opts *ListOptions) ([]*Message, []*Message, *Response, error) {
|
2020-08-17 21:13:33 -04:00
|
|
|
root, resp, err := s.inbox(ctx, "message/inbox", opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, resp, err
|
|
|
|
}
|
2020-08-29 14:45:45 -04:00
|
|
|
return root.Comments, root.Messages, resp, nil
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// InboxUnread returns unread comments and messages that appear in your inbox, respectively.
|
2020-08-29 14:45:45 -04:00
|
|
|
func (s *MessageService) InboxUnread(ctx context.Context, opts *ListOptions) ([]*Message, []*Message, *Response, error) {
|
2020-08-17 21:13:33 -04:00
|
|
|
root, resp, err := s.inbox(ctx, "message/unread", opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, resp, err
|
|
|
|
}
|
2020-08-29 14:45:45 -04:00
|
|
|
return root.Comments, root.Messages, resp, nil
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sent returns messages that you've sent.
|
2020-08-29 14:45:45 -04:00
|
|
|
func (s *MessageService) Sent(ctx context.Context, opts *ListOptions) ([]*Message, *Response, error) {
|
2020-08-17 21:13:33 -04:00
|
|
|
root, resp, err := s.inbox(ctx, "message/sent", opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
2020-08-29 14:45:45 -04:00
|
|
|
return root.Messages, resp, nil
|
2020-08-17 21:13:33 -04:00
|
|
|
}
|
|
|
|
|
2020-08-29 14:45:45 -04:00
|
|
|
func (s *MessageService) inbox(ctx context.Context, path string, opts *ListOptions) (*inboxListing, *Response, error) {
|
2020-08-17 21:13:33 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-08-29 14:45:45 -04:00
|
|
|
root := new(inboxListing)
|
2020-08-17 21:13:33 -04:00
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return root, resp, nil
|
|
|
|
}
|