WIP: Account service

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian 2020-06-23 21:47:33 -04:00
parent 3bb31e2b43
commit bdd39f5d21
7 changed files with 305 additions and 0 deletions

172
account.go Normal file
View file

@ -0,0 +1,172 @@
package geddit
import (
"context"
"fmt"
"net/http"
)
// AccountService handles communication with the account
// related methods of the Reddit API.
type AccountService interface {
Karma(ctx context.Context) ([]SubredditKarma, *Response, error)
}
// AccountServiceOp implements the AccountService interface.
type AccountServiceOp struct {
client *Client
}
var _ AccountService = &AccountServiceOp{}
type rootSubredditKarma struct {
Kind string `json:"kind,omitempty"`
Data []SubredditKarma `json:"data,omitempty"`
}
// SubredditKarma holds user karma data for the subreddit.
type SubredditKarma struct {
Subreddit string `json:"sr"`
PostKarma int `json:"link_karma"`
CommentKarma int `json:"comment_karma"`
}
// Settings are the user's account settings.
// Some of the fields' descriptions are taken from:
// https://praw.readthedocs.io/en/latest/code_overview/other/preferences.html#praw.models.Preferences.update
// todo: these should probably be pointers with omitempty
type Settings struct {
AcceptPrivateMessages bool `json:"accept_pms"`
// Allow Reddit to use your activity on Reddit to show you more relevant advertisements.
ActivityRelevantAds bool `json:"activity_relevant_ads"`
// Allow reddit to log my outbound clicks for personalization.
AllowClickTracking bool `json:"allow_clicktracking"`
// I would like to beta test features for reddit. By enabling, you will join r/beta immediately.
Beta bool `json:"beta"`
ClickGadget bool `json:"clickgadget"`
CollapseReadMessages bool `json:"collapse_read_messages"`
Compress bool `json:"compress"`
CredditAutorenew bool `json:"creddit_autorenew"`
DefaultCommentSort string `json:"default_comment_sort"`
ShowDomainDetails bool `json:"domain_details"`
SendEmailDigests bool `json:"email_digests"`
SendMessagesAsEmails bool `json:"email_messages"`
UnsubscribeFromAllEmails bool `json:"email_unsubscribe_all"`
DisableCustomThemes bool `json:"enable_default_themes"`
Location string `json:"g"`
HideAds bool `json:"hide_ads"`
// Don't allow search engines to index my user profile.
HideFromSearchEngines bool `json:"hide_from_robots"`
HideUpvotedPosts bool `json:"hide_ups"`
HideDownvotedPosts bool `json:"hide_downs"`
HighlightControversialComments bool `json:"highlight_controversial"`
HighlightNewComments bool `json:"highlight_new_comments"`
IgnoreSuggestedSorts bool `json:"ignore_suggested_sort"`
// Use new Reddit as my default experience.
UseNewReddit bool `json:"in_redesign_beta"`
LabelNSFW bool `json:"label_nsfw"`
Language string `json:"lang"`
ShowOldSearchPage bool `json:"legacy_search"`
EnableNotifications bool `json:"live_orangereds"`
MarkMessagesAsRead bool `json:"mark_messages_read"`
// Determine whether to show thumbnails next to posts in subreddits.
// - "on": show thumbnails next to posts
// - "off": do not show thumbnails next to posts
// - "subreddit": show thumbnails next to posts based on the subreddit's preferences
ShowThumbnails string `json:"media"`
// Determine whether to auto-expand media in subreddits.
// - "on": auto-expand media previews
// - "off": do not auto-expand media previews
// - "subreddit": auto-expand media previews based on the subreddit's preferences
AutoExpandMedia string `json:"media_preview"`
MinimumCommentScore *int `json:"min_comment_score"`
MinimumPostScore *int `json:"min_link_score"`
EnableMentionNotifications bool `json:"monitor_mentions"`
OpenLinksInNewWindow bool `json:"newwindow"`
// todo: test this
DarkMode bool `json:"nightmode"`
DisableProfanity bool `json:"no_profanity"`
NumComments int `json:"num_comments,omitempty"`
NumPosts int `json:"numsites,omitempty"`
ShowSpotlightBox bool `json:"organic"`
// todo: test this
SubredditTheme string `json:"other_theme"`
ShowNSFW bool `json:"over_18"`
EnablePrivateRSSFeeds bool `json:"private_feeds"`
ProfileOptOut bool `json:"profile_opt_out"`
// Make my upvotes and downvotes public.
PublicizeVotes bool `json:"public_votes"`
// Allow my data to be used for research purposes.
AllowResearch bool `json:"research"`
IncludeNSFWSearchResults bool `json:"search_include_over_18"`
ReceiveCrosspostMessages bool `json:"send_crosspost_messages"`
ReceiveWelcomeMessages bool `json:"send_welcome_messages"`
// Show a user's flair (next to their name on a post or comment).
ShowUserFlair bool `json:"show_flair"`
// Show a post's flair.
ShowPostFlair bool `json:"show_link_flair"`
ShowGoldExpiration bool `json:"show_gold_expiration"`
ShowLocationBasedRecommendations bool `json:"show_location_based_recommendations"`
ShowPromote bool `json:"show_promote"`
ShowCustomSubredditThemes bool `json:"show_stylesheets"`
ShowTrendingSubreddits bool `json:"show_trending"`
ShowTwitter bool `json:"show_twitter"`
StoreVisits bool `json:"store_visits"`
ThemeSelector string `json:"theme_selector"`
// Allow Reddit to use data provided by third-parties to show you more relevant advertisements on Reddit.i
AllowThirdPartyDataAdPersonalization bool `json:"third_party_data_personalized_ads"`
// Allow personalization of advertisements using data from third-party websites.
AllowThirdPartySiteDataAdPersonalization bool `json:"third_party_site_data_personalized_ads"`
// Allow personalization of content using data from third-party websites.
AllowThirdPartySiteDataContentPersonalization bool `json:"third_party_site_data_personalized_content"`
EnableThreadedMessages bool `json:"threaded_messages"`
EnableThreadedModmail bool `json:"threaded_modmail"`
TopKarmaSubreddits bool `json:"top_karma_subreddits"`
UseGlobalDefaults bool `json:"use_global_defaults"`
// todo: test this, no_video_autoplay
EnableVideoAutoplay bool `json:"video_autoplay"`
}
// Karma returns a breakdown of your karma per subreddit.
func (s *AccountServiceOp) Karma(ctx context.Context) ([]SubredditKarma, *Response, error) {
path := "api/v1/me/karma"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(rootSubredditKarma)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.Data, resp, nil
}
// Settings returns your account settings.
func (s *AccountServiceOp) Settings(ctx context.Context) {
path := "api/v1/me/prefs"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return
}
fmt.Println(req)
root := new(Settings)
fmt.Println(root.ShowThumbnails)
}

32
account_test.go Normal file
View file

@ -0,0 +1,32 @@
package geddit
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
var expectedKarma = []SubredditKarma{
{Subreddit: "nba", PostKarma: 144, CommentKarma: 21999},
{Subreddit: "redditdev", PostKarma: 19, CommentKarma: 4},
{Subreddit: "test", PostKarma: 1, CommentKarma: 0},
{Subreddit: "golang", PostKarma: 1, CommentKarma: 0},
}
func TestAccountServiceOp_Karma(t *testing.T) {
setup()
defer teardown()
blob := readFileContents(t, "testdata/account/karma.json")
mux.HandleFunc("/api/v1/me/karma", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, blob)
})
karma, _, err := client.Account.Karma(ctx)
assert.NoError(t, err)
assert.Equal(t, expectedKarma, karma)
}

View file

@ -91,6 +91,7 @@ type Client struct {
// This is the client's user ID in Reddit's database.
redditID string
Account AccountService
Comment CommentService
Flair FlairService
Listings ListingsService
@ -120,6 +121,7 @@ func newClient(httpClient *http.Client) *Client {
c := &Client{client: httpClient, BaseURL: baseURL, TokenURL: tokenURL}
c.Account = &AccountServiceOp{client: c}
c.Comment = &CommentServiceOp{client: c}
c.Flair = &FlairServiceOp{client: c}
c.Listings = &ListingsServiceOp{client: c}

25
testdata/account/karma.json vendored Normal file
View file

@ -0,0 +1,25 @@
{
"kind": "KarmaList",
"data": [
{
"sr": "nba",
"comment_karma": 21999,
"link_karma": 144
},
{
"sr": "redditdev",
"comment_karma": 4,
"link_karma": 19
},
{
"sr": "test",
"comment_karma": 0,
"link_karma": 1
},
{
"sr": "golang",
"comment_karma": 0,
"link_karma": 1
}
]
}

69
testdata/account/settings.json vendored Normal file
View file

@ -0,0 +1,69 @@
{
"default_theme_sr": null,
"threaded_messages": true,
"hide_downs": false,
"label_nsfw": true,
"activity_relevant_ads": false,
"email_messages": false,
"profile_opt_out": false,
"video_autoplay": true,
"third_party_site_data_personalized_content": false,
"show_link_flair": true,
"show_trending": true,
"private_feeds": true,
"monitor_mentions": true,
"public_server_seconds": false,
"research": false,
"ignore_suggested_sort": true,
"send_crosspost_messages": false,
"email_digests": false,
"layout": 0,
"num_comments": 200,
"clickgadget": true,
"use_global_defaults": false,
"show_snoovatar": false,
"over_18": true,
"show_stylesheets": true,
"live_orangereds": true,
"enable_default_themes": false,
"legacy_search": false,
"domain_details": false,
"collapse_left_bar": false,
"lang": "en-ca",
"hide_ups": false,
"third_party_data_personalized_ads": false,
"allow_clicktracking": false,
"hide_from_robots": false,
"show_twitter": false,
"compress": false,
"store_visits": false,
"threaded_modmail": false,
"design_beta": false,
"min_link_score": null,
"media_preview": "off",
"show_location_based_recommendations": false,
"nightmode": false,
"highlight_controversial": true,
"geopopular": "GLOBAL",
"third_party_site_data_personalized_ads": false,
"survey_last_seen_time": null,
"min_comment_score": null,
"public_votes": false,
"collapse_read_messages": false,
"show_flair": true,
"mark_messages_read": true,
"search_include_over_18": true,
"no_profanity": false,
"hide_ads": false,
"beta": false,
"top_karma_subreddits": false,
"newwindow": true,
"numsites": 25,
"media": "subreddit",
"send_welcome_messages": true,
"show_gold_expiration": false,
"highlight_new_comments": true,
"email_unsubscribe_all": false,
"default_comment_sort": "top",
"accept_pms": "everyone"
}

View file

@ -13,6 +13,7 @@ const (
kindSubreddit = "t5"
kindAward = "t6"
kindListing = "Listing"
kingKarmaList = "KarmaList"
kingTrophyList = "TrophyList"
kindUserList = "UserList"
kindMode = "more"

View file

@ -26,6 +26,7 @@ type UserService interface {
CommentsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Comments, *Response, error)
Saved(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error)
// todo: votes can be public (they're private by default) so make UpvotedOf and DownvotedOf as well
Upvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
Downvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
Hidden(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
@ -36,6 +37,7 @@ type UserService interface {
Unfriend(ctx context.Context, username string) (*Response, error)
Block(ctx context.Context, username string) (*Blocked, *Response, error)
// todo: decide if you wanna keep these
// BlockByID(ctx context.Context, id string) (*Blocked, *Response, error)
Unblock(ctx context.Context, username string) (*Response, error)
// UnblockByID(ctx context.Context, id string) (*Response, error)
@ -108,6 +110,8 @@ type Trophy struct {
// Trophies is a list of trophies.
type Trophies []Trophy
// todo: we don't really need this. just make a rootTrophyListingData struct or something and use the default unmarshaler for that
// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *Trophies) UnmarshalJSON(b []byte) error {
var data map[string]interface{}