packages feed

heddit 0.0.3 → 0.1

raw patch · 29 files changed

+1068/−225 lines, 29 filesdep ~timePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: time

API changes (from Hackage documentation)

- Network.Reddit: InvalidJSON :: JSONError -> APIException
- Network.Reddit: JSONError :: [Text] -> Text -> Text -> Text -> JSONError
- Network.Reddit: data JSONError
- Network.Reddit.Types.Account: Trophy :: Name -> Maybe Text -> Maybe Text -> Maybe Body -> Maybe UTCTime -> Trophy
- Network.Reddit.Types.Account: [$sel:awardID:Trophy] :: Trophy -> Maybe Text
- Network.Reddit.Types.Account: [$sel:description:Trophy] :: Trophy -> Maybe Body
- Network.Reddit.Types.Account: [$sel:grantedAt:Trophy] :: Trophy -> Maybe UTCTime
- Network.Reddit.Types.Account: [$sel:name:Trophy] :: Trophy -> Name
- Network.Reddit.Types.Account: [$sel:trophyID:Trophy] :: Trophy -> Maybe Text
- Network.Reddit.Types.Account: data Trophy
- Network.Reddit.Types.Account: data TrophyList
- Network.Reddit.Types.Account: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Account.Trophy
- Network.Reddit.Types.Account: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Account.TrophyList
- Network.Reddit.Types.Account: instance GHC.Classes.Eq Network.Reddit.Types.Account.Trophy
- Network.Reddit.Types.Account: instance GHC.Generics.Generic Network.Reddit.Types.Account.Trophy
- Network.Reddit.Types.Account: instance GHC.Generics.Generic Network.Reddit.Types.Account.TrophyList
- Network.Reddit.Types.Account: instance GHC.Show.Show Network.Reddit.Types.Account.Trophy
- Network.Reddit.Types.Account: instance GHC.Show.Show Network.Reddit.Types.Account.TrophyList
- Network.Reddit.Types.Internal: InvalidJSON :: JSONError -> APIException
- Network.Reddit.Types.Internal: JSONError :: [Text] -> Text -> Text -> Text -> JSONError
- Network.Reddit.Types.Internal: [$sel:explanation:JSONError] :: JSONError -> Text
- Network.Reddit.Types.Internal: [$sel:fields:JSONError] :: JSONError -> [Text]
- Network.Reddit.Types.Internal: [$sel:message:JSONError] :: JSONError -> Text
- Network.Reddit.Types.Internal: [$sel:reason:JSONError] :: JSONError -> Text
- Network.Reddit.Types.Internal: data JSONError
- Network.Reddit.Types.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Internal.JSONError
- Network.Reddit.Types.Internal: instance GHC.Classes.Eq (Network.Reddit.Types.Internal.ItemOpts a)
- Network.Reddit.Types.Internal: instance GHC.Classes.Eq Network.Reddit.Types.Internal.JSONError
- Network.Reddit.Types.Internal: instance GHC.Generics.Generic (Network.Reddit.Types.Internal.ItemOpts a)
- Network.Reddit.Types.Internal: instance GHC.Generics.Generic Network.Reddit.Types.Internal.JSONError
- Network.Reddit.Types.Internal: instance GHC.Show.Show (Network.Reddit.Types.Internal.ItemOpts a)
- Network.Reddit.Types.Internal: instance GHC.Show.Show Network.Reddit.Types.Internal.JSONError
- Network.Reddit.Types.Internal: instance Web.Internal.FormUrlEncoded.ToForm (Network.Reddit.Types.Internal.ItemOpts a)
- Network.Reddit.Types.Moderation: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Moderation.SubredditType
- Network.Reddit.Types.Moderation: instance GHC.Classes.Eq Network.Reddit.Types.Moderation.SubredditType
- Network.Reddit.Types.Moderation: instance GHC.Generics.Generic Network.Reddit.Types.Moderation.SubredditType
- Network.Reddit.Types.Moderation: instance GHC.Show.Show Network.Reddit.Types.Moderation.SubredditType
- Network.Reddit.Types.Moderation: instance Web.Internal.HttpApiData.ToHttpApiData Network.Reddit.Types.Moderation.SubredditType
- Network.Reddit.User: Trophy :: Name -> Maybe Text -> Maybe Text -> Maybe Body -> Maybe UTCTime -> Trophy
+ Network.Reddit: InsufficientCoins :: ErrorMessage
+ Network.Reddit: InvalidPOST :: POSTError -> APIException
+ Network.Reddit: POSTError :: [Text] -> Text -> Text -> Text -> POSTError
+ Network.Reddit: data POSTError
+ Network.Reddit.Comment: awardComment :: MonadReddit m => Award -> CommentID -> m AwardingsSummary
+ Network.Reddit.Comment: getCommentByURL :: MonadReddit m => URL -> m Comment
+ Network.Reddit.Comment: gildComment :: MonadReddit m => CommentID -> m AwardingsSummary
+ Network.Reddit.Item: Award :: AwardID -> Bool -> Maybe Body -> Award
+ Network.Reddit.Item: AwardID :: Text -> AwardID
+ Network.Reddit.Item: AwardingsSummary :: Seq Awarding -> HashMap AwardID Integer -> Integer -> AwardingsSummary
+ Network.Reddit.Item: award :: MonadReddit m => Award -> ItemID -> m AwardingsSummary
+ Network.Reddit.Item: data Award
+ Network.Reddit.Item: data AwardingsSummary
+ Network.Reddit.Item: gild :: MonadReddit m => ItemID -> m AwardingsSummary
+ Network.Reddit.Item: mkAward :: AwardID -> Award
+ Network.Reddit.Item: newtype AwardID
+ Network.Reddit.Item: pattern Argentium :: AwardID
+ Network.Reddit.Item: pattern Gold :: AwardID
+ Network.Reddit.Item: pattern Platinum :: AwardID
+ Network.Reddit.Item: pattern Silver :: AwardID
+ Network.Reddit.Item: pattern Ternion :: AwardID
+ Network.Reddit.Me: getMySubreddit :: MonadReddit m => m Subreddit
+ Network.Reddit.Me: getMySubredditSettings :: MonadReddit m => m SubredditSettings
+ Network.Reddit.Submission: awardSubmission :: MonadReddit m => Award -> SubmissionID -> m AwardingsSummary
+ Network.Reddit.Submission: gildSubmission :: MonadReddit m => SubmissionID -> m AwardingsSummary
+ Network.Reddit.Types.Account: [$sel:awardeeKarma:Account] :: Account -> Maybe Integer
+ Network.Reddit.Types.Account: [$sel:awarderKarma:Account] :: Account -> Maybe Integer
+ Network.Reddit.Types.Account: [$sel:creddits:Account] :: Account -> Maybe Integer
+ Network.Reddit.Types.Account: [$sel:goldExpiration:Account] :: Account -> Maybe UTCTime
+ Network.Reddit.Types.Account: [$sel:hasGoldSubscription:Account] :: Account -> Maybe Bool
+ Network.Reddit.Types.Account: [$sel:iconImg:Account] :: Account -> URL
+ Network.Reddit.Types.Account: [$sel:isFriend:Account] :: Account -> Bool
+ Network.Reddit.Types.Account: [$sel:isSuspended:Account] :: Account -> Maybe Bool
+ Network.Reddit.Types.Account: [$sel:numFriends:Account] :: Account -> Maybe Integer
+ Network.Reddit.Types.Account: [$sel:subreddit:Account] :: Account -> Subreddit
+ Network.Reddit.Types.Account: [$sel:suspensionExpiration:Account] :: Account -> Maybe UTCTime
+ Network.Reddit.Types.Account: [$sel:totalKarma:Account] :: Account -> Integer
+ Network.Reddit.Types.Account: usernameToDisplayName :: Username -> Text
+ Network.Reddit.Types.Award: Award :: AwardID -> Bool -> Maybe Body -> Award
+ Network.Reddit.Types.Award: AwardID :: Text -> AwardID
+ Network.Reddit.Types.Award: AwardingsSummary :: Seq Awarding -> HashMap AwardID Integer -> Integer -> AwardingsSummary
+ Network.Reddit.Types.Award: Community :: AwardType
+ Network.Reddit.Types.Award: Global :: AwardType
+ Network.Reddit.Types.Award: [$sel:allAwardings:AwardingsSummary] :: AwardingsSummary -> Seq Awarding
+ Network.Reddit.Types.Award: [$sel:awardID:Award] :: Award -> AwardID
+ Network.Reddit.Types.Award: [$sel:coins:AwardingsSummary] :: AwardingsSummary -> Integer
+ Network.Reddit.Types.Award: [$sel:gildings:AwardingsSummary] :: AwardingsSummary -> HashMap AwardID Integer
+ Network.Reddit.Types.Award: [$sel:isAnonymous:Award] :: Award -> Bool
+ Network.Reddit.Types.Award: [$sel:message:Award] :: Award -> Maybe Body
+ Network.Reddit.Types.Award: data Award
+ Network.Reddit.Types.Award: data AwardType
+ Network.Reddit.Types.Award: data Awarding
+ Network.Reddit.Types.Award: data AwardingsSummary
+ Network.Reddit.Types.Award: data Trophy
+ Network.Reddit.Types.Award: data TrophyList
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.AwardType
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.Awarding
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.AwardingsSummary
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.Trophy
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Award.TrophyList
+ Network.Reddit.Types.Award: instance Data.Aeson.Types.FromJSON.FromJSONKey Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance Data.Hashable.Class.Hashable Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.Award
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.AwardType
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.Awarding
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.AwardingsSummary
+ Network.Reddit.Types.Award: instance GHC.Classes.Eq Network.Reddit.Types.Award.Trophy
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.Award
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.AwardType
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.Awarding
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.AwardingsSummary
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.Trophy
+ Network.Reddit.Types.Award: instance GHC.Generics.Generic Network.Reddit.Types.Award.TrophyList
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.Award
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.AwardType
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.Awarding
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.AwardingsSummary
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.Trophy
+ Network.Reddit.Types.Award: instance GHC.Show.Show Network.Reddit.Types.Award.TrophyList
+ Network.Reddit.Types.Award: instance Web.Internal.FormUrlEncoded.ToForm Network.Reddit.Types.Award.Award
+ Network.Reddit.Types.Award: instance Web.Internal.HttpApiData.ToHttpApiData Network.Reddit.Types.Award.AwardID
+ Network.Reddit.Types.Award: mkAward :: AwardID -> Award
+ Network.Reddit.Types.Award: newtype AwardID
+ Network.Reddit.Types.Award: pattern Argentium :: AwardID
+ Network.Reddit.Types.Award: pattern Gold :: AwardID
+ Network.Reddit.Types.Award: pattern Platinum :: AwardID
+ Network.Reddit.Types.Award: pattern Silver :: AwardID
+ Network.Reddit.Types.Award: pattern Ternion :: AwardID
+ Network.Reddit.Types.Award: type TrophyID = Text
+ Network.Reddit.Types.Comment: [$sel:allAwardings:Comment] :: Comment -> Seq Awarding
+ Network.Reddit.Types.Internal: Archived :: SubredditType
+ Network.Reddit.Types.Internal: EmployeesOnly :: SubredditType
+ Network.Reddit.Types.Internal: GoldOnly :: SubredditType
+ Network.Reddit.Types.Internal: GoldRestricted :: SubredditType
+ Network.Reddit.Types.Internal: InsufficientCoins :: ErrorMessage
+ Network.Reddit.Types.Internal: InvalidPOST :: POSTError -> APIException
+ Network.Reddit.Types.Internal: POSTError :: [Text] -> Text -> Text -> Text -> POSTError
+ Network.Reddit.Types.Internal: Private :: SubredditType
+ Network.Reddit.Types.Internal: Public :: SubredditType
+ Network.Reddit.Types.Internal: Restricted :: SubredditType
+ Network.Reddit.Types.Internal: UserSubreddit :: SubredditType
+ Network.Reddit.Types.Internal: [$sel:explanation:POSTError] :: POSTError -> Text
+ Network.Reddit.Types.Internal: [$sel:fields:POSTError] :: POSTError -> [Text]
+ Network.Reddit.Types.Internal: [$sel:message:POSTError] :: POSTError -> Text
+ Network.Reddit.Types.Internal: [$sel:reason:POSTError] :: POSTError -> Text
+ Network.Reddit.Types.Internal: breakOnTypeLenient :: Coercible a Text => Text -> Text -> Parser a
+ Network.Reddit.Types.Internal: data POSTError
+ Network.Reddit.Types.Internal: data SubredditType
+ Network.Reddit.Types.Internal: fromOptional :: (FromJSON (Item b), IsList b, Monoid b) => Object -> Text -> Parser b
+ Network.Reddit.Types.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Internal.POSTError
+ Network.Reddit.Types.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.Reddit.Types.Internal.SubredditType
+ Network.Reddit.Types.Internal: instance GHC.Classes.Eq Network.Reddit.Types.Internal.ItemOpts
+ Network.Reddit.Types.Internal: instance GHC.Classes.Eq Network.Reddit.Types.Internal.POSTError
+ Network.Reddit.Types.Internal: instance GHC.Classes.Eq Network.Reddit.Types.Internal.SubredditType
+ Network.Reddit.Types.Internal: instance GHC.Generics.Generic Network.Reddit.Types.Internal.ItemOpts
+ Network.Reddit.Types.Internal: instance GHC.Generics.Generic Network.Reddit.Types.Internal.POSTError
+ Network.Reddit.Types.Internal: instance GHC.Generics.Generic Network.Reddit.Types.Internal.SubredditType
+ Network.Reddit.Types.Internal: instance GHC.Show.Show Network.Reddit.Types.Internal.ItemOpts
+ Network.Reddit.Types.Internal: instance GHC.Show.Show Network.Reddit.Types.Internal.POSTError
+ Network.Reddit.Types.Internal: instance GHC.Show.Show Network.Reddit.Types.Internal.SubredditType
+ Network.Reddit.Types.Internal: instance Web.Internal.FormUrlEncoded.ToForm Network.Reddit.Types.Internal.ItemOpts
+ Network.Reddit.Types.Internal: instance Web.Internal.HttpApiData.ToHttpApiData Network.Reddit.Types.Internal.SubredditType
+ Network.Reddit.Types.Submission: [$sel:allAwardings:Submission] :: Submission -> Seq Awarding
+ Network.Reddit.Types.Subreddit: [$sel:allowImages:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:allowPolls:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:allowVideoGIFs:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:allowVideos:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:bannerImg:Subreddit] :: Subreddit -> Maybe URL
+ Network.Reddit.Types.Subreddit: [$sel:bannerSize:Subreddit] :: Subreddit -> Maybe (Int, Int)
+ Network.Reddit.Types.Subreddit: [$sel:freeFormReports:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:headerImg:Subreddit] :: Subreddit -> Maybe URL
+ Network.Reddit.Types.Subreddit: [$sel:headerSize:Subreddit] :: Subreddit -> Maybe (Int, Int)
+ Network.Reddit.Types.Subreddit: [$sel:iconImg:Subreddit] :: Subreddit -> Maybe URL
+ Network.Reddit.Types.Subreddit: [$sel:keyColor:Subreddit] :: Subreddit -> Maybe RGBText
+ Network.Reddit.Types.Subreddit: [$sel:linkFlairEnabled:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:restrictCommenting:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:restrictPosting:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:spoilersEnabled:Subreddit] :: Subreddit -> Maybe Bool
+ Network.Reddit.Types.Subreddit: [$sel:submitText:Subreddit] :: Subreddit -> Maybe Text
+ Network.Reddit.Types.Subreddit: [$sel:submitTextHTML:Subreddit] :: Subreddit -> Maybe Text
+ Network.Reddit.Types.Subreddit: [$sel:submitTextLabel:Subreddit] :: Subreddit -> Maybe Text
+ Network.Reddit.Types.Subreddit: [$sel:subredditType:Subreddit] :: Subreddit -> SubredditType
+ Network.Reddit.Types.Subreddit: [$sel:userIsContributor:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.Types.Subreddit: [$sel:userIsMuted:Subreddit] :: Subreddit -> Bool
+ Network.Reddit.User: gildUser :: MonadReddit m => Maybe Word -> Username -> m ()
- Network.Reddit: ItemOpts :: Maybe ItemSort -> Maybe ItemType -> Maybe Time -> Maybe Word -> ItemOpts a
+ Network.Reddit: ItemOpts :: Maybe ItemSort -> Maybe ItemType -> Maybe Time -> Maybe Word -> ItemOpts
- Network.Reddit: data ItemOpts a
+ Network.Reddit: data ItemOpts
- Network.Reddit: defaultItemOpts :: ItemOpts a
+ Network.Reddit: defaultItemOpts :: ItemOpts
- Network.Reddit.Comment: Comment :: CommentID -> Username -> Body -> Body -> Seq ChildComment -> Maybe Integer -> Maybe Integer -> Maybe Integer -> UTCTime -> Maybe UTCTime -> SubredditName -> SubredditID -> Int -> Maybe Bool -> SubmissionID -> Maybe URL -> Maybe Username -> URL -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Comment
+ Network.Reddit.Comment: Comment :: CommentID -> Username -> Body -> Body -> Seq ChildComment -> Maybe Integer -> Maybe Integer -> Maybe Integer -> UTCTime -> Maybe UTCTime -> SubredditName -> SubredditID -> Int -> Maybe Bool -> SubmissionID -> Maybe URL -> Maybe Username -> URL -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Seq Awarding -> Comment
- Network.Reddit.Comment: getComments :: (MonadReddit m, Foldable t) => ItemOpts Comment -> t CommentID -> m (Seq Comment)
+ Network.Reddit.Comment: getComments :: (MonadReddit m, Foldable t) => ItemOpts -> t CommentID -> m (Seq Comment)
- Network.Reddit.Comment: loadMoreComments :: forall m. MonadReddit m => Maybe Word -> ItemOpts Comment -> SubmissionID -> MoreComments -> m (Seq ChildComment)
+ Network.Reddit.Comment: loadMoreComments :: forall m. MonadReddit m => Maybe Word -> ItemOpts -> SubmissionID -> MoreComments -> m (Seq ChildComment)
- Network.Reddit.Comment: withReplies :: MonadReddit m => ItemOpts a -> Comment -> m Comment
+ Network.Reddit.Comment: withReplies :: MonadReddit m => ItemOpts -> Comment -> m Comment
- Network.Reddit.Live: LiveContributor :: UserID -> Username -> Seq LivePermission -> LiveContributor
+ Network.Reddit.Live: LiveContributor :: UserID -> Username -> [LivePermission] -> LiveContributor
- Network.Reddit.Moderation: ModAccount :: Username -> UserID -> RelID -> Maybe FlairText -> Maybe CSSClass -> UTCTime -> Maybe (Seq ModPermission) -> ModAccount
+ Network.Reddit.Moderation: ModAccount :: Username -> UserID -> RelID -> Maybe FlairText -> Maybe CSSClass -> UTCTime -> Maybe [ModPermission] -> ModAccount
- Network.Reddit.Submission: Submission :: SubmissionID -> Title -> Username -> SubmissionContent -> SubredditName -> UTCTime -> Maybe UTCTime -> URL -> Domain -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Maybe Rational -> Integer -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe PollData -> Submission
+ Network.Reddit.Submission: Submission :: SubmissionID -> Title -> Username -> SubmissionContent -> SubredditName -> UTCTime -> Maybe UTCTime -> URL -> Domain -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Maybe Rational -> Integer -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe PollData -> Seq Awarding -> Submission
- Network.Reddit.Submission: getSubmissions :: (MonadReddit m, Foldable t) => ItemOpts Submission -> t SubmissionID -> m (Seq Submission)
+ Network.Reddit.Submission: getSubmissions :: (MonadReddit m, Foldable t) => ItemOpts -> t SubmissionID -> m (Seq Submission)
- Network.Reddit.Subreddit: Subreddit :: SubredditID -> SubredditName -> Title -> UTCTime -> Body -> Maybe Body -> Body -> Integer -> Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Bool -> Subreddit
+ Network.Reddit.Subreddit: Subreddit :: SubredditID -> SubredditName -> Title -> UTCTime -> SubredditType -> Integer -> Body -> Maybe RGBText -> Body -> Maybe Body -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe URL -> Maybe URL -> Maybe (Int, Int) -> Maybe URL -> Maybe (Int, Int) -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Subreddit
- Network.Reddit.Types.Account: Account :: UserID -> Username -> UTCTime -> Int -> Bool -> Bool -> Int -> Maybe Integer -> Maybe Text -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Account
+ Network.Reddit.Types.Account: Account :: UserID -> Username -> UTCTime -> Integer -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Bool -> Bool -> Bool -> URL -> Subreddit -> Maybe Integer -> Maybe Integer -> Maybe Integer -> Maybe Text -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe UTCTime -> Maybe Bool -> Maybe UTCTime -> Account
- Network.Reddit.Types.Account: [$sel:commentKarma:Account] :: Account -> Int
+ Network.Reddit.Types.Account: [$sel:commentKarma:Account] :: Account -> Integer
- Network.Reddit.Types.Account: [$sel:linkKarma:Account] :: Account -> Int
+ Network.Reddit.Types.Account: [$sel:linkKarma:Account] :: Account -> Integer
- Network.Reddit.Types.Comment: Comment :: CommentID -> Username -> Body -> Body -> Seq ChildComment -> Maybe Integer -> Maybe Integer -> Maybe Integer -> UTCTime -> Maybe UTCTime -> SubredditName -> SubredditID -> Int -> Maybe Bool -> SubmissionID -> Maybe URL -> Maybe Username -> URL -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Comment
+ Network.Reddit.Types.Comment: Comment :: CommentID -> Username -> Body -> Body -> Seq ChildComment -> Maybe Integer -> Maybe Integer -> Maybe Integer -> UTCTime -> Maybe UTCTime -> SubredditName -> SubredditID -> Int -> Maybe Bool -> SubmissionID -> Maybe URL -> Maybe Username -> URL -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Seq Awarding -> Comment
- Network.Reddit.Types.Internal: ItemOpts :: Maybe ItemSort -> Maybe ItemType -> Maybe Time -> Maybe Word -> ItemOpts a
+ Network.Reddit.Types.Internal: ItemOpts :: Maybe ItemSort -> Maybe ItemType -> Maybe Time -> Maybe Word -> ItemOpts
- Network.Reddit.Types.Internal: [$sel:context:ItemOpts] :: ItemOpts a -> Maybe Word
+ Network.Reddit.Types.Internal: [$sel:context:ItemOpts] :: ItemOpts -> Maybe Word
- Network.Reddit.Types.Internal: [$sel:itemSort:ItemOpts] :: ItemOpts a -> Maybe ItemSort
+ Network.Reddit.Types.Internal: [$sel:itemSort:ItemOpts] :: ItemOpts -> Maybe ItemSort
- Network.Reddit.Types.Internal: [$sel:itemTime:ItemOpts] :: ItemOpts a -> Maybe Time
+ Network.Reddit.Types.Internal: [$sel:itemTime:ItemOpts] :: ItemOpts -> Maybe Time
- Network.Reddit.Types.Internal: [$sel:itemType:ItemOpts] :: ItemOpts a -> Maybe ItemType
+ Network.Reddit.Types.Internal: [$sel:itemType:ItemOpts] :: ItemOpts -> Maybe ItemType
- Network.Reddit.Types.Internal: data ItemOpts a
+ Network.Reddit.Types.Internal: data ItemOpts
- Network.Reddit.Types.Internal: defaultItemOpts :: ItemOpts a
+ Network.Reddit.Types.Internal: defaultItemOpts :: ItemOpts
- Network.Reddit.Types.Live: LiveContributor :: UserID -> Username -> Seq LivePermission -> LiveContributor
+ Network.Reddit.Types.Live: LiveContributor :: UserID -> Username -> [LivePermission] -> LiveContributor
- Network.Reddit.Types.Live: [$sel:permissions:LiveContributor] :: LiveContributor -> Seq LivePermission
+ Network.Reddit.Types.Live: [$sel:permissions:LiveContributor] :: LiveContributor -> [LivePermission]
- Network.Reddit.Types.Moderation: ModAccount :: Username -> UserID -> RelID -> Maybe FlairText -> Maybe CSSClass -> UTCTime -> Maybe (Seq ModPermission) -> ModAccount
+ Network.Reddit.Types.Moderation: ModAccount :: Username -> UserID -> RelID -> Maybe FlairText -> Maybe CSSClass -> UTCTime -> Maybe [ModPermission] -> ModAccount
- Network.Reddit.Types.Moderation: [$sel:permissions:ModAccount] :: ModAccount -> Maybe (Seq ModPermission)
+ Network.Reddit.Types.Moderation: [$sel:permissions:ModAccount] :: ModAccount -> Maybe [ModPermission]
- Network.Reddit.Types.Submission: Submission :: SubmissionID -> Title -> Username -> SubmissionContent -> SubredditName -> UTCTime -> Maybe UTCTime -> URL -> Domain -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Maybe Rational -> Integer -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe PollData -> Submission
+ Network.Reddit.Types.Submission: Submission :: SubmissionID -> Title -> Username -> SubmissionContent -> SubredditName -> UTCTime -> Maybe UTCTime -> URL -> Domain -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Maybe Rational -> Integer -> Seq ItemReport -> Seq ItemReport -> Maybe Integer -> Maybe Distinction -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe PollData -> Seq Awarding -> Submission
- Network.Reddit.Types.Subreddit: Subreddit :: SubredditID -> SubredditName -> Title -> UTCTime -> Body -> Maybe Body -> Body -> Integer -> Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Bool -> Subreddit
+ Network.Reddit.Types.Subreddit: Subreddit :: SubredditID -> SubredditName -> Title -> UTCTime -> SubredditType -> Integer -> Body -> Maybe RGBText -> Body -> Maybe Body -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe URL -> Maybe URL -> Maybe (Int, Int) -> Maybe URL -> Maybe (Int, Int) -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Subreddit
- Network.Reddit.Types.Subreddit: [$sel:userIsBanned:Subreddit] :: Subreddit -> Maybe Bool
+ Network.Reddit.Types.Subreddit: [$sel:userIsBanned:Subreddit] :: Subreddit -> Bool
- Network.Reddit.Types.Subreddit: [$sel:userIsModerator:Subreddit] :: Subreddit -> Maybe Bool
+ Network.Reddit.Types.Subreddit: [$sel:userIsModerator:Subreddit] :: Subreddit -> Bool
- Network.Reddit.Types.Subreddit: [$sel:userIsSubscriber:Subreddit] :: Subreddit -> Maybe Bool
+ Network.Reddit.Types.Subreddit: [$sel:userIsSubscriber:Subreddit] :: Subreddit -> Bool
- Network.Reddit.User: Account :: UserID -> Username -> UTCTime -> Int -> Bool -> Bool -> Int -> Maybe Integer -> Maybe Text -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Account
+ Network.Reddit.User: Account :: UserID -> Username -> UTCTime -> Integer -> Integer -> Integer -> Maybe Integer -> Maybe Integer -> Bool -> Bool -> Bool -> URL -> Subreddit -> Maybe Integer -> Maybe Integer -> Maybe Integer -> Maybe Text -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe UTCTime -> Maybe Bool -> Maybe UTCTime -> Account

Files

CHANGELOG.org view
@@ -1,10 +1,22 @@ #+TITLE: CHANGELOG +* 0.1++ Add actions for awarding/gilding items and gilding users++ New module ~Network.Reddit.Types.Award~ exports ~Award~, ~AwardID~, ~Awarding~, and ~AwardingsSummary~ types; ~Trophy~ moved to this module as well++ Add ~allAwardings~ field to ~Submission~ and ~Comment~++ Document [[file:./doc/awards.org][a big list of different awards]]++ Add ~InsufficientCoins~ to ~ErrorMessage~++ Add ~getCommentByURL~++ Rename ~JSONError~ to ~POSTError~++ Remove redundant phantom type parameter from ~ItemOpts~++ Added several more fields to ~Account~ and ~Subreddit~++ ~Subredddit~ now supports the special user subreddit; the actions ~getMySubreddit~ and ~getMySubredditSettings~ were added for convenience+ * 0.0.3 + Fixes to cabal release  * 0.0.2-+ Add more documentation and examples programs++ Add more documentation and example programs + Export ~Paginable~ class from ~Reddit.hs~ + ~firstPage~ now gets the API limit of items (100) 
README.org view
@@ -99,6 +99,7 @@ - It is currently alpha quality. It needs more tests and users to work out some of the bugs that are assuredly lurking deep within. The exposed API is a little weird and may be difficult to work with. - The Reddit API seems overwhemlingly geared towards dynamic representations of data. This can cause obvious issues when decoding its JSON responses in Haskell. If an exception is thrown when trying to hit an endpoint, please let me know by opening an issue or PR! - ~heddit~ only supports authenticating via OAuth (see [[file:./doc/auth.org][Authenticating]]) and cannot access Reddit's old JSON API. This means that it is not possible to create a truly anonymous client. Instead, you must register all applications, even those without a user context.+- At the moment, it doesn't build on GHC < 8.8.3, so if you are working with an older GHC version, ~heddit~ will not be suitable.  * Examples Here are some executable examples using different features from ~heddit~ that hopefully illustrate how to use the library and accomplish different tasks. At the moment, the available example programs are:
+ doc/awards.org view
@@ -0,0 +1,106 @@+#+TITLE: Global Reddit Awards+#+STARTUP: shrink+#+STARTUP: align++Here is a list of global Reddit awards, adapted from the [[https://github.com/praw-dev/praw/blob/c818949c848f4520df08b16c098f80a41e897ab5/docs/code_overview/models/awards.txt][praw docs]]+++| Name                       | ID                                   |  Cost | Icon                                                                                                                                                                        |+|----------------------------+--------------------------------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|+| Silver                     | gid_1                                |   100 | https://www.redditstatic.com/gold/awards/icon/silver_64.png                                                                                                                 |+| Gold                       | gid_2                                |   500 | https://www.redditstatic.com/gold/awards/icon/gold_64.png                                                                                                                   |+| Platinum                   | gid_3                                |  1800 | https://www.redditstatic.com/gold/awards/icon/platinum_64.png                                                                                                               |+| LOVE                       | 5eac457f-ebac-449b-93a7-eb17b557f03c |    20 | https://preview.redd.it/award_images/t5_22cerq/j3azv69qjfn51_LOVE.png?width=64&height=64&crop=smart&auto=webp&s=405d338bf34818596ab84bc26a09316917def8bb                    |+| Starstruck                 | abb865cf-620b-4219-8777-3658cf9091fb |    20 | https://www.redditstatic.com/gold/awards/icon/Starstruck_64.png                                                                                                             |+| All-Seeing Upvote          | b4ff447e-05a5-42dc-9002-63568807cfe6 |    30 | https://preview.redd.it/award_images/t5_22cerq/rg960rc47jj41_All-SeeingUpvote.png?width=64&height=64&crop=smart&auto=webp&s=46a9f176646afded7050299a1fbad5c66e6ed1e6        |+| Narwhal Salute             | a2506925-fc82-4d6c-ae3b-b7217e09d7f0 |    30 | https://preview.redd.it/award_images/t5_22cerq/80j20o397jj41_NarwhalSalute.png?width=64&height=64&crop=smart&auto=webp&s=8629e84ef3188ad75ebca713afb4a0f97e4c3c70           |+| Wholesome Seal of Approval | c4b2e438-16bb-4568-88e7-7893b7662944 |    30 | https://preview.redd.it/award_images/t5_22cerq/b9ks3a5k7jj41_WholesomeSealofApproval.png?width=64&height=64&crop=smart&auto=webp&s=2bcedd41a51b17ff305fc0ec6eb7c5a631a90c60 |+| Ally                       | 69c94eb4-d6a3-48e7-9cf2-0f39fed8b87c |    50 | https://preview.redd.it/award_images/t5_22cerq/5nswjpyy44551_Ally.png?width=64&height=64&crop=smart&auto=webp&s=655fb8310589a3964a82ac0e56d016eb97aa6d4d                    |+| I am disappoint            | 03c4f93d-efc7-463b-98a7-c01814462ab0 |    50 | https://preview.redd.it/award_images/t5_22cerq/3ekkailk5s551_Iamdisappoint.png?width=64&height=64&crop=smart&auto=webp&s=3aa59d2aa096aba2d6cba9709f79b788676e3731           |+| Looking Busy               | d33fddd7-a58a-4472-b1a2-3157d8c8b76f |    50 | https://i.redd.it/award_images/t5_22cerq/k0qzautvyyk51_LookingBusy.png                                                                                                      |+| Take My Energy             | 02d9ab2c-162e-4c01-8438-317a016ed3d9 |    50 | https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=64&height=64&crop=smart&auto=webp&s=e62f7cf1108ff17381b1ed42b1edca26f721dce0            |+| Wearing is Caring          | 80d4d339-95d0-43ac-b051-bc3fe0a9bab8 |    50 | https://preview.redd.it/award_images/t5_22cerq/lcswc5d07hb51_WearingisCaring.png?width=64&height=64&crop=smart&auto=webp&s=50da94b41c2b6c8bbe2723243ed0985059bba9fa         |+| Facepalm                   | b1b44fa1-8179-4d84-a9ed-f25bb81f1c5f |    70 | https://preview.redd.it/award_images/t5_22cerq/ey2iodron2s41_Facepalm.png?width=64&height=64&crop=smart&auto=webp&s=9d3c8e8642dc97c655867ea5ba37b7d76d653203                |+| Faith In Humanity Restored | 7becef23-fb0b-4d62-b8a6-01d5759367cb |    70 | https://preview.redd.it/award_images/t5_22cerq/gva4vt20qc751_FaithInHumanityRestored.png?width=64&height=64&crop=smart&auto=webp&s=a76d915a6eb1cd6d5efa2e0280d90740d2f74de9 |+| Snek                       | 99d95969-6100-45b2-b00c-0ec45ae19596 |    70 | https://preview.redd.it/award_images/t5_22cerq/rc5iesz2z8t41_Snek.png?width=64&height=64&crop=smart&auto=webp&s=2856d19d59c0af0d38fd0b6058622f6a1e91956a                    |+| Tree Hug                   | b92370bb-b7de-4fb3-9608-c5b4a22f714a |    70 | https://preview.redd.it/award_images/t5_22cerq/fukjtec638u41_TreeHug.png?width=64&height=64&crop=smart&auto=webp&s=b46d14cae99b5519775167d0aea8418c480c28c0                 |+| Bravo Grande!              | 9663243a-e77f-44cf-abc6-850ead2cd18d |    75 | https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_64.png                                                                                                    |+| Party Train                | 75f9bc56-eba3-4988-a1af-aec974404a0b |    75 | https://www.redditstatic.com/gold/awards/icon/Train/Train_silver_64.png                                                                                                     |+| Take My Power              | 92cb6518-a71a-4217-9f8f-7ecbd7ab12ba |    75 | https://www.redditstatic.com/gold/awards/icon/TakeMyPower_64.png                                                                                                            |+| Hugz                       | 8352bdff-3e03-4189-8a08-82501dd8f835 |    80 | https://preview.redd.it/award_images/t5_22cerq/niiatoknifn51_Hugz.png?width=64&height=64&crop=smart&auto=webp&s=840debef89811f54b3f1b498bddf55c118eb62cb                    |+| 'MURICA                    | 869d4135-8738-41e5-8630-de593b4f049f |   100 | https://preview.redd.it/award_images/t5_22cerq/18mwqw5th9e51_MURICA.png?width=64&height=64&crop=smart&auto=webp&s=759d235c371d6ae8ad5efb1c98a74b259a3ff8fe                  |+| Cosplay Famous             | 6f0a496f-c3e2-484c-90d0-d26bffb2e286 |   100 | https://preview.redd.it/award_images/t5_22cerq/7ptj05lgqys51_CosplayFamous.png?width=64&height=64&crop=smart&auto=webp&s=405250108b91c08113cd9c38b4d872d9b521d116           |+| Dread                      | 81cf5c92-8500-498c-9c94-3e4034cece0a |   100 | https://preview.redd.it/award_images/t5_22cerq/nvfe4gyawnf51_Dread.png?width=64&height=64&crop=smart&auto=webp&s=34247d8bba8dc5aa7eaed6e81135da0c935d37f3                   |+| Evil Cackle                | 483d8e29-bbe5-404e-a09a-c2d7b16c4fff |   100 | https://preview.redd.it/award_images/t5_22cerq/43zl6dfcg9e51_EvilCackle.png?width=64&height=64&crop=smart&auto=webp&s=bd7d6c66b7594ac91ff2d8a7d86340a0f49a153c              |+| Excited                    | 74fe5152-7906-4991-9016-bc2d8e261200 |   100 | https://preview.redd.it/award_images/t5_22cerq/x069ow7ewnf51_Excited.png?width=64&height=64&crop=smart&auto=webp&s=b1d186a93a86d3bae5ad3bee36e41a9e93d35281                 |+| Glow Up                    | 01178870-6a4f-4172-8f36-9ed5092ee4f9 |   100 | https://preview.redd.it/award_images/t5_22cerq/2754pa5jvsj51_GlowUp.png?width=64&height=64&crop=smart&auto=webp&s=60f710eccc52d9d05db204bb2b962372a1e45671                  |+| Heartwarming               | 19860e30-3331-4bac-b3d1-bd28de0c7974 |   100 | https://preview.redd.it/award_images/t5_22cerq/v1mxw8i6wnf51_Heartwarming.png?width=64&height=64&crop=smart&auto=webp&s=c09f506dc0be92c9849ee7dd2cfe9d65852d6064            |+| I'll Drink to That         | 3267ca1c-127a-49e9-9a3d-4ba96224af18 |   100 | https://preview.redd.it/award_images/t5_22cerq/45aeu8mzvsj51_IllDrinktoThat.png?width=64&height=64&crop=smart&auto=webp&s=94580e58d47d0a1eb1c6cc776437c574b5bbad7c          |+| Keep Calm                  | 1da6ff27-7c0d-4524-9954-86e5cda5fcac |   100 | https://preview.redd.it/award_images/t5_22cerq/g77c4oud7hb51_KeepCalm.png?width=64&height=64&crop=smart&auto=webp&s=9510d2496464d0fa8ff2288e1f0e17f114226383                |+| Kiss                       | 1e516e18-cbee-4668-b338-32d5530f91fe |   100 | https://preview.redd.it/award_images/t5_22cerq/sb42u5gmwsj51_Kiss.png?width=64&height=64&crop=smart&auto=webp&s=12e56d5bba8f00fe467d3efe0c962e70bcbc92ed                    |+| Last Minute Costume        | a0c3e268-87e7-4918-9a36-f6aa462e4dee |   100 | https://preview.redd.it/award_images/t5_22cerq/icn71p34nct51_LastMinuteCostume.png?width=64&height=64&crop=smart&auto=webp&s=f6865e93870b4386ab4a7c32662200b961870d22       |+| Lawyer Up                  | ae89e420-c4a5-47b8-a007-5dacf1c0f0d4 |   100 | https://preview.redd.it/award_images/t5_22cerq/iq0sgwn5bzy41_LawyerUp.png?width=64&height=64&crop=smart&auto=webp&s=3baf45de6ba3e1535df885b91a2aa714e5ece9d5                |+| Masterpiece                | b4072731-c0fb-4440-adc7-1063d6a5e6a0 |   100 | https://preview.redd.it/award_images/t5_22cerq/2juh333m40n51_Masterpiece.png?width=64&height=64&crop=smart&auto=webp&s=0183ad39a56ba3e46c4bcb5d8938392f75d5d9d5             |+| Shocked                    | fbe9527a-adb3-430e-af1a-5fd3489e641b |   100 | https://preview.redd.it/award_images/t5_22cerq/fck3iedi2ug51_Shocked.png?width=64&height=64&crop=smart&auto=webp&s=627fbd255cb9cce5b72551255cb061bf0c8af71a                 |+| Spacefaring Snoo           | a3df1615-dcf8-4f5f-ac7c-3c2be31332a7 |   100 | https://preview.redd.it/award_images/t5_22cerq/0dy3wj06qys51_SpacefaringSnoo.png?width=64&height=64&crop=smart&auto=webp&s=fb6e1c4f29f00c5229c58b769f788842f4bcb85d         |+| Sweet Tooth                | bd6ccb1d-118a-462a-a451-f550cd3133d2 |   100 | https://preview.redd.it/award_images/t5_22cerq/gbsaculna3u51_SweetTooth.png?width=64&height=64&crop=smart&auto=webp&s=5ce49bdf63d850847bb4dfced763fb80a8a99024              |+| Tearing Up                 | 43f3bf99-92d6-47ab-8205-130d26e7929f |   100 | https://preview.redd.it/award_images/t5_22cerq/lop66ut2wnf51_TearingUp.png?width=64&height=64&crop=smart&auto=webp&s=a6ee8b3c876bdc7a022072134f8157a7ea85d884               |+| Yummy                      | ae7f17fb-6538-4c75-9ff4-5f48b4cdaa94 |   100 | https://preview.redd.it/award_images/t5_22cerq/a7dhg27hvnf51_Yummy.png?width=64&height=64&crop=smart&auto=webp&s=7a70c563dbc8bb2659271ddd451c8e657f3a3aa8                   |+| Wholesome                  | 5f123e3d-4f48-42f4-9c11-e98b566d5897 |   125 | https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=64&height=64&crop=smart&auto=webp&s=89a077c4955b50fa2e57449eebf8c19e7f23ceb7               |+| Bless Up                   | 77ba55a2-c33c-4351-ac49-807455a80148 |   150 | https://preview.redd.it/award_images/t5_22cerq/trfv6ems1md41_BlessUp.png?width=64&height=64&crop=smart&auto=webp&s=5e84fe0dae062adff22f01c384d86c0633d06b28                 |+| Buff Doge                  | c42dc561-0b41-40b6-a23d-ef7e110e739e |   150 | https://preview.redd.it/award_images/t5_22cerq/zc4a9vk5zmc51_BuffDoge.png?width=64&height=64&crop=smart&auto=webp&s=04b5cf5717304307515af46c73431f11bb66c3bd                |+| Cake                       | 5fb42699-4911-42a2-884c-6fc8bdc36059 |   150 | https://preview.redd.it/award_images/t5_22cerq/aoa99054n2s41_Cake.png?width=64&height=64&crop=smart&auto=webp&s=a1564ae45fe22bcd07a9a98d0b4c321c497ba194                    |+| Helpful                    | f44611f1-b89e-46dc-97fe-892280b13b82 |   150 | https://preview.redd.it/award_images/t5_22cerq/klvxk1wggfd41_Helpful.png?width=64&height=64&crop=smart&auto=webp&s=cc59d3722210878a975f8d97eb62f3092e472693                 |+| Press F                    | 88fdcafc-57a0-48db-99cc-76276bfaf28b |   150 | https://preview.redd.it/award_images/t5_22cerq/tcofsbf92md41_PressF.png?width=64&height=64&crop=smart&auto=webp&s=b33bda0aa0310bbe1877ba2d02a259d7cc52d616                  |+| Take My Money              | a7f9cbd7-c0f1-4569-a913-ebf8d18de00b |   150 | https://preview.redd.it/award_images/t5_22cerq/9jr8pv84v7i51_TakeMyMoney.png?width=64&height=64&crop=smart&auto=webp&s=8cae8bb3d91b20e0d13b24010a103842e365ff9f             |+| Giggle                     | e813313c-1002-49bf-ac37-e966710f605f |   200 | https://www.redditstatic.com/gold/awards/icon/Giggle_64.png                                                                                                                 |+| Got the W                  | 8dc476c7-1478-4d41-b940-f139e58f7756 |   200 | https://preview.redd.it/award_images/t5_22cerq/9avdcwgupta41_GottheW.png?width=64&height=64&crop=smart&auto=webp&s=d7c1acaee0770a6a0ff7853606ba9992824cdcec                 |+| I'd Like to Thank...       | 1703f934-cf44-40cc-a96d-3729d0b48262 |   200 | https://preview.redd.it/award_images/t5_22cerq/8ad2jffnclf41_Thanks.png?width=64&height=64&crop=smart&auto=webp&s=430fa4cf2928cc37ed0a3344c8da31277a53929c                  |+| I'm Deceased               | b28d9565-4137-433d-bb65-5d4aa82ade4c |   200 | https://preview.redd.it/award_images/t5_22cerq/2jd92wtn25g41_ImDeceased.png?width=64&height=64&crop=smart&auto=webp&s=106ba80d6001af5b5b47b874b4f86da6ca88113f              |+| Looking                    | 4922c1be-3646-4d62-96ea-19a56798df51 |   200 | https://preview.redd.it/award_images/t5_22cerq/kjpl76213ug51_Looking.png?width=64&height=64&crop=smart&auto=webp&s=5d6beac8304bc8fe28e0d6acb1b4b1339abecc67                 |+| Plus One                   | f7562045-905d-413e-9ed2-0a16d4bfe349 |   200 | https://preview.redd.it/award_images/t5_22cerq/6vgr8y21i9741_PlusOne.png?width=64&height=64&crop=smart&auto=webp&s=ecea37cb71525e80d9ca2dbf2bd504b9f934606e                 |+| Stonks Falling             | 9ee30a8f-463e-4ef7-9da9-a09f270ec026 |   200 | https://preview.redd.it/award_images/t5_22cerq/ree13odobef41_StonksFalling.png?width=64&height=64&crop=smart&auto=webp&s=b1016427b3550d229b4ec93437ac8b69c28c5ae2           |+| Stonks Rising              | d125d124-5c03-490d-af3d-d07c462003da |   200 | https://preview.redd.it/award_images/t5_22cerq/s5edqq9abef41_StonksRising.png?width=64&height=64&crop=smart&auto=webp&s=8c11db37505feb2a3e4130eb15e9470e72e65b63            |+| This is 2020               | dc391ef9-0df8-468f-bd3c-7b177092de35 |   200 | https://preview.redd.it/award_images/t5_22cerq/ncon692ev7i51_Thisis2020.png?width=64&height=64&crop=smart&auto=webp&s=eb415a44432807861d7bac64d9e26c5c6ea194bb              |+| 1UP                        | 11be92ba-509e-46d3-991b-593239006521 |   250 | https://www.redditstatic.com/gold/awards/icon/Levelup_64.png                                                                                                                |+| Awesome Answer             | 2adc49e8-d6c9-4923-9293-2bfab1648569 |   250 | https://preview.redd.it/award_images/t5_22cerq/71v56o5a5v541_AwesomeAnswer.png?width=64&height=64&crop=smart&auto=webp&s=a6c7ee017a2cbab3e291b81823eb179faeb96b99           |+| It's Cute!                 | cc540de7-dfdb-4a68-9acf-6f9ce6b17d21 |   250 | https://preview.redd.it/award_images/t5_22cerq/n94bgm83in941_ItsCute.png?width=64&height=64&crop=smart&auto=webp&s=2632d9c9236d0501427072373b789fdb5a59377a                 |+| Mind Blown                 | 9583d210-a7d0-4f3c-b0c7-369ad579d3d4 |   250 | https://preview.redd.it/award_images/t5_22cerq/wa987k0p4v541_MindBlown.png?width=64&height=64&crop=smart&auto=webp&s=5bcf973d6d2b64223cc5a2995c95b797bcf68dd2               |+| Original                   | d306c865-0d49-4a36-a1ab-a4122a0e3480 |   250 | https://preview.redd.it/award_images/t5_22cerq/b8xt4z8yajz31_Original.png?width=64&height=64&crop=smart&auto=webp&s=8ca70e1f45c2ddca559d456a74798fcedf36427e                |+| Timeless Beauty            | 31260000-2f4a-4b40-ad20-f5aa46a577bf |   250 | https://www.redditstatic.com/gold/awards/icon/Timeless_512.png                                                                                                              |+| Today I Learned            | a67d649d-5aa5-407e-a98b-32fd9e3a9696 |   250 | https://preview.redd.it/award_images/t5_22cerq/bph2png4ajz31_TodayILearned.png?width=64&height=64&crop=smart&auto=webp&s=0dec60a8d3226978d4b824226b0c864b663293b3           |+| Yas Queen                  | d48aad4b-286f-4a3a-bb41-ec05b3cd87cc |   250 | https://preview.redd.it/award_images/t5_22cerq/kthj3e4h3bm41_YasQueen.png?width=64&height=64&crop=smart&auto=webp&s=98b385524aee26412e9dcf25bd20b4cff0eb2376                |+| Coin Gift                  | 3dd248bc-3438-4c5b-98d4-24421fd6d670 |   300 | https://preview.redd.it/award_images/t5_22cerq/cr1mq4yysv541_CoinGift.png?width=64&height=64&crop=smart&auto=webp&s=d39d0850925a193c9f2ca8203243c694d15fb40a                |+| Crab Rave                  | f7a4fd5e-7cd1-4c11-a1c9-c18d05902e81 |   300 | https://www.redditstatic.com/gold/awards/icon/CrabRave_64.png                                                                                                               |+| Frankensnoo                | aef30fbe-92e4-4593-8aa7-4b82cfe8d172 |   300 | https://www.redditstatic.com/gold/awards/icon/Electric_64.png                                                                                                               |+| GOAT                       | cc299d65-77de-4828-89de-708b088349a0 |   300 | https://preview.redd.it/award_images/t5_22cerq/x52x5be57fd41_GOAT.png?width=64&height=64&crop=smart&auto=webp&s=d8a0ff8871eeebadc700284d0186073b8e56ac58                    |+| Rocket Like                | 28e8196b-d4e9-45bc-b612-cd4c7d3ed4b3 |   300 | https://preview.redd.it/award_images/t5_22cerq/94pn64yuas941_RocketLike.png?width=64&height=64&crop=smart&auto=webp&s=381ec4bd15ab6029d5009a180f749093de815e60              |+| Spooky Season              | 176a3f8a-2229-4a12-bcdc-86464cfd6dc1 |   300 | https://www.redditstatic.com/gold/awards/icon/Spooky_64.png                                                                                                                 |+| Table Flip                 | 3e000ecb-c1a4-49dc-af14-c8ac2029ca97 |   300 | https://preview.redd.it/award_images/t5_22cerq/a05z7bb9v7i51_TableFlip.png?width=64&height=64&crop=smart&auto=webp&s=3efc978414612ce3b518813a124501b8a204e56b               |+| This                       | 68ba1ee3-9baf-4252-be52-b808c1e8bdc4 |   300 | https://preview.redd.it/award_images/t5_22cerq/vu6om0xnb7e41_This.png?width=64&height=64&crop=smart&auto=webp&s=4b2a7c5439da6b4a9e2173bbf3cf9a610538600d                    |+| Updoot                     | 725b427d-320b-4d02-8fb0-8bb7aa7b78aa |   300 | https://preview.redd.it/award_images/t5_22cerq/7atjjqpy1mc41_Updoot.png?width=64&height=64&crop=smart&auto=webp&s=f07dcd6402653b86d321cff6ded773cfa13d918a                  |+| Spit-take                  | 3409a4c0-ba69-43a0-be9f-27bc27c159cc |   325 | https://www.redditstatic.com/gold/awards/icon/Spits_drink_64.png                                                                                                            |+| Super Heart Eyes           | 6220ecfe-4552-4949-aa13-fb1fb7db537c |   325 | https://www.redditstatic.com/gold/awards/icon/Superheart_64.png                                                                                                             |+| Table Slap                 | 9f928aff-c9f5-4e7e-aa91-8619dce60f1c |   325 | https://www.redditstatic.com/gold/awards/icon/TableSlap_64.png                                                                                                              |+| To The Stars               | 2bc47247-b107-44a8-a78c-613da21869ff |   325 | https://www.redditstatic.com/gold/awards/icon/Rocket_64.png                                                                                                                 |+| Aww-some                   | e55d1889-11f2-4d04-8abb-44b1de7dd53d |   350 | https://www.redditstatic.com/gold/awards/icon/Awwsome2_64.png                                                                                                               |+| Heartbeat                  | 11eb6af3-3d0d-4d70-8261-22d216ab591d |   350 | https://www.redditstatic.com/gold/awards/icon/Heartbeat_64.png                                                                                                              |+| Into the Magic Portal      | 2ff1fdd0-ff73-47e6-a43c-bde6d4de8fbd |   350 | https://www.redditstatic.com/gold/awards/icon/TeleportIn_64.png                                                                                                             |+| Out of the Magic Portal    | 7fe72f36-1141-4a39-ba76-0d481889b390 |   350 | https://www.redditstatic.com/gold/awards/icon/TeleportOut_64.png                                                                                                            |+| Bravo!                     | 84276b1e-cc8f-484f-a19c-be6c09adc1a5 |   400 | https://www.redditstatic.com/gold/awards/icon/SnooClapping_64.png                                                                                                           |+| Doot 🎵 Doot               | 5b39e8fd-7a58-4cbe-8ca0-bdedd5ed1f5a |   400 | https://www.redditstatic.com/gold/awards/icon/Updoot_64.png                                                                                                                 |+| Pumpkin Spice              | 89164d08-80db-453b-a7aa-74c50fa84bfa |   400 | https://preview.redd.it/award_images/t5_22cerq/c1ll7h6zedq51_PumpkinSpice.png?width=64&height=64&crop=smart&auto=webp&s=c8a3297c17b9b70255353e933f5ed0a5b5bb3934            |+| Bless Up (Pro)             | 43c43a35-15c5-4f73-91ef-fe538426435a |   500 | https://preview.redd.it/award_images/t5_22cerq/xe5mw55w5v541_BlessUp.png?width=64&height=64&crop=smart&auto=webp&s=54b7d71c74cf41fc8ada4a2110babf8b68d2f172                 |+| Brighten My Day            | 9591a26e-b2e4-4ef2-bed4-28ff69246691 |   500 | https://www.redditstatic.com/gold/awards/icon/Sunny_64.png                                                                                                                  |+| Eureka!                    | 65f78ca2-45d8-4cb6-bf79-a67beadf2e47 |   500 | https://www.redditstatic.com/gold/awards/icon/Bulb_64.png                                                                                                                   |+| Heart Eyes                 | a9009ea5-1a36-42ae-aab2-5967563ee054 |   500 | https://preview.redd.it/award_images/t5_22cerq/12kz7a7j4v541_HeartEyes.png?width=64&height=64&crop=smart&auto=webp&s=02bb7182aad468e16e0a711b594110225679c79e               |+| Helpful (Pro)              | 2ae56630-cfe0-424e-b810-4945b9145358 |   500 | https://preview.redd.it/award_images/t5_22cerq/trz28na8ajz31_Helpful.png?width=64&height=64&crop=smart&auto=webp&s=5dfea529ee529d33105b592b42696d05e7b734c2                 |+| Made Me Smile              | a7a04d6a-8dd8-41bb-b906-04fa8f144014 |   500 | https://preview.redd.it/award_images/t5_22cerq/hwnbr9l67s941_MadeMeSmile.png?width=64&height=64&crop=smart&auto=webp&s=918705b3b6804a6151b6ce4f8b1c86dfa604a470             |+| Snoo Nice                  | 27d3176c-b388-4616-80ec-11b8ece5b7ee |   500 | https://preview.redd.it/award_images/t5_22cerq/9r7hexe6pta41_SnooNice.png?width=64&height=64&crop=smart&auto=webp&s=88748b0fe08311b512dc08909acdcdbe8d91d747                |+| Starry                     | 0e957fb0-c8f1-4ba1-a8ef-e1e524b60d7d |   500 | https://www.redditstatic.com/gold/awards/icon/Starry_512.png                                                                                                                |+| Wholesome (Pro)            | 1f0462ee-18f5-4f33-89cf-f1f79336a452 |   500 | https://preview.redd.it/award_images/t5_22cerq/0o2j782f00e41_WholesomeSuperpro.png?width=64&height=64&crop=smart&auto=webp&s=2a04235b56e4fbc90106067bfe0d61bd60300c4f       |+| Pot o' Coins               | 35c78e6e-507b-4f1d-b3d8-ed4384090    |  1000 | https://preview.redd.it/award_images/t5_22cerq/wg3lzllyg9n41_PotoCoins.png?width=64&height=64&crop=smart&auto=webp&s=7bb8b7ab622f25b5e6f7f2068663b5ea964d2957               |+| Cornucopia                 | 9a123cdb-d26d-4d0c-b7fa-46750b8289fa |  2000 | https://preview.redd.it/award_images/t5_22cerq/27xs8z0ja3u51_Cornucopia.png?width=64&height=64&crop=smart&auto=webp&s=4298b88d2511a0951817f440fe5244f6dcf513a9              |+| Argentium                  | 4ca5a4e6-8873-4ac5-99b9-71b1d5161a91 | 20000 | https://www.redditstatic.com/gold/awards/icon/Mithril_64.png                                                                                                                |+| Ternion All-Powerful       | 2385c499-a1fb-44ec-b9b7-d260f3dc55de | 50000 | https://www.redditstatic.com/gold/awards/icon/Trinity_64.png                                                                                                                |+|                            |                                      |       | <14>                                                                                                                                                                        |
heddit.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               heddit-version:            0.0.3+version:            0.1 synopsis:           Reddit API bindings description:        See the README at https://gitlab.com/ngua/heddit license:            BSD-3-Clause@@ -60,6 +60,7 @@     Network.Reddit.Subreddit     Network.Reddit.Types     Network.Reddit.Types.Account+    Network.Reddit.Types.Award     Network.Reddit.Types.Comment     Network.Reddit.Types.Emoji     Network.Reddit.Types.Flair@@ -107,7 +108,7 @@     , scientific            ^>=0.3.6     , split                 ^>=0.2.3     , text                  ^>=1.2-    , time                  >=1.8     && <1.12+    , time                  >=1.9     && <1.13     , unliftio              ^>=0.2     , unordered-containers  ^>=0.2.10     , uri-bytestring        ^>=0.3@@ -224,7 +225,7 @@     , hspec          >=2.0    && <3.0     , microlens-ghc  ^>=0.4.10     , text           ^>=1.2-    , time           >=1.8    && <1.12+    , time           >=1.9    && <1.13     , unliftio       ^>=0.2    other-modules:
io-tests/SubredditSpec.hs view
@@ -15,7 +15,7 @@ import           Test.Hspec  spec :: Spec-spec = beforeAll (loadClient Nothing) . describe "Network.Reddit.Submission"+spec = beforeAll (loadClient Nothing) . describe "Network.Reddit.Subreddit"     $ sequence_ [ getInfo                 , getTop                 , nameSearch
src/Network/Reddit.hs view
@@ -66,7 +66,7 @@     , ErrorMessage(..)     , StatusMessage(StatusMessage)     , StatusCode-    , JSONError(JSONError)+    , POSTError(POSTError)       -- * Auth     , ClientState     , AppType(..)
src/Network/Reddit/Comment.hs view
@@ -21,6 +21,7 @@     (  -- * Reading comments       getComments     , getComment+    , getCommentByURL     , withReplies     , loadMoreComments     , loadMoreCommentsDef@@ -37,6 +38,10 @@     , upvoteComment     , downvoteComment     , unvoteComment+      -- * Awarding+    , awardComment+    , gildComment+      -- * Misc     , reportComment       -- * Types     , module M@@ -70,7 +75,7 @@  -- | Get the 'Comment's corresponding to a container of 'CommentID's getComments :: (MonadReddit m, Foldable t)-            => ItemOpts Comment+            => ItemOpts              -> t CommentID             -> m (Seq Comment) getComments = getMany@@ -82,6 +87,11 @@     comment :<| _ -> pure comment     _             -> throwM $ InvalidResponse "getComment: No results" +-- | Get a comment based on its permalink URL, which must be of the form+-- https:\/\/{www.}reddit.com\/r\/\<SUB\>\/comments\/\<SID\>\/\<NAME\>/\<ID\>+getCommentByURL :: MonadReddit m => URL -> m Comment+getCommentByURL url = getComment =<< commentIDFromURL url+ -- | Get new 'Comment's, either for the site as a whole or for a single subreddit, -- given its 'SubredditName' getNewComments :: MonadReddit m@@ -98,7 +108,7 @@ -- | Update a 'Comment' to include its 'ChildComment's, returning the updated -- 'Comment'. This will probably be necessary if the original 'Comment' was obtained -- by getting a 'Username'\'s or 'Subreddit'\'s comments, etc...-withReplies :: MonadReddit m => ItemOpts a -> Comment -> m Comment+withReplies :: MonadReddit m => ItemOpts -> Comment -> m Comment withReplies ItemOpts { .. } Comment { .. } =     runAction @WithReplies r <&> wrappedTo   where@@ -150,6 +160,16 @@ unvoteComment :: MonadReddit m => CommentID -> m () unvoteComment = vote Unvote . CommentItemID +-- | Grant an 'Award' to a comment. See 'mkAward' for creating an award with+-- default values, as well as the pattern synonyms for common awards+awardComment :: MonadReddit m => Award -> CommentID -> m AwardingsSummary+awardComment a = award a . CommentItemID++-- | Convenience action to anonymously give 'Gold' to the author of a comment,+-- without a message. See 'awardComment' if you need more fine-grained control+gildComment :: MonadReddit m => CommentID -> m AwardingsSummary+gildComment = gild . CommentItemID+ -- | Report a comment to the subreddit\'s mods reportComment :: MonadReddit m => Report -> CommentID -> m () reportComment r = report r . CommentItemID@@ -164,7 +184,7 @@     :: forall m.     MonadReddit m     => Maybe Word-    -> ItemOpts Comment+    -> ItemOpts      -> SubmissionID     -> MoreComments     -> m (Seq ChildComment)@@ -188,8 +208,8 @@      more = case remaining of         Seq.Empty -> mempty-        cids      -> Seq.singleton . More-            $ MoreComments cids (count - fromIntegral limit)+        cids      -> Seq.singleton . More . MoreComments cids+            $ count - fromIntegral limit      (toFetch, remaining) = Seq.splitAt limit childIDs 
src/Network/Reddit/Internal.hs view
@@ -26,6 +26,7 @@     , getMany     , redditURL     , oauthURL+    , getMe     ) where  import           Conduit                               ( (.|), runConduit )@@ -71,6 +72,7 @@ import qualified Network.HTTP.Types                    as H import           Network.Reddit.Auth import           Network.Reddit.Types+import           Network.Reddit.Types.Account import           Network.Reddit.Utils  import           UnliftIO.IORef@@ -227,7 +229,7 @@ -- 'Seq' of 'Network.Reddit.Types.Comment.Comment's getMany :: forall a b t m.         (MonadReddit m, Foldable t, Thing b, FromJSON a, FromJSON b)-        => ItemOpts a+        => ItemOpts         -> t b         -> m (Seq a) getMany opts ids = mconcat <$> traverse getChunk (chunked ids)@@ -246,3 +248,9 @@                             , ("limit", toQueryParam @Int apiRequestLimit)                             ]         }++-- NOTE+-- This action needs to be in this module to avoid cyclic module dependencies+-- | Get account information for the currently logged-in user+getMe :: MonadReddit m => m Account+getMe = runAction defaultAPIAction { pathSegments = [ "api", "v1", "me" ] }
src/Network/Reddit/Item.hs view
@@ -21,6 +21,8 @@     , report     , save     , unsave+    , award+    , gild     , setInboxReplies     , getGildedItems       -- * Types@@ -33,6 +35,13 @@  import           Network.Reddit.Internal import           Network.Reddit.Types+import           Network.Reddit.Types.Award+import           Network.Reddit.Types.Award     as M+                 ( Award(Award)+                 , AwardID(..)+                 , AwardingsSummary(AwardingsSummary)+                 , mkAward+                 ) import           Network.Reddit.Types.Comment import           Network.Reddit.Types.Item      ( PostedItem ) import           Network.Reddit.Types.Item      as M@@ -45,6 +54,7 @@ import           Network.Reddit.Types.Subreddit import           Network.Reddit.Utils +import           Web.FormUrlEncoded             ( ToForm(toForm) ) import           Web.HttpApiData                  ( ToHttpApiData(toQueryParam, toUrlPiece)                  )@@ -128,6 +138,27 @@                , method       = POST                , requestData  = mkTextFormData [ ("id", fullname iid) ]                }++-- | Grant an award to the author of an 'Item'. See the docs for 'Award' for+-- information about the available options when awarding as well as 'mkAward'+-- to use defaults.+--+-- To grant a common award, such as gold, see the bundled pattern synonyms of+-- 'AwardID'. If you wish to anonymously gild an item without a message, see the+-- 'gild' action in this module, provided for convenience+award :: MonadReddit m => Award -> ItemID -> m AwardingsSummary+award aw iid =+    runAction defaultAPIAction+              { pathSegments = [ "api", "v2", "gold", "gild" ]+              , method       = POST+              , requestData  = WithForm+                    $ mkTextForm [ ("thing_id", fullname iid) ] <> toForm aw+              }++-- | Gild an item anonymously, without a message. To grant other types of+-- awards to items, or to gild non-anonymously\/with a message, see 'award'+gild :: MonadReddit m => ItemID -> m AwardingsSummary+gild = award $ mkAward Gold  -- | Enable or disable inbox replies for an item given its 'ItemID' setInboxReplies :: MonadReddit m => Bool -> ItemID -> m ()
src/Network/Reddit/Me.hs view
@@ -21,6 +21,7 @@       getMe     , getPreferences     , updatePreferences+    , getMySubreddit     , getMyOverview     , getMySaved     , getMyComments@@ -39,6 +40,7 @@     , getMyModerated     , getMyContributing     , getMyMultireddits+    , getMySubredditSettings     ) where  import           Control.Monad.Catch@@ -55,6 +57,8 @@ import           Lens.Micro  import           Network.Reddit.Internal+import           Network.Reddit.Moderation+import           Network.Reddit.Subreddit import           Network.Reddit.Types import           Network.Reddit.Types.Account import           Network.Reddit.Types.Comment@@ -62,17 +66,12 @@ import           Network.Reddit.Types.Item import           Network.Reddit.Types.Multireddit import           Network.Reddit.Types.Submission-import           Network.Reddit.Types.Subreddit import           Network.Reddit.User import           Network.Reddit.Utils  import           Web.FormUrlEncoded               ( ToForm(toForm) ) import           Web.HttpApiData                  ( ToHttpApiData(..) ) --- | Get account information for the currently logged-in user-getMe :: MonadReddit m => m Account-getMe = runAction defaultAPIAction { pathSegments = mePath mempty }- -- | Get the user 'Preferences' for the currently authenticated user getPreferences :: MonadReddit m => m Preferences getPreferences =@@ -92,6 +91,20 @@               , method       = PATCH               , requestData  = mkTextFormData [ ("json", textEncode prefs) ]               }++-- | Get the special user 'Subreddit' associated with the authenticated account+getMySubreddit :: MonadReddit m => m Subreddit+getMySubreddit = do+    Account { username } <- getMe+    getSubreddit =<< mkSubredditName (usernameToDisplayName username)++-- | Get settings for the special user 'Subreddit' associated with the authenticated+-- account. In order to update the settings, you can use 'setSubredditSettings'+-- directly, as it requires the subreddit ID+getMySubredditSettings :: MonadReddit m => m SubredditSettings+getMySubredditSettings = do+    Account { username } <- getMe+    getSubredditSettings =<< mkSubredditName (usernameToDisplayName username)  -- | Get an overview of the authenticated user\'s 'Comment's and 'Submission's getMyOverview
src/Network/Reddit/Moderation.hs view
@@ -263,7 +263,6 @@  import           Network.HTTP.Client.MultipartFormData ( partBS, partFile ) import           Network.Reddit.Internal-import           Network.Reddit.Me import           Network.Reddit.Submission import           Network.Reddit.Types import           Network.Reddit.Types.Account
src/Network/Reddit/Multireddit.hs view
@@ -40,7 +40,6 @@ import           Data.Foldable                    ( traverse_ )  import           Network.Reddit.Internal-import           Network.Reddit.Me import           Network.Reddit.Types import           Network.Reddit.Types.Account import           Network.Reddit.Types.Multireddit
src/Network/Reddit/Submission.hs view
@@ -55,8 +55,11 @@     , upvoteSubmission     , downvoteSubmission     , unvoteSubmission-    , reportSubmission+      -- * Awards+    , awardSubmission+    , gildSubmission       -- * Misc+    , reportSubmission     , unmarkNSFW     , markNSFW     , setOC@@ -143,7 +146,7 @@  -- | Get a information on 'Submission's given a container of 'SubmissionID's getSubmissions :: (MonadReddit m, Foldable t)-               => ItemOpts Submission+               => ItemOpts                -> t SubmissionID                -> m (Seq Submission) getSubmissions = getMany@@ -495,6 +498,17 @@ -- | Remove an existing vote on a submission unvoteSubmission :: MonadReddit m => SubmissionID -> m () unvoteSubmission = vote Unvote . SubmissionItemID++-- | Grant an 'Award' to a submission. See 'mkAward' for creating an award with+-- default values, as well as the pattern synonyms for common awards+awardSubmission+    :: MonadReddit m => Award -> SubmissionID -> m AwardingsSummary+awardSubmission a = award a . SubmissionItemID++-- | Convenience action to anonymously give 'Gold' to the author of a submission,+-- without a message. See 'awardSubmission' if you need more fine-grained control+gildSubmission :: MonadReddit m => SubmissionID -> m AwardingsSummary+gildSubmission = gild . SubmissionItemID  -- | Report a submission to the subreddit\'s mods reportSubmission :: MonadReddit m => Report -> SubmissionID -> m ()
src/Network/Reddit/Types/Account.hs view
@@ -32,20 +32,20 @@     , FriendList     , Karma(..)     , KarmaList-    , Trophy(..)-    , TrophyList     , UserSummary(..)     , UserSummaryList     , Preferences(..)     , MediaPreference(..)     , AcceptPMs(..)+    , usernameToDisplayName     ) where  import           Control.Monad                  ( (<=<) ) import           Control.Monad.Catch            ( MonadThrow )  import           Data.Aeson-                 ( (.:)+                 ( (.!=)+                 , (.:)                  , (.:?)                  , Array                  , FromJSON(..)@@ -89,40 +89,54 @@     deriving ( Eq ) via CIText Username  -- | Smart constructor for 'Username', which must be between 3 and 20 chars,--- and may only include upper/lowercase alphanumeric chars, underscores, or+-- and may only include upper\/lowercase alphanumeric chars, underscores, or -- hyphens mkUsername :: MonadThrow m => Text -> m Username mkUsername = validateName Nothing Nothing "Username" +-- | Prefix the username with \"u_\"+usernameToDisplayName :: Username -> Text+usernameToDisplayName (Username uname) = "u_" <> uname+ -- | A unique, site-wide ID for an account newtype UserID = UserID Text     deriving stock ( Show, Generic )     deriving newtype ( Eq )  instance FromJSON UserID where-    parseJSON = withText "UserID" (coerce . dropTypePrefix AccountKind)+    parseJSON = withText "UserID" $ coerce . dropTypePrefix AccountKind  instance Thing UserID where     fullname (UserID uid) = prependType AccountKind uid --- | Account information. @Maybe@ fields denote data that Reddit sets to null if--- the requester does not own the account in question. Note that this does /not/--- include all of the possible fields that may be present in Reddit's response ---- which are quite numerous in total and poorly documented+-- | Account information. Most @Maybe@ fields denote data that Reddit sets to null+-- if the requester does not own the account in question data Account = Account-    { userID           :: UserID-    , username         :: Username-    , created          :: UTCTime-    , commentKarma     :: Int-    , isGold           :: Bool-    , isMod            :: Bool-    , linkKarma        :: Int-    , inboxCount       :: Maybe Integer-    , modHash          :: Maybe Text-    , over18           :: Maybe Bool-    , hasMail          :: Maybe Bool-    , hasModMail       :: Maybe Bool-    , hasVerifiedEmail :: Maybe Bool+    { userID               :: UserID+    , username             :: Username+    , created              :: UTCTime+    , commentKarma         :: Integer+    , linkKarma            :: Integer+    , totalKarma           :: Integer+    , awarderKarma         :: Maybe Integer+    , awardeeKarma         :: Maybe Integer+    , isFriend             :: Bool+    , isGold               :: Bool+    , isMod                :: Bool+    , iconImg              :: URL+    , subreddit            :: Subreddit+    , numFriends           :: Maybe Integer+    , inboxCount           :: Maybe Integer+    , creddits             :: Maybe Integer+    , modHash              :: Maybe Text+    , over18               :: Maybe Bool+    , hasMail              :: Maybe Bool+    , hasModMail           :: Maybe Bool+    , hasVerifiedEmail     :: Maybe Bool+    , hasGoldSubscription  :: Maybe Bool+    , goldExpiration       :: Maybe UTCTime+    , isSuspended          :: Maybe Bool+    , suspensionExpiration :: Maybe UTCTime     }     deriving stock ( Show, Generic ) @@ -131,19 +145,34 @@                        , withKind AccountKind "Account" accountP v                        ]       where-        accountP o = Account <$> (o .: "id")-            <*> o .: "name"-            <*> (integerToUTC <$> o .: "created_utc")-            <*> o .: "comment_karma"-            <*> o .: "is_gold"-            <*> o .: "is_mod"-            <*> o .: "link_karma"-            <*> o .:? "inbox_count"-            <*> o .:? "modhash"-            <*> o .:? "over_18"-            <*> o .:? "has_mail"-            <*> o .:? "has_mod_mail"-            <*> o .:? "has_verified_email"+        accountP o = do+            userID <- o .: "id"+            username <- o .: "name"+            created <- integerToUTC <$> o .: "created_utc"+            commentKarma <- o .: "comment_karma"+            linkKarma <- o .: "link_karma"+            totalKarma <- o .:? "total_karma" .!= (commentKarma + linkKarma)+            awarderKarma <- o .:? "awarder_karma"+            awardeeKarma <- o .:? "awardee_karma"+            numFriends <- o .:? "num_friends"+            isFriend <- o .:? "is_friend" .!= False+            isGold <- o .: "is_gold"+            isMod <- o .: "is_mod"+            iconImg <- o .: "icon_img"+            subreddit <- o .: "subreddit" <&> field @"created" .~ created+            inboxCount <- o .:? "inbox_count"+            creddits <- o .:? "gold_creddits"+            modHash <- o .:? "modhash"+            over18 <- o .:? "over_18"+            hasMail <- o .:? "has_mail"+            hasModMail <- o .:? "has_mod_mail"+            hasVerifiedEmail <- o .:? "has_verified_email"+            hasGoldSubscription <- o .:? "has_gold_subscription"+            goldExpiration <- o .:? "gold_expiration"+            isSuspended <- o .:? "is_suspended"+            suspensionExpiration+                <- fmap integerToUTC <$> o .:? "suspension_expiration"+            pure Account { .. }  instance Paginable Account where     type PaginateOptions Account = AccountSearchOpts@@ -234,33 +263,6 @@ instance FromJSON KarmaList where     parseJSON = withKind @Array KarmaListKind "KarmaList"         $ fmap (KarmaList . fromList) . traverse parseJSON . toList---- | A Reddit award, such as the \"one-year club\"-data Trophy = Trophy-    { name        :: Name-    , trophyID    :: Maybe Text-    , awardID     :: Maybe Text-    , description :: Maybe Body-    , grantedAt   :: Maybe UTCTime-    }-    deriving stock ( Show, Eq, Generic )--instance FromJSON Trophy where-    parseJSON = withKind AwardKind "Trophy" $ \o -> Trophy <$> o .: "name"-        <*> o .:? "id"-        <*> o .:? "award_id"-        <*> o .:? "description"-        <*> (fmap integerToUTC <$> o .:? "granted_at")---- | Wrapper for parsing JSON objects listing 'Trophy's-newtype TrophyList = TrophyList (Seq Trophy)-    deriving stock ( Show, Generic )--instance FromJSON TrophyList where-    parseJSON = withKind TrophyListKind "TrophyList"-        $ fmap (TrophyList . fromList) . (trophiesP <=< (.: "trophies"))-      where-        trophiesP = withArray "[Trophy]" (traverse parseJSON . toList)  -- | A brief summary of a user, with significantly less information than a -- 'Account'
+ src/Network/Reddit/Types/Award.hs view
@@ -0,0 +1,222 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StrictData #-}+{-# LANGUAGE TupleSections #-}++-- |+-- Module      : Network.Reddit.Types.Award+-- Copyright   : (c) 2021 Rory Tyler Hayford+-- License     : BSD-3-Clause+-- Maintainer  : rory.hayford@protonmail.com+-- Stability   : experimental+-- Portability : GHC+--+module Network.Reddit.Types.Award+    ( Awarding+    , AwardID(AwardID, Silver, Gold, Platinum, Argentium, Ternion)+    , AwardType(..)+    , Award(..)+    , mkAward+    , Trophy+    , TrophyID+    , TrophyList+    , AwardingsSummary(..)+    ) where++import           Control.Monad                  ( (<=<) )++import           Data.Aeson+                 ( (.:)+                 , (.:?)+                 , FromJSON(parseJSON)+                 , FromJSONKey+                 , Options(..)+                 , defaultOptions+                 , genericParseJSON+                 , withArray+                 , withText+                 )+import           Data.Aeson.Casing              ( snakeCase )+import           Data.Char                      ( toLower )+import           Data.HashMap.Strict            ( HashMap )+import           Data.Hashable                  ( Hashable )+import           Data.Sequence                  ( Seq )+import           Data.Text                      ( Text )+import           Data.Time                      ( UTCTime )++import           GHC.Exts                       ( IsList(fromList, toList) )+import           GHC.Generics                   ( Generic )++import           Network.Reddit.Types.Internal+import           Network.Reddit.Types.Subreddit++import           Web.FormUrlEncoded             ( ToForm(toForm) )+import           Web.HttpApiData                ( ToHttpApiData(..) )++-- | Information about a Reddit award that has been granted. This can be a+-- \"global\" award that may be granted site-wide, or a \"community\" award+-- that is limited to a single subreddit+data Awarding = Awarding+    { awardID       :: AwardID+    , name          :: Name+      -- | This will only be present for 'Community' awards+    , description   :: Body+    , subredditID   :: Maybe SubredditID+    , awardType     :: AwardType+    , count         :: Int+      -- | The number of \"creddits\" required to grant the award+    , coinPrice     :: Int+      -- | The number of \"creddits\" given to the recipient+    , coinReward    :: Int+      -- | How many days of premium Reddit the awardee is granted+    , daysOfPremium :: Int+      -- | URL of Reddit-hosted icon image+    , iconURL       :: URL+      -- | Width of the icon in pixels+    , iconHeight    :: Int+      -- | Width of the icon in pixels+    , iconWidth     :: Int+    }+    deriving stock ( Show, Eq, Generic )++instance FromJSON Awarding where+    parseJSON = genericParseJSON defaultOptions { fieldLabelModifier }+      where+        fieldLabelModifier = \case+            "awardID"     -> "id"+            "subredditID" -> "subreddit_id"+            "iconURL"     -> "icon_url"+            s             -> snakeCase s++-- | The ID for an award, which users can grant each other. If you want to create+-- a new 'AwardID', see the bundled pattern synonyms for this type, which include+-- various common awards. Also see \"doc/awards.org\" in this repository for a list+-- of awards and their IDs.+--+-- __Note__: Most newer awards are composed of a UUID identifier and an \"award_\"+-- prefix. If you construct this type directly, you should omit the prefix, which+-- will be added for you when making API requests+newtype AwardID = AwardID Text+    deriving stock ( Show, Generic )+    deriving newtype ( Eq, FromJSONKey, Hashable )++instance FromJSON AwardID where+    parseJSON = withText "AwardID" $ breakOnTypeLenient "award"++instance ToHttpApiData AwardID where+    toQueryParam aw@(AwardID a)+        | aw `elem` [ Silver, Gold, Platinum ] = a+        | otherwise = "award_" <> a++-- | The type of the 'Awarding', either site-wide (\"global\") or limited to a+-- \"community\"+data AwardType+    = Global+    | Community+    deriving stock ( Show, Eq, Generic )++instance FromJSON AwardType where+    parseJSON = genericParseJSON --+        defaultOptions { constructorTagModifier = fmap toLower }++-- | Options for awarding an item+data Award = Award+    { -- | The ID of the award you wish to grant. See 'AwardID' and+      -- its bundled pattern synonyms for common awards (e.g. gold)+      awardID     :: AwardID+      -- | If the award is issued anonymously+    , isAnonymous :: Bool+      -- | Optional message sent to the recipient+    , message     :: Maybe Body+    }+    deriving stock ( Show, Eq, Generic )++instance ToForm Award where+    toForm Award { .. } = fromList+        $ [ ("gild_type", toQueryParam awardID)+          , ("is_anonymous", toQueryParam isAnonymous)+          , ("api_type", "json")+          ]+        <> foldMap pure (("award", ) <$> message)++-- | Create a new anonymous 'Awarding' without a message, given an 'AwardID'+mkAward :: AwardID -> Award+mkAward awardID = Award { isAnonymous = True, message = Nothing, .. }++-- | A summary of your 'Awardings', returned after you have awarded an item+data AwardingsSummary = AwardingsSummary+    { -- | All of the awardings issued by the current user+      allAwardings :: Seq Awarding+      -- | A mapping of award IDs to the number of times the+      -- authenticated user has granted each one+    , gildings     :: HashMap AwardID Integer+      -- | Coin balance for the authenticated user+    , coins        :: Integer+    }+    deriving stock ( Show, Eq, Generic )++instance FromJSON AwardingsSummary where+    parseJSON =+        genericParseJSON defaultOptions { fieldLabelModifier = snakeCase }++-- | A Reddit trophy, such as the \"one-year club\", that the Reddit grants+-- users. Redditors cannot gift each other these trophies+data Trophy = Trophy+    { name        :: Name+    , trophyID    :: Maybe TrophyID+      -- | It is not clear what this field is referring to, as 'Trophy's and+      -- 'Award's are supposed to be entirely distinct, according to Reddit+    , awardID     :: Maybe Text+    , description :: Maybe Body+    , grantedAt   :: Maybe UTCTime+    , url         :: Maybe URL+      -- | URL for a 41x41 px icon+    , icon40      :: URL+      -- | URL for a 71x71 px icon+    , icon70      :: URL+    }+    deriving stock ( Show, Eq, Generic )++instance FromJSON Trophy where+    parseJSON = withKind AwardKind "Trophy" $ \o -> Trophy <$> o .: "name"+        <*> o .:? "id"+        <*> o .:? "award_id"+        <*> o .:? "description"+        <*> (fmap integerToUTC <$> o .:? "granted_at")+        <*> o .:? "url"+        <*> o .: "icon_40"+        <*> o .: "icon_70"++-- | The ID of a 'Trophy'+type TrophyID = Text++-- | Wrapper for parsing JSON objects listing 'Trophy's+newtype TrophyList = TrophyList (Seq Trophy)+    deriving stock ( Show, Generic )++instance FromJSON TrophyList where+    parseJSON = withKind TrophyListKind "TrophyList"+        $ fmap (TrophyList . fromList) . (trophiesP <=< (.: "trophies"))+      where+        trophiesP = withArray "[Trophy]" (traverse parseJSON . toList)++pattern Silver :: AwardID+pattern Silver = AwardID "gid_1"++pattern Gold :: AwardID+pattern Gold = AwardID "gid_2"++pattern Platinum :: AwardID+pattern Platinum = AwardID "gid_3"++pattern Argentium :: AwardID+pattern Argentium = AwardID "4ca5a4e6-8873-4ac5-99b9-71b1d5161a91"++pattern Ternion :: AwardID+pattern Ternion = AwardID "2385c499-a1fb-44ec-b9b7-d260f3dc55de"
src/Network/Reddit/Types/Comment.hs view
@@ -54,6 +54,7 @@ import           Lens.Micro  import           Network.Reddit.Types.Account+import           Network.Reddit.Types.Award import           Network.Reddit.Types.Internal import           Network.Reddit.Types.Submission ( SubmissionID ) import           Network.Reddit.Types.Subreddit@@ -100,6 +101,7 @@       -- | Whether the author of the comment is also the submission author     , isSubmitter   :: Bool     , stickied      :: Bool+    , allAwardings  :: Seq Awarding     }     deriving stock ( Show, Eq, Generic ) @@ -133,6 +135,7 @@     distinguished <- o .:? "distinguished"     isSubmitter <- o .: "is_submitter"     stickied <- o .: "stickied"+    allAwardings <- o .: "all_awardings"     pure Comment { .. }   where     repliesP (String _)   = pure mempty@@ -141,7 +144,7 @@     repliesP _            = mempty  instance Paginable Comment where-    type PaginateOptions Comment = ItemOpts Comment+    type PaginateOptions Comment = ItemOpts       type PaginateThing Comment = CommentID @@ -196,10 +199,11 @@     deriving newtype ( Eq )  instance FromJSON LoadedChildren where-    parseJSON = withObject "LoadedChildren" $ \o -> LoadedChildren . fromList-        <$> (loadedP =<< ((.: "things") =<< (.: "data") =<< o .: "json"))+    parseJSON = withObject "LoadedChildren"+        $ fmap (LoadedChildren . fromList)+        . (loadedP <=< (.: "things") <=< (.: "data") <=< (.: "json"))       where-        loadedP = withArray "[ChildComment]" (traverse parseJSON . toList)+        loadedP = withArray "[ChildComment]" $ traverse parseJSON . toList  -- | A link to load more children 'Comment's data MoreComments = MoreComments
src/Network/Reddit/Types/Internal.hs view
@@ -10,6 +10,8 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-}++{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -45,6 +47,7 @@     , Time(..)     , ItemType(..)     , UploadURL+    , SubredditType(..)     , Body     , Title     , URL@@ -62,7 +65,7 @@     , ErrorMessage(..)     , StatusCode     , StatusMessage(..)-    , JSONError(..)+    , POSTError(..)       -- * Utilities     , dropTypePrefix     , integerToUTC@@ -79,12 +82,15 @@     , textEncode     , withKinds     , breakOnType+    , breakOnTypeLenient     , getVals     , mkTextForm+    , fromOptional     ) where  import           Conduit                      ( ConduitM ) +import           Control.Applicative          ( optional ) import           Control.Exception            ( Exception(..), SomeException ) import           Control.Monad                ( guard ) import           Control.Monad.Catch          ( MonadThrow(throwM) )@@ -93,6 +99,7 @@                  ( (.:)                  , (.:?)                  , FromJSON(..)+                 , Object                  , Options(constructorTagModifier)                  , ToJSON(toJSON)                  , Value(..)@@ -104,6 +111,7 @@                  , withObject                  , withText                  )+import           Data.Aeson.Casing            ( snakeCase ) import           Data.Aeson.Text              ( encodeToLazyText ) import           Data.Aeson.Types             ( Pair, Parser ) import           Data.Bool                    ( bool )@@ -132,7 +140,7 @@ import           Data.Time.Clock.POSIX        ( posixSecondsToUTCTime )  import           GHC.Exts                     ( Coercible-                                              , IsList(fromList, toList)+                                              , IsList(fromList, toList, Item)                                               ) import           GHC.Generics                 ( Generic ) @@ -313,9 +321,8 @@     HKD Identity a = a     HKD f a = f a --- | Options that can be applied to comments or submissions, as represented by the--- phantom type parameter-data ItemOpts a = ItemOpts+-- | Options that can be applied to comments or submissions+data ItemOpts = ItemOpts     { itemSort :: Maybe ItemSort     , itemType :: Maybe ItemType     , itemTime :: Maybe Time@@ -325,7 +332,7 @@     }     deriving stock ( Show, Eq, Generic ) -instance ToForm (ItemOpts a) where+instance ToForm ItemOpts where     toForm ItemOpts { .. } = fromList         $ catMaybes [ ("sort", ) . toQueryParam <$> itemSort                     , ("type", ) . toQueryParam <$> itemType@@ -334,7 +341,7 @@                     ]  -- | Defaults for fetching items, like comments or submissions-defaultItemOpts :: ItemOpts a+defaultItemOpts :: ItemOpts defaultItemOpts = ItemOpts     { itemSort = Nothing     , itemType = Nothing@@ -438,6 +445,29 @@     deriving stock ( Show, Generic )     deriving newtype ( Eq, FromJSON, ToJSON, ToHttpApiData ) +-- | The privacy level for the subreddit+data SubredditType+    = Public+    | Restricted+    | Private+    | Archived+    | GoldRestricted+    | EmployeesOnly+    | GoldOnly+    | UserSubreddit+    deriving stock ( Show, Eq, Generic )++instance FromJSON SubredditType where+    parseJSON = genericParseJSON --+        defaultOptions { constructorTagModifier }+      where+        constructorTagModifier = \case+            "UserSubreddit" -> "user"+            s               -> snakeCase s++instance ToHttpApiData SubredditType where+    toQueryParam = showTextData+ -- | Type synonym for URLs type URL = Text @@ -493,7 +523,7 @@     = ErrorWithStatus StatusMessage     | ErrorWithMessage ErrorMessage     | InvalidCredentials OAauthError-    | InvalidJSON JSONError+    | InvalidPOST POSTError       -- ^ Sent if errors occur when posting JSON     | JSONParseError Text LB.ByteString       -- ^ With the response body, for further debugging@@ -515,7 +545,7 @@     parseJSON = withObject "APIException" $ \(Object -> o) ->         asum [ ErrorWithStatus <$> parseJSON o              , ErrorWithMessage <$> parseJSON o-             , InvalidJSON <$> parseJSON o+             , InvalidPOST <$> parseJSON o              , InvalidCredentials <$> parseJSON o              ] @@ -549,6 +579,7 @@     | TooShort     | BadCaptcha     | UserRequired+    | InsufficientCoins     deriving stock ( Show, Eq, Generic )  instance FromJSON ErrorMessage where@@ -575,6 +606,7 @@                 "BAD_CAPTCHA" : _ -> pure BadCaptcha                 "TOO_SHORT" : _ -> pure TooShort                 "USER_REQUIRED" : _ -> pure UserRequired+                "INSUFFICIENT_COINS_WITH_AMOUNT" : _ -> pure InsufficientCoins                 v -> pure $ OtherErrorMessage v  -- | Type synonym for status codes in responses@@ -589,9 +621,10 @@     parseJSON = withObject "StatusMessage" $ \o ->         StatusMessage <$> (o .: "error") <*> (o .: "message") --- | Details about a non-200 response when posting JSON-data JSONError = JSONError-    { -- | The fields of the object containing errors+-- | Details about a non-200 response when sending a POST request+data POSTError = POSTError+    { -- | The fields of the JSON object containing errors (only if+      -- JSON was posted)       fields      :: [Text]     , explanation :: Text     , message     :: Text@@ -599,7 +632,12 @@     }     deriving stock ( Show, Eq, Generic ) -instance FromJSON JSONError+instance FromJSON POSTError where+    parseJSON = withObject "POSTError" $ \o -> POSTError+        <$> fromOptional o "fields"+        <*> o .: "explanation"+        <*> o .: "message"+        <*> o .: "reason"  redditExToException :: Exception e => e -> SomeException redditExToException = toException . RedditException@@ -746,6 +784,13 @@         | T.null r -> pure $ coerce prefix         | otherwise -> mempty +-- | Split a JSON identifier on \"_\"; if it matches the given type+-- prefix, returning the remaining text. If there are no matches, return+-- the text whole. This is useful if Reddit does not consistently prefix+-- values with the type identifier+breakOnTypeLenient :: (Coercible a Text) => Text -> Text -> Parser a+breakOnTypeLenient ty t = asum [ breakOnType ty t, pure $ coerce t ]+ -- | Get all of the values from a 'HashMap' and place them in a 'Seq', discarding -- the keys getVals :: FromJSON b => HashMap Text Value -> Parser (Seq b)@@ -754,3 +799,9 @@ -- | Make a form from @[(Text, Text)]@ pairs mkTextForm :: [(Text, Text)] -> Form mkTextForm = toForm @[(Text, Text)]++-- Parses a container of values that may be missing from the JSON, in which case+-- it returns @mempty@ as a default value+fromOptional+    :: (FromJSON (Item b), IsList b, Monoid b) => Object -> Text -> Parser b+fromOptional o fld = maybe mempty fromList <$> optional (o .: fld)
src/Network/Reddit/Types/Item.hs view
@@ -72,7 +72,7 @@     deriving stock ( Show, Eq, Generic )  instance Paginable Item where-    type PaginateOptions Item = ItemOpts Item+    type PaginateOptions Item = ItemOpts      type PaginateThing Item = ItemID 
src/Network/Reddit/Types/Live.hs view
@@ -253,7 +253,7 @@ data LiveContributor = LiveContributor     { userID      :: UserID     , username    :: Username-    , permissions :: Seq LivePermission+    , permissions :: [LivePermission]     }     deriving stock ( Show, Eq, Generic ) @@ -265,7 +265,7 @@       where         permissionsP = withArray "[LivePermission]" $ \a -> case toList a of             [ "all" ] -> pure $ fromList [ Edit .. Manage ]-            xs        -> fromList <$> traverse parseJSON xs+            xs        -> traverse parseJSON xs  -- | Wrapper to parse lists of 'LiveContributor's newtype LiveContributorList = LiveContributorList (Seq LiveContributor)
src/Network/Reddit/Types/Moderation.hs view
@@ -377,7 +377,7 @@     , flairCSS    :: Maybe CSSClass     , date        :: UTCTime       -- | If @Nothing@, indicates the user has all mod permissions-    , permissions :: Maybe (Seq ModPermission)+    , permissions :: Maybe [ModPermission]     }     deriving stock ( Show, Eq, Generic ) @@ -600,29 +600,6 @@  instance ToHttpApiData CrowdControlLevel where     toQueryParam = showTextData . fromEnum---- | The privacy level for the subreddit-data SubredditType-    = Public-    | Restricted-    | Private-    | Archived-    | GoldRestricted-    | EmployeesOnly-    | GoldOnly-    | UserSubreddit-    deriving stock ( Show, Eq, Generic )--instance FromJSON SubredditType where-    parseJSON = genericParseJSON ---        defaultOptions { constructorTagModifier }-      where-        constructorTagModifier = \case-            "UserSubreddit" -> "user"-            s               -> snakeCase s--instance ToHttpApiData SubredditType where-    toQueryParam = showTextData  -- | Permissible submissions on the subreddit data ContentOptions
src/Network/Reddit/Types/Submission.hs view
@@ -113,6 +113,7 @@ import           GHC.Generics                   ( Generic )  import           Network.Reddit.Types.Account+import           Network.Reddit.Types.Award import           Network.Reddit.Types.Flair import           Network.Reddit.Types.Internal import           Network.Reddit.Types.Subreddit@@ -161,6 +162,7 @@     , locked        :: Bool     , spoiler       :: Bool     , pollData      :: Maybe PollData+    , allAwardings  :: Seq Awarding     }     deriving stock ( Show, Eq, Generic ) @@ -195,6 +197,7 @@     distinguished <- o .:? "distinguished"     pollData <- o .:? "poll_data"     domain <- o .: "domain"+    allAwardings <- o .: "all_awardings"     pure Submission { .. }   where     contentP v = (v .: "is_self") >>= \case@@ -204,7 +207,7 @@                       ]  instance Paginable Submission where-    type PaginateOptions Submission = ItemOpts Submission+    type PaginateOptions Submission = ItemOpts      type PaginateThing Submission = SubmissionID 
src/Network/Reddit/Types/Subreddit.hs view
@@ -45,7 +45,8 @@ import           Control.Monad.Catch           ( MonadThrow )  import           Data.Aeson-                 ( (.:)+                 ( (.!=)+                 , (.:)                  , (.:?)                  , FromJSON(..)                  , Options(..)@@ -60,6 +61,8 @@                  , withText                  ) import           Data.Aeson.Casing             ( snakeCase )+import           Data.Coerce                   ( coerce )+import           Data.Foldable                 ( asum ) import           Data.Maybe                    ( catMaybes, fromMaybe ) import           Data.Sequence                 ( Seq ) import           Data.Text                     ( Text )@@ -83,42 +86,106 @@     , name               :: SubredditName     , title              :: Title     , created            :: UTCTime+    , subredditType      :: SubredditType+    , subscribers        :: Integer+      -- | Description as shown in searches+    , publicDescription  :: Body       -- | The description of the subreddit in markdown+    , keyColor           :: Maybe RGBText     , description        :: Body     , descriptionHTML    :: Maybe Body-      -- | Description as shown in searches-    , publicDescription  :: Body-    , subscribers        :: Integer+      -- | Text shown when submitting, in markdown+    , submitText         :: Maybe Text+    , submitTextHTML     :: Maybe Text+      -- | The label shown on the submit button+    , submitTextLabel    :: Maybe Text+    , iconImg            :: Maybe URL+      -- -- | The sub banner image+    , bannerImg          :: Maybe URL+      -- | The dimensions (w, h) for the banner image, if it exists+    , bannerSize         :: Maybe (Int, Int)+    , headerImg          :: Maybe URL+      -- | The dimensions (w, h) for the header image, if it exists+    , headerSize         :: Maybe (Int, Int)     , over18             :: Bool-    , userIsBanned       :: Maybe Bool-    , userIsModerator    :: Maybe Bool-    , userIsSubscriber   :: Maybe Bool+      -- | Whether the subreddit is quarantined+    , quarantine         :: Bool+    , userIsBanned       :: Bool+    , userIsMuted        :: Bool+    , userIsModerator    :: Bool+    , userIsContributor  :: Bool+    , userIsSubscriber   :: Bool+    , allowImages        :: Bool+    , allowPolls         :: Bool+    , allowVideos        :: Bool+    , allowVideoGIFs     :: Bool+      -- | Whether users can specify custom reasons in reports+    , freeFormReports    :: Bool+      -- | Whether users are forbidden from posting submissions+    , restrictPosting    :: Bool+      -- | Whether users are forbidden from posting comments+    , restrictCommenting :: Bool+      -- | Whether link flair is enabled at all+    , linkFlairEnabled   :: Bool       -- | Whether users can assign their own link flair     , canAssignLinkFlair :: Maybe Bool       -- | Whether users can assign their own user flair     , canAssignUserFlair :: Maybe Bool-      -- | Whether the subreddit is quarantined-    , quarantine         :: Bool+      -- | Whether the sub supports marking posts with the+      -- spoiler tag+    , spoilersEnabled    :: Maybe Bool     }     deriving stock ( Show, Eq, Generic )  instance FromJSON Subreddit where-    parseJSON = withKind SubredditKind "Subreddit" $ \o -> Subreddit-        <$> o .: "id"-        <*> o .: "display_name"-        <*> o .: "title"-        <*> (integerToUTC <$> o .: "created_utc")-        <*> o .: "description"-        <*> o .: "description_html"-        <*> o .: "public_description"-        <*> o .: "subscribers"-        <*> o .: "over18"-        <*> o .: "user_is_banned"-        <*> o .: "user_is_moderator"-        <*> o .: "user_is_subscriber"-        <*> o .:? "can_assign_link_flair"-        <*> o .:? "can_assign_user_flair"-        <*> o .: "quarantine"+    parseJSON v =+        asum [ withKind SubredditKind "Subreddit" (subredditP "id") v+             , withObject "Subreddit" (subredditP "name") v+             ]+      where+        subredditP idField o = do+            subredditID <- o .: idField+            name <- o .: "display_name"+            title <- o .: "title"+            -- This field is only missing when getting a user's subreddit.+            -- We can use the user's creation date in that case, after+            -- setting an (arbitrary) default here+            created <- integerToUTC <$> o .:? "created_utc" .!= 0+            subredditType <- o .: "subreddit_type"+            keyColor <- nothingTxtNull =<< o .: "key_color"+            description <- o .: "description"+            descriptionHTML <- o .:? "description_html"+            publicDescription <- o .: "public_description"+            submitTextLabel <- o .: "submit_text_label"+            submitText <- o .:? "submit_text"+            submitTextHTML <- o .:? "submit_text_html"+            subscribers <- o .: "subscribers"+            iconImg <- maybe (pure Nothing) nothingTxtNull+                =<< o .:? "icon_img"+            bannerImg <- maybe (pure Nothing) nothingTxtNull+                =<< o .: "banner_img"+            bannerSize <- o .:? "banner_size"+            headerImg <- o .:? "header_img"+            headerSize <- o .:? "header_size"+            over18 <- o .: "over_18" <|> o .: "over18"+            quarantine <- o .: "quarantine"+            userIsBanned <- o .: "user_is_banned"+            userIsMuted <- o .: "user_is_muted"+            userIsModerator <- o .: "user_is_moderator"+            userIsContributor <- o .: "user_is_contributor"+            userIsSubscriber <- o .: "user_is_subscriber"+            allowImages <- o .:? "allow_images" .!= True+            allowVideos <- o .:? "allow_videos" .!= True+            allowPolls <- o .:? "allow_polls" .!= True+            allowVideoGIFs <- o .:? "allow_videogifs" .!= True+            freeFormReports <- o .: "free_form_reports"+            restrictPosting <- o .: "restrict_posting"+            restrictCommenting <- o .: "restrict_commenting"+            linkFlairEnabled <- o .: "link_flair_enabled"+            canAssignLinkFlair <- o .:? "can_assign_link_flair"+            canAssignUserFlair <- o .:? "can_assign_user_flair"+            spoilersEnabled <- o .:? "spoilers_enabled"+            pure Subreddit { .. }  -- Dummy instance so that @Listing ... Subreddit@ can work with convenience -- actions@@ -148,8 +215,11 @@ -- | Unique site-wide identifier for a subreddit newtype SubredditID = SubredditID Text     deriving stock ( Show, Generic )-    deriving newtype ( Eq, FromJSON )+    deriving newtype ( Eq ) +instance FromJSON SubredditID where+    parseJSON = withText "SubredditID" $ coerce . dropTypePrefix SubredditKind+ instance Thing SubredditID where     fullname (SubredditID sid) = prependType SubredditKind sid @@ -306,4 +376,3 @@         "notAllowed" -> pure BodyNotAllowed         "none"       -> pure NoRestriction         _            -> mempty-
src/Network/Reddit/Types/Widget.hs view
@@ -103,7 +103,7 @@ import           Data.Text                      ( Text ) import qualified Data.Text                      as T -import           GHC.Exts                       ( IsList(fromList, Item) )+import           GHC.Exts                       ( IsList(fromList) ) import           GHC.Generics                   ( Generic(Rep) )  import           Lens.Micro@@ -909,12 +909,6 @@ defaultWidgetModifier = \case     "widgetID" -> "id"     s          -> s---- Parses a container of values that may be missing from the JSON, in which case--- it returns @mempty@ as a default value-fromOptional-    :: (FromJSON (Item b), IsList b, Monoid b) => Object -> Text -> Parser b-fromOptional o fld = maybe mempty fromList <$> optional (o .: fld)  withWidgetKind     :: WidgetType -> [Char] -> (Value -> Parser a) -> Value -> Parser a
src/Network/Reddit/User.hs view
@@ -3,6 +3,7 @@  {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ViewPatterns #-}  -- | -- Module      : Network.Reddit.User@@ -29,7 +30,8 @@     , getUserGilded     , getUserSaved     , getUserMultireddits-      -- * User @Listing@s and summaries+    , gildUser+      -- ** User @Listing@s and summaries       -- | These actions return @Listing@s for special user subreddits,       -- user accounts, or 'UserSummary's for existing user IDs     , getNewUsers@@ -39,14 +41,18 @@     , getUserSummary       -- * Types     , module M+    , Trophy     ) where +import           Control.Monad                    ( unless ) import           Control.Monad.Catch              ( MonadThrow(throwM) )  import           Data.Aeson                       ( FromJSON ) import qualified Data.Foldable                    as F import           Data.Generics.Wrapped            ( wrappedTo )+import           Data.Ix                          ( Ix(inRange) ) import           Data.List.Split                  ( chunksOf )+import           Data.Maybe                       ( fromMaybe ) import           Data.Sequence                    ( Seq((:<|)) ) import           Data.Text                        ( Text ) @@ -64,12 +70,12 @@                  , Karma(Karma)                  , MediaPreference(..)                  , Preferences(Preferences)-                 , Trophy(Trophy)                  , UserID(UserID)                  , UserSummary(UserSummary)                  , Username                  , mkUsername                  )+import           Network.Reddit.Types.Award import           Network.Reddit.Types.Comment import           Network.Reddit.Types.Item import           Network.Reddit.Types.Multireddit@@ -166,6 +172,23 @@ getUserMultireddits uname =     runAction defaultAPIAction               { pathSegments = [ "api", "multi", "user", toUrlPiece uname ] }++-- | Give the user Reddit gold, optionally specifying the duration+gildUser :: MonadReddit m+         => Maybe Word+         -- ^ Defaults to 1 when @Nothing@. Must be between 1 and 36+         -> Username+         -> m ()+gildUser (fromMaybe 1 -> months) uname = do+    unless (inRange (1, 36) months) . throwM+        $ InvalidRequest "Duration must be between 1 and 36"+    runAction_ defaultAPIAction+               { pathSegments =+                     [ "api", "v1", "gold", "give", toUrlPiece uname ]+               , method       = POST+               , requestData  =+                     mkTextFormData [ ("months", toQueryParam months) ]+               }  -- | Get a @Listing@ of special user subreddits, sorted on creation date (newest -- first)
src/Network/Reddit/Utils.hs view
@@ -26,6 +26,7 @@     , mkTextForm     , mkTextFormData     , submissionIDFromURL+    , commentIDFromURL     , subAPIPath     , subAboutPath     , textObject@@ -43,21 +44,25 @@ import           Data.Aeson                      ( eitherDecode ) import           Data.ByteString                 ( ByteString ) import qualified Data.ByteString.Char8           as C8+import           Data.Coerce                     ( coerce ) import           Data.Containers.ListUtils       ( nubOrd ) import qualified Data.Foldable                   as F-import           Data.Generics.Wrapped           ( wrappedFrom, wrappedTo ) import           Data.List                       ( (\\) ) import qualified Data.Text                       as T import           Data.Text                       ( Text ) import qualified Data.Text.Encoding              as T +import           Lens.Micro+ import           Network.Reddit.Types+import           Network.Reddit.Types.Comment import           Network.Reddit.Types.Submission import           Network.Reddit.Types.Subreddit  import           URI.ByteString                  ( Authority(..)                  , URIRef(URI, uriPath, uriAuthority)+                 , hostBSL                  , laxURIParserOptions                  , parseURI                  )@@ -114,37 +119,53 @@  -- | Parse a 'SubmissionID' from a Reddit URL submissionIDFromURL :: MonadThrow m => Text -> m SubmissionID-submissionIDFromURL url = case parsed of-    Left _           -> invalidURL "Invalid URL provided"-    Right URI { .. }-        | Just Authority { authorityHost } <- uriAuthority    ---            -> case wrappedTo authorityHost of-                host-                    | host `elem` [ "reddit.com", "www.reddit.com" ]    ---                        -> case splitPath uriPath of-                            [ "gallery", sid ] -> pure $ mkID sid-                            ("comments" : sid : _) -> pure $ mkID sid-                            ("r" : _ : "comments" : sid : _) ->-                                pure $ mkID sid-                            _ -> invalidURL-                                $ mconcat [ "Path must be one of "-                                          , "/r/<SUBREDDIT>/comments/<ID>/<NAME>/, "-                                          , "/gallery/<ID>, or /comments/<ID>/"-                                          ]+submissionIDFromURL = idFromURL f g+  where+    f    = \case+        "gallery" : sid : _ -> mkID sid+        "comments" : sid : _ -> mkID sid+        "r" : _ : "comments" : sid : _ -> mkID sid+        _ -> throwM . InvalidRequest+            $ mconcat [ "Path must be one of "+                      , "/r/<SUBREDDIT>/comments/<ID>/<NAME>/, "+                      , "/gallery/<ID>, or /comments/<ID>/"+                      ] -                    | host == "redd.it" -> case splitPath uriPath of-                        [ sid ] -> pure $ mkID sid-                        _       -> invalidURL "Path may only contain /<ID>"+    g    = \case+        sid : _ -> mkID sid+        _       -> throwM . InvalidRequest $ "Path may only contain /<ID>" -                    | otherwise -> invalidURL "Unrecognized host"-        | otherwise -> invalidURL "URL authority not present or unrecognized"+    mkID = pure . coerce++-- | Parse a 'SubmissionID' from a Reddit URL+commentIDFromURL :: MonadThrow m => Text -> m CommentID+commentIDFromURL = idFromURL f g   where-    parsed     = parseURI laxURIParserOptions $ T.encodeUtf8 url+    f    = \case+        "r" : _ : "comments" : _ : _ : cid : _ -> mkID cid+        _ -> throwM . InvalidRequest+            $ "Path must be /r/<SUBREDDIT>/comments/<SID>/<NAME>/<ID>" -    invalidURL = throwM . InvalidRequest+    g    = const . throwM . InvalidRequest+        $ "Cannot get comment ID from redd.it hosts" -    mkID       = wrappedFrom . T.decodeUtf8+    mkID = pure . coerce +idFromURL :: MonadThrow m+          => ([PathSegment] -> m a) -- for various reddit.com hosts+          -> ([PathSegment] -> m a) -- for redd.it host+          -> Text+          -> m a+idFromURL f g url = splitURL url >>= \case+    (host, ps)+        | host `elem` hosts -> f ps+        | host == "redd.it" -> g ps+        | otherwise -> invalidURL "Unrecognized host"+      where+        hosts      = [ "reddit.com", "www.reddit.com", "old.reddit.com" ]++        invalidURL = throwM . InvalidRequest+ -- | Get the API path for a subreddit given its 'SubredditName' subAPIPath :: SubredditName -> PathSegment -> [PathSegment] subAPIPath sname path = [ "r", toUrlPiece sname, "api", path ]@@ -181,8 +202,9 @@ splitURL url = case parseURI laxURIParserOptions $ T.encodeUtf8 url of     Right URI { .. }         | Just Authority { authorityHost } <- uriAuthority ---            ->-            pure (wrappedTo authorityHost, T.decodeUtf8 <$> splitPath uriPath)+            -> pure ( authorityHost ^. hostBSL+                    , T.decodeUtf8 <$> splitPath uriPath+                    )         | otherwise -> invalidURL     Left _           -> invalidURL   where
tests/Main.hs view
@@ -8,6 +8,7 @@  import           Network.Reddit.Types import           Network.Reddit.Types.Account+import           Network.Reddit.Types.Award import           Network.Reddit.Types.Comment import           Network.Reddit.Types.Emoji import           Network.Reddit.Types.Flair@@ -34,6 +35,7 @@                  , decodeWidgetTypes                  , decodeEmojiTypes                  , decodeLiveTypes+                 , decodeAwardTypes                  ]  {- HLINT ignore "Redundant do"-}@@ -230,6 +232,14 @@             liveContributors <- eitherDecodeFileStrict' @LiveContributorList                 $ typesDataPath "live-contributor-list.json"             liveContributors `shouldSatisfy` isRight++decodeAwardTypes :: IO ()+decodeAwardTypes = hspec $ do+    describe "Network.Reddit.Types.Award" $ do+        it "decodes Awarding" $ do+            award <- eitherDecodeFileStrict' @[Awarding]+                $ typesDataPath "awarding.json"+            award `shouldSatisfy` isRight  typesDataPath :: FilePath -> FilePath typesDataPath = (<>) "./tests/data/types/"
+ tests/data/types/awarding.json view
@@ -0,0 +1,170 @@+[+  {+    "giver_coin_reward": null,+    "subreddit_id": null,+    "is_new": false,+    "days_of_drip_extension": 0,+    "coin_price": 125,+    "id": "award_5f123e3d-4f48-42f4-9c11-e98b566d5897",+    "penny_donate": null,+    "award_sub_type": "GLOBAL",+    "coin_reward": 0,+    "icon_url": "https://i.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png",+    "days_of_premium": 0,+    "tiers_by_required_awardings": null,+    "resized_icons": [+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=16&amp;height=16&amp;auto=webp&amp;s=92932f465d58e4c16b12b6eac4ca07d27e3d11c0",+        "width": 16,+        "height": 16+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=32&amp;height=32&amp;auto=webp&amp;s=d11484a208d68a318bf9d4fcf371171a1cb6a7ef",+        "width": 32,+        "height": 32+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=48&amp;height=48&amp;auto=webp&amp;s=febdf28b6f39f7da7eb1365325b85e0bb49a9f63",+        "width": 48,+        "height": 48+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=64&amp;height=64&amp;auto=webp&amp;s=b4406a2d88bf86fa3dc8a45aacf7e0c7bdccc4fb",+        "width": 64,+        "height": 64+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=128&amp;height=128&amp;auto=webp&amp;s=19555b13e3e196b62eeb9160d1ac1d1b372dcb0b",+        "width": 128,+        "height": 128+      }+    ],+    "icon_width": 2048,+    "static_icon_width": 2048,+    "start_date": null,+    "is_enabled": true,+    "awardings_required_to_grant_benefits": null,+    "description": "When you come across a feel-good thing.",+    "end_date": null,+    "subreddit_coin_reward": 0,+    "count": 1,+    "static_icon_height": 2048,+    "name": "Wholesome",+    "resized_static_icons": [+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=16&amp;height=16&amp;auto=webp&amp;s=92932f465d58e4c16b12b6eac4ca07d27e3d11c0",+        "width": 16,+        "height": 16+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=32&amp;height=32&amp;auto=webp&amp;s=d11484a208d68a318bf9d4fcf371171a1cb6a7ef",+        "width": 32,+        "height": 32+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=48&amp;height=48&amp;auto=webp&amp;s=febdf28b6f39f7da7eb1365325b85e0bb49a9f63",+        "width": 48,+        "height": 48+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=64&amp;height=64&amp;auto=webp&amp;s=b4406a2d88bf86fa3dc8a45aacf7e0c7bdccc4fb",+        "width": 64,+        "height": 64+      },+      {+        "url": "https://preview.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png?width=128&amp;height=128&amp;auto=webp&amp;s=19555b13e3e196b62eeb9160d1ac1d1b372dcb0b",+        "width": 128,+        "height": 128+      }+    ],+    "icon_format": null,+    "icon_height": 2048,+    "penny_price": null,+    "award_type": "global",+    "static_icon_url": "https://i.redd.it/award_images/t5_22cerq/5izbv4fn0md41_Wholesome.png"+  },+  {+    "giver_coin_reward": null,+    "subreddit_id": "t5_2qzb6",+    "is_new": false,+    "days_of_drip_extension": 0,+    "coin_price": 2000,+    "id": "award_48b83aa4-a3f1-4ea7-9c95-558a6252e773",+    "penny_donate": null,+    "award_sub_type": "COMMUNITY",+    "coin_reward": 0,+    "icon_url": "https://i.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png",+    "days_of_premium": 0,+    "tiers_by_required_awardings": null,+    "resized_icons": [+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=16\\u0026height=16\\u0026auto=webp\\u0026s=2731c2b6c61a6aeeef7ca07cf02ed3afd581f706",+        "width": 16,+        "height": 16+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=32\\u0026height=32\\u0026auto=webp\\u0026s=bd98c9c8a0d6f2a9669d341423b632931ec9053e",+        "width": 32,+        "height": 32+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=48\\u0026height=48\\u0026auto=webp\\u0026s=fce2d3366e64c6a1cc71f1e9fc6006601fa49a5b",+        "width": 48,+        "height": 48+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=64\\u0026height=64\\u0026auto=webp\\u0026s=f2d6c300dfffec549b0ae2019c778c5074831725",+        "width": 64,+        "height": 64+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=128\\u0026height=128\\u0026auto=webp\\u0026s=65b616a93309199b6f76b1e3d3b6b162bc7aa5c0",+        "width": 128,+        "height": 128+      }+    ],+    "icon_width": 512,+    "static_icon_width": 512,+    "start_date": null,+    "is_enabled": true,+    "awardings_required_to_grant_benefits": null,+    "description": "Shows the Star of Excellence Award and grants %{coin_symbol}400 Coins to the community. Exclusive to this community.",+    "end_date": null,+    "subreddit_coin_reward": 400,+    "count": 1,+    "static_icon_height": 512,+    "name": "Star of Excellence",+    "resized_static_icons": [+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=16\\u0026height=16\\u0026auto=webp\\u0026s=2731c2b6c61a6aeeef7ca07cf02ed3afd581f706",+        "width": 16,+        "height": 16+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=32\\u0026height=32\\u0026auto=webp\\u0026s=bd98c9c8a0d6f2a9669d341423b632931ec9053e",+        "width": 32,+        "height": 32+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=48\\u0026height=48\\u0026auto=webp\\u0026s=fce2d3366e64c6a1cc71f1e9fc6006601fa49a5b",+        "width": 48,+        "height": 48+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=64\\u0026height=64\\u0026auto=webp\\u0026s=f2d6c300dfffec549b0ae2019c778c5074831725",+        "width": 64,+        "height": 64+      },+      {+        "url": "https://preview.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png?width=128\\u0026height=128\\u0026auto=webp\\u0026s=65b616a93309199b6f76b1e3d3b6b162bc7aa5c0",+        "width": 128,+        "height": 128+      }+    ],+    "icon_format": null,+    "icon_height": 512,+    "penny_price": null,+    "award_type": "community",+    "static_icon_url": "https://i.redd.it/award_images/t5_2qzb6/n8h85cxvcw731_StarofExcellence.png"+  }+]
+ tests/data/types/awardings-summary.json view
@@ -0,0 +1,39 @@+{+  "subreddit_balance": 0,+  "treatment_tags": [],+  "coins": 0,+  "gildings": { "gid_1": 0, "gid_2": 0, "gid_3": 0 },+  "awarder_karma_received": 16,+  "all_awardings": [+    {+      "giver_coin_reward": null,+      "subreddit_id": null,+      "is_new": false,+      "days_of_drip_extension": 0,+      "coin_price": 150,+      "id": "award_f44611f1-b89e-46dc-97fe-892280b13b82",+      "penny_donate": null,+      "coin_reward": 0,+      "icon_url": "https://i.redd.it/award_images/t5_22cerq/klvxk1wggfd41_Helpful.png",+      "days_of_premium": 0,+      "icon_height": 2048,+      "tiers_by_required_awardings": null,+      "icon_width": 2048,+      "static_icon_width": 2048,+      "start_date": null,+      "is_enabled": true,+      "awardings_required_to_grant_benefits": null,+      "description": "Thank you stranger. Shows the award.",+      "end_date": null,+      "subreddit_coin_reward": 0,+      "count": 1,+      "static_icon_height": 2048,+      "name": "Helpful",+      "icon_format": null,+      "award_sub_type": "GLOBAL",+      "penny_price": null,+      "award_type": "global",+      "static_icon_url": "https://i.redd.it/award_images/t5_22cerq/klvxk1wggfd41_Helpful.png"+    }+  ]+}
tests/data/types/subreddit.json view
@@ -1,47 +1,100 @@ {   "kind": "t5",   "data": {-    "banner_img": "",+    "user_flair_background_color": "",     "submit_text_html": null,+    "restrict_posting": true,     "user_is_banned": false,-    "wiki_enabled": null,-    "id": "2r0ij",-    "user_is_contributor": false,-    "submit_text": "",-    "display_name": "<SUBREDDIT_NAME>",+    "free_form_reports": true,+    "wiki_enabled": true,+    "user_is_muted": false,+    "user_can_flair_in_sr": true,+    "display_name": "<SUBREDDIT>",     "header_img": null,+    "title": "Title",+    "icon_size": null,+    "primary_color": "",+    "active_user_count": 8,+    "icon_img": null,+    "display_name_prefixed": "r/<SUBREDDIT>",+    "accounts_active": 8,+    "public_traffic": false,+    "subscribers": 1,+    "user_flair_richtext": [{ "e": "text", "t": "" }],+    "videostream_links_count": 0,+    "name": "t5_49n01",+    "quarantine": false,+    "hide_ads": false,+    "emojis_enabled": true,+    "advertiser_category": "",+    "public_description": "no",+    "comment_score_hide_mins": 0,+    "user_has_favorited": false,+    "user_flair_template_id": null,+    "community_icon": "",+    "banner_background_image": "https://styles.redditmedia.com/t5_49n01/styles/bannerBackgroundImage_m2ppq02m.jpg",+    "original_content_tag_enabled": false,+    "submit_text": "",     "description_html": "",-    "title": "<TITLE>",+    "spoilers_enabled": true,+    "header_title": "",+    "header_size": null,+    "user_flair_position": "right",+    "all_original_content": false,+    "collections_enabled": true,+    "is_enrolled_in_new_modmail": true,+    "key_color": "",+    "event_posts_enabled": true,+    "can_assign_user_flair": false,+    "created": 1503566715.0,+    "wls": null,+    "show_media_preview": true,+    "submission_type": "any",+    "user_is_subscriber": false,+    "disable_contributor_requests": false,+    "allow_videogifs": true,+    "user_flair_type": "richtext",+    "allow_polls": true,     "collapse_deleted_comments": false,-    "public_description": "",+    "coins": 0,+    "emojis_custom_size": null,+    "public_description_html": "",+    "allow_videos": true,+    "is_crosspostable_subreddit": true,+    "notification_level": null,+    "can_assign_link_flair": true,+    "has_menu_widget": false,+    "accounts_active_is_fuzzed": false,+    "submit_text_label": "",+    "link_flair_position": "left",+    "user_sr_flair_enabled": true,+    "user_flair_enabled_in_sr": true,+    "allow_chat_post_creation": false,+    "allow_discovery": false,+    "user_sr_theme_enabled": true,+    "link_flair_enabled": true,+    "subreddit_type": "restricted",+    "suggested_comment_sort": null,+    "banner_img": null,+    "user_flair_text": "",+    "banner_background_color": "",+    "show_media": true,+    "id": "49n01",+    "user_is_moderator": true,     "over18": false,-    "public_description_html": null,-    "icon_size": [256, 256],-    "suggested_comment_sort": "qa",-    "icon_img": "",-    "header_title": null,     "description": "",-    "user_is_muted": false,-    "submit_link_label": null,-    "accounts_active": null,-    "public_traffic": false,-    "header_size": null,-    "subscribers": 11755132,-    "submit_text_label": null,+    "is_chat_post_feature_enabled": true,+    "submit_link_label": "",+    "user_flair_text_color": "dark",+    "restrict_commenting": false,+    "user_flair_css_class": "",+    "allow_images": true,     "lang": "en",-    "key_color": "#ff4500",-    "name": "t5_2r0ij",-    "created": 1245284901.0,-    "url": "/r/<SUBREDDIT_NAME>/",-    "quarantine": false,-    "hide_ads": false,-    "created_utc": 1245256101.0,+    "whitelist_status": null,+    "url": "/r/<SUBREDDIT>/",+    "created_utc": 1503537915.0,     "banner_size": null,-    "user_is_moderator": false,-    "user_sr_theme_enabled": true,-    "comment_score_hide_mins": 0,-    "subreddit_type": "restricted",-    "submission_type": "any",-    "user_is_subscriber": true+    "mobile_banner_image": "",+    "user_is_contributor": false   } }