{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}

-- |
-- Module      : Network.Reddit.Submission
-- Copyright   : (c) 2021 Rory Tyler Hayford
-- License     : BSD-3-Clause
-- Maintainer  : rory.hayford@protonmail.com
-- Stability   : experimental
-- Portability : GHC
--
module Network.Reddit.Submission
    (  -- * Reading submissions
      getSubmissions
    , getSubmission
    , getSubmissionByURL
    , getSubmissionsByDomain
    , getBest
    , getChildComments
    , getDuplicateSubmissions
    , saveSubmission
    , unsaveSubmission
    , hideSubmissions
    , hideSubmission
    , unhideSubmissions
    , unhideSubmission
      -- * Creating, editing, and deleting
    , deleteSubmission
    , editSubmission
    , replyToSubmission
    , submitSelfPost
    , submitWithInlineMedia
    , submitLink
    , submitImage
    , submitVideo
    , submitPoll
    , submitGallery
    , submit
    , crosspost
    , setSubmissionReplies
      -- * Submission flair
    , getSubmissionFlairChoices
    , selectSubmissionFlair
      -- * Searching
    , search
      -- * Voting
      -- $vote
    , upvoteSubmission
    , downvoteSubmission
    , unvoteSubmission
      -- * Awards
    , awardSubmission
    , gildSubmission
      -- * Misc
    , reportSubmission
    , unmarkNSFW
    , markNSFW
    , setOC
    , unsetOC
    , setSpoiler
    , unsetSpoiler
      -- * Types
    , module M'
    ) where

import           Control.Monad                         ( void, when )
import           Control.Monad.Catch                   ( MonadThrow(throwM) )
import           Control.Monad.IO.Class                ( MonadIO(liftIO) )

import           Data.Aeson                            ( ToJSON(toJSON) )
import           Data.Generics.Product                 ( HasField(field) )
import           Data.Generics.Wrapped
                 ( wrappedFrom
                 , wrappedTo
                 )
import qualified Data.HashMap.Strict                   as HM
import qualified Data.Map                              as M
import           Data.Maybe                            ( fromMaybe )
import           Data.Sequence                         ( Seq((:<|), Empty) )
import           Data.Text                             ( Text )
import qualified Data.Text                             as T
import qualified Data.Text.Encoding                    as T
import           Data.Traversable                      ( for )

import           Lens.Micro

import           Network.HTTP.Client.MultipartFormData ( partBS, partFile )
import           Network.Reddit.Internal
import           Network.Reddit.Item
import           Network.Reddit.Subreddit
import           Network.Reddit.Types
import           Network.Reddit.Types.Comment
import           Network.Reddit.Types.Flair
import           Network.Reddit.Types.Submission
import           Network.Reddit.Types.Submission       as M'
                 ( Collection(Collection)
                 , CollectionID
                 , CollectionLayout(..)
                 , CrosspostOptions(CrosspostOptions)
                 , Fancypants
                 , GalleryImage(GalleryImage)
                 , InlineMedia(InlineMedia)
                 , InlineMediaType(..)
                 , NewCollection(NewCollection)
                 , NewSubmission(..)
                 , Poll(Poll)
                 , PollData(PollData)
                 , PollOption(PollOption)
                 , PollOptionID
                 , PostedSubmission
                 , ResultID(ResultID)
                 , S3UploadLease(S3UploadLease)
                 , Search(Search)
                 , SearchCategory
                 , SearchOpts(SearchOpts)
                 , SearchSort(..)
                 , SearchSyntax(..)
                 , Submission(Submission)
                 , SubmissionContent(..)
                 , SubmissionID(SubmissionID)
                 , SubmissionOptions(SubmissionOptions)
                 , mkCrosspostOptions
                 , mkGalleryImage
                 , mkPoll
                 , mkSearch
                 , mkSearchCategory
                 , mkSubmissionOptions
                 , writeInlineMedia
                 )
import           Network.Reddit.Utils

import           Paths_heddit

import qualified System.FilePath                       as FP

import           Web.FormUrlEncoded                    ( ToForm(toForm) )
import           Web.HttpApiData                       ( ToHttpApiData(..) )
import           Web.Internal.FormUrlEncoded           ( Form )

-- | Get a information on 'Submission's given a container of 'SubmissionID's
getSubmissions :: (MonadReddit m, Foldable t)
               => ItemOpts
               -> t SubmissionID
               -> m (Seq Submission)
getSubmissions :: ItemOpts -> t SubmissionID -> m (Seq Submission)
getSubmissions = ItemOpts -> t SubmissionID -> m (Seq Submission)
forall a b (t :: * -> *) (m :: * -> *).
(MonadReddit m, Foldable t, Thing b, FromJSON a, FromJSON b) =>
ItemOpts -> t b -> m (Seq a)
getMany

-- | Get information on a single submission. Throws an exception if no such
-- 'Submission' exists
getSubmission :: MonadReddit m => SubmissionID -> m Submission
getSubmission :: SubmissionID -> m Submission
getSubmission SubmissionID
sid = ItemOpts -> [SubmissionID] -> m (Seq Submission)
forall (m :: * -> *) (t :: * -> *).
(MonadReddit m, Foldable t) =>
ItemOpts -> t SubmissionID -> m (Seq Submission)
getSubmissions ItemOpts
defaultItemOpts [ SubmissionID
sid ] m (Seq Submission)
-> (Seq Submission -> m Submission) -> m Submission
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Submission
sub :<| Seq Submission
_ -> Submission -> m Submission
forall (f :: * -> *) a. Applicative f => a -> f a
pure Submission
sub
    Seq Submission
_         -> ClientException -> m Submission
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ClientException -> m Submission)
-> ClientException -> m Submission
forall a b. (a -> b) -> a -> b
$ Text -> ClientException
InvalidResponse Text
"getSubmission: No results"

-- | Get a 'Submission' from a URL pointing to it, in one of the following forms:
--
--      * http{s}:\/\/redd.it\/\<ID\>
--      * http{s}:\/\/{www.}reddit.com\/comments\/\<ID\>\/
--      * http{s}:\/\/{www.}reddit.com\/r\/\<SUBREDDIT\>\/comments\/\<ID\>\/{NAME}
--      * http{s}:\/\/{www.}reddit.com\/gallery\/\<ID\>
--
getSubmissionByURL :: MonadReddit m => URL -> m Submission
getSubmissionByURL :: Text -> m Submission
getSubmissionByURL Text
url = SubmissionID -> m Submission
forall (m :: * -> *). MonadReddit m => SubmissionID -> m Submission
getSubmission (SubmissionID -> m Submission) -> m SubmissionID -> m Submission
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Text -> m SubmissionID
forall (m :: * -> *). MonadThrow m => Text -> m SubmissionID
submissionIDFromURL Text
url

-- | Get a @Listing@ of submissions based on their domain
getSubmissionsByDomain :: MonadReddit m
                       => Domain
                       -> Paginator SubmissionID Submission
                       -> m (Listing SubmissionID Submission)
getSubmissionsByDomain :: Text
-> Paginator SubmissionID Submission
-> m (Listing SubmissionID Submission)
getSubmissionsByDomain Text
dm Paginator SubmissionID Submission
paginator =
    APIAction (Listing SubmissionID Submission)
-> m (Listing SubmissionID Submission)
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction APIAction Any
forall a. APIAction a
defaultAPIAction
              { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"domain", Text
dm ]
              , $sel:requestData:APIAction :: WithData
requestData  = Paginator SubmissionID Submission -> WithData
forall t a. (Thing t, Paginable a) => Paginator t a -> WithData
paginatorToFormData Paginator SubmissionID Submission
paginator
              }

-- | Get the \"best\" 'Submission's from the frontpage
getBest :: MonadReddit m
        => Paginator SubmissionID Submission
        -> m (Listing SubmissionID Submission)
getBest :: Paginator SubmissionID Submission
-> m (Listing SubmissionID Submission)
getBest Paginator SubmissionID Submission
paginator = APIAction (Listing SubmissionID Submission)
-> m (Listing SubmissionID Submission)
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction APIAction Any
forall a. APIAction a
defaultAPIAction
                              { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"best" ]
                              , $sel:requestData:APIAction :: WithData
requestData  = Paginator SubmissionID Submission -> WithData
forall t a. (Thing t, Paginable a) => Paginator t a -> WithData
paginatorToFormData Paginator SubmissionID Submission
paginator
                              }

-- | Get a submission\'s 'ChildComment's
getChildComments :: MonadReddit m => SubmissionID -> m (Seq ChildComment)
getChildComments :: SubmissionID -> m (Seq ChildComment)
getChildComments SubmissionID
sid = APIAction WithChildren -> m WithChildren
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction @WithChildren APIAction WithChildren
r m WithChildren
-> (WithChildren -> Seq ChildComment) -> m (Seq ChildComment)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> WithChildren -> Seq ChildComment
forall s t a b. Wrapped s t a b => s -> a
wrappedTo
  where
    r :: APIAction WithChildren
r = APIAction Any
forall a. APIAction a
defaultAPIAction { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"comments", SubmissionID -> Text
forall a. ToHttpApiData a => a -> Text
toUrlPiece SubmissionID
sid ] }

-- | Get a @Listing@ of 'Submission's that are marked as duplicates of the given
-- submission
getDuplicateSubmissions :: MonadReddit m => SubmissionID -> m (Seq Submission)
getDuplicateSubmissions :: SubmissionID -> m (Seq Submission)
getDuplicateSubmissions SubmissionID
sid =
    -- This endpoint is very strange. It returns an /array/ of @Listing@s, each of
    -- which contains a single submission in its @children@ field. Having tested it,
    -- it appears that the @after@ field is always @null@. Not sure if this is the
    -- correct way to deal with this, but getting a @Seq@ of subsmissions is a lot
    -- more ergonomic than a nested mess of @Listing@s
    APIAction [Listing SubmissionID Submission]
-> m [Listing SubmissionID Submission]
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction @[Listing SubmissionID Submission] APIAction [Listing SubmissionID Submission]
r m [Listing SubmissionID Submission]
-> ([Listing SubmissionID Submission] -> m (Seq Submission))
-> m (Seq Submission)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        []   -> Seq Submission -> m (Seq Submission)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Seq Submission
forall a. Seq a
Empty
        [Listing SubmissionID Submission]
dups -> Seq Submission -> m (Seq Submission)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Seq Submission -> m (Seq Submission))
-> ([Seq Submission] -> Seq Submission)
-> [Seq Submission]
-> m (Seq Submission)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Seq Submission] -> Seq Submission
forall a. Monoid a => [a] -> a
mconcat ([Seq Submission] -> m (Seq Submission))
-> [Seq Submission] -> m (Seq Submission)
forall a b. (a -> b) -> a -> b
$ [Listing SubmissionID Submission]
dups [Listing SubmissionID Submission]
-> (Listing SubmissionID Submission -> Seq Submission)
-> [Seq Submission]
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Listing SubmissionID Submission
-> Getting
     (Seq Submission) (Listing SubmissionID Submission) (Seq Submission)
-> Seq Submission
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "children" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"children")
  where
    r :: APIAction [Listing SubmissionID Submission]
r = APIAction Any
forall a. APIAction a
defaultAPIAction { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"duplicates", SubmissionID -> Text
forall a. ToHttpApiData a => a -> Text
toUrlPiece SubmissionID
sid ] }

-- | Save a submission
saveSubmission :: MonadReddit m => SubmissionID -> m ()
saveSubmission :: SubmissionID -> m ()
saveSubmission = ItemID -> m ()
forall (m :: * -> *). MonadReddit m => ItemID -> m ()
save (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Unsave a submission
unsaveSubmission :: MonadReddit m => SubmissionID -> m ()
unsaveSubmission :: SubmissionID -> m ()
unsaveSubmission = ItemID -> m ()
forall (m :: * -> *). MonadReddit m => ItemID -> m ()
unsave (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Hide the submissions corresponding to a container of 'SubmissionID's. The
-- submissions will no longer appear in your default view of submissions on the
-- subdreddit
hideSubmissions :: (MonadReddit m, Foldable t) => t SubmissionID -> m ()
hideSubmissions :: t SubmissionID -> m ()
hideSubmissions = Text -> t SubmissionID -> m ()
forall (m :: * -> *) (t :: * -> *).
(MonadReddit m, Foldable t) =>
Text -> t SubmissionID -> m ()
hide Text
"hide"

-- | Hide a single submission
hideSubmission :: MonadReddit m => SubmissionID -> m ()
hideSubmission :: SubmissionID -> m ()
hideSubmission SubmissionID
sid = [SubmissionID] -> m ()
forall (m :: * -> *) (t :: * -> *).
(MonadReddit m, Foldable t) =>
t SubmissionID -> m ()
hideSubmissions [ SubmissionID
sid ]

-- | Unhide the submissions corresponding to a container of 'SubmissionID's,
-- returning them to your default view
unhideSubmissions :: (MonadReddit m, Foldable t) => t SubmissionID -> m ()
unhideSubmissions :: t SubmissionID -> m ()
unhideSubmissions = Text -> t SubmissionID -> m ()
forall (m :: * -> *) (t :: * -> *).
(MonadReddit m, Foldable t) =>
Text -> t SubmissionID -> m ()
hide Text
"unhide"

-- | Unhide a single submission
unhideSubmission :: MonadReddit m => SubmissionID -> m ()
unhideSubmission :: SubmissionID -> m ()
unhideSubmission SubmissionID
sid = [SubmissionID] -> m ()
forall (m :: * -> *) (t :: * -> *).
(MonadReddit m, Foldable t) =>
t SubmissionID -> m ()
unhideSubmissions [ SubmissionID
sid ]

hide :: (MonadReddit m, Foldable t) => Text -> t SubmissionID -> m ()
hide :: Text -> t SubmissionID -> m ()
hide Text
path t SubmissionID
ss =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
path ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", t SubmissionID -> Text
forall a. Thing a => a -> Text
fullname t SubmissionID
ss) ]
               }

-- | Delete a submission that the currently authenticated user has authored
deleteSubmission :: MonadReddit m => SubmissionID -> m ()
deleteSubmission :: SubmissionID -> m ()
deleteSubmission = ItemID -> m ()
forall (m :: * -> *). MonadReddit m => ItemID -> m ()
delete (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Edit a submission, receving an updated 'Submission' in response
editSubmission :: MonadReddit m => SubmissionID -> Body -> m Submission
editSubmission :: SubmissionID -> Text -> m Submission
editSubmission (SubmissionID -> ItemID
SubmissionItemID -> ItemID
sid) Text
txt = ItemID -> Text -> m Item
forall (m :: * -> *). MonadReddit m => ItemID -> Text -> m Item
edit ItemID
sid Text
txt m Item -> (Item -> m Submission) -> m Submission
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    SubmissionItem Submission
s -> Submission -> m Submission
forall (f :: * -> *) a. Applicative f => a -> f a
pure Submission
s
    CommentItem Comment
_    -> ClientException -> m Submission
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM
        (ClientException -> m Submission)
-> ClientException -> m Submission
forall a b. (a -> b) -> a -> b
$ Text -> ClientException
InvalidResponse Text
"editSubmission: Expected a Submission, got a Comment"

-- | Leave a reply on a submission, returning the new 'Comment' that has been
-- created
replyToSubmission :: MonadReddit m => SubmissionID -> Body -> m Comment
replyToSubmission :: SubmissionID -> Text -> m Comment
replyToSubmission = ItemID -> Text -> m Comment
forall (m :: * -> *). MonadReddit m => ItemID -> Text -> m Comment
reply (ItemID -> Text -> m Comment)
-> (SubmissionID -> ItemID) -> SubmissionID -> Text -> m Comment
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Submit a new self-post (with no inline media)
submitSelfPost
    :: MonadReddit m => SubredditName -> Title -> Body -> m Submission
submitSelfPost :: SubredditName -> Text -> Text -> m Submission
submitSelfPost SubredditName
sname Text
t Text
b = NewSubmission -> m Submission
forall (m :: * -> *).
MonadReddit m =>
NewSubmission -> m Submission
submit (NewSubmission -> m Submission) -> NewSubmission -> m Submission
forall a b. (a -> b) -> a -> b
$ Text -> SubmissionOptions -> NewSubmission
SelfPost Text
b (SubredditName -> Text -> SubmissionOptions
mkSubmissionOptions SubredditName
sname Text
t)

-- | Submit a new self-post with inline media. The @Body@ must be markdown-
-- formatted text containing placeholders. Each placeholder must correspond to
-- exactly one of the 'InlineMedia's\' @key@ field. For example, given the
-- following:
--
-- >>> gif = InlineMedia InlineGIF "/path/to/a.gif" "gif" Nothing
-- >>> vid = InlineMedia InlineVideo "/path/to/a.mp4" "vid" (Just "my video")
-- >>> body = "Body with an inline gif #gif# and video #vid#"
-- >>> submitWithInlineMedia Nothing body [gif, vid] myOptions
--
-- Will automatically produce (internally) the following markdown body for
-- the submission, after uploading the media to Reddit\'s servers:
--
-- @
--    Body with an inline gif
--
--    ![gif](j2k2xmfl3 \"\")
--
--     and video
--
--    ![video](ccdoe02xu \"my video\")
-- @
--
submitWithInlineMedia
    :: (MonadReddit m, Traversable t)
    => Maybe Char
    -- ^ Delimiter for the placeholders in the body, defaults to @#@
    -> Body -- ^ Should contain the placeholders, as described above
    -> t InlineMedia
    -- ^ The @key@ field in each 'InlineMedia' must correspond to
    -- exactly one of the placeholders in the body
    -> SubmissionOptions
    -> m Submission
submitWithInlineMedia :: Maybe Char
-> Text -> t InlineMedia -> SubmissionOptions -> m Submission
submitWithInlineMedia (Char -> Text
T.singleton (Char -> Text) -> (Maybe Char -> Char) -> Maybe Char -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Maybe Char -> Char
forall a. a -> Maybe a -> a
fromMaybe Char
'#' -> Text
delim) Text
b t InlineMedia
media SubmissionOptions
sos = do
    t InlineMediaUpload
uploaded <- t InlineMedia
-> (InlineMedia -> m InlineMediaUpload) -> m (t InlineMediaUpload)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for t InlineMedia
media ((InlineMedia -> m InlineMediaUpload) -> m (t InlineMediaUpload))
-> (InlineMedia -> m InlineMediaUpload) -> m (t InlineMediaUpload)
forall a b. (a -> b) -> a -> b
$ \m :: InlineMedia
m@InlineMedia { FilePath
$sel:mediaPath:InlineMedia :: InlineMedia -> FilePath
mediaPath :: FilePath
mediaPath } ->
        InlineMedia -> UploadURL -> InlineMediaUpload
inlineMediaToUpload InlineMedia
m (UploadURL -> InlineMediaUpload)
-> m UploadURL -> m InlineMediaUpload
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> UploadType -> FilePath -> m UploadURL
forall (m :: * -> *).
MonadReddit m =>
UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
SelfPostUpload FilePath
mediaPath
    Fancypants
rtjson <- Text -> m Fancypants
forall (m :: * -> *). MonadReddit m => Text -> m Fancypants
putOnTheFancypants (Text -> m Fancypants) -> Text -> m Fancypants
forall a b. (a -> b) -> a -> b
$ (InlineMediaUpload -> Text -> Text)
-> Text -> t InlineMediaUpload -> Text
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr InlineMediaUpload -> Text -> Text
replacePlaceholders Text
b t InlineMediaUpload
uploaded
    Text -> m Submission
forall (m :: * -> *). MonadReddit m => Text -> m Submission
getSubmissionByURL (Text -> m Submission)
-> (PostedSubmission -> Text) -> PostedSubmission -> m Submission
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PostedSubmission -> Text
forall s t a b. Wrapped s t a b => s -> a
wrappedTo
        (PostedSubmission -> m Submission)
-> m PostedSubmission -> m Submission
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< APIAction PostedSubmission -> m PostedSubmission
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction @PostedSubmission
                      (Form -> APIAction PostedSubmission
forall a. Form -> APIAction a
submissionAction (Form -> APIAction PostedSubmission)
-> (NewSubmission -> Form)
-> NewSubmission
-> APIAction PostedSubmission
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NewSubmission -> Form
forall a. ToForm a => a -> Form
toForm (NewSubmission -> APIAction PostedSubmission)
-> NewSubmission -> APIAction PostedSubmission
forall a b. (a -> b) -> a -> b
$ Fancypants -> SubmissionOptions -> NewSubmission
WithInlineMedia Fancypants
rtjson SubmissionOptions
sos)
  where
    replacePlaceholders :: InlineMediaUpload -> Text -> Text
replacePlaceholders im :: InlineMediaUpload
im@InlineMediaUpload { Text
UploadURL
InlineMediaType
$sel:key:InlineMediaUpload :: InlineMediaUpload -> Text
$sel:caption:InlineMediaUpload :: InlineMediaUpload -> Text
$sel:mediaID:InlineMediaUpload :: InlineMediaUpload -> UploadURL
$sel:mediaType:InlineMediaUpload :: InlineMediaUpload -> InlineMediaType
key :: Text
caption :: Text
mediaID :: UploadURL
mediaType :: InlineMediaType
.. } Text
md =
        Text -> Text -> Text -> Text
T.replace (Text
delim Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
key Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
delim) (InlineMediaUpload -> Text
writeInlineMedia InlineMediaUpload
im) Text
md

putOnTheFancypants :: MonadReddit m => Body -> m Fancypants
putOnTheFancypants :: Text -> m Fancypants
putOnTheFancypants Text
b =
    APIAction Fancypants -> m Fancypants
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction APIAction Any
forall a. APIAction a
defaultAPIAction
              { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"convert_rte_body_format" ]
              , $sel:method:APIAction :: Method
method       = Method
POST
              , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"output_mode", Text
"rtjson")
                                              , (Text
"markdown_text", Text
b)
                                              ]
              }

-- | Submit a new link post
submitLink :: MonadReddit m => SubredditName -> Title -> URL -> m Submission
submitLink :: SubredditName -> Text -> Text -> m Submission
submitLink SubredditName
sname Text
t Text
u = NewSubmission -> m Submission
forall (m :: * -> *).
MonadReddit m =>
NewSubmission -> m Submission
submit (NewSubmission -> m Submission) -> NewSubmission -> m Submission
forall a b. (a -> b) -> a -> b
$ Text -> SubmissionOptions -> NewSubmission
Link Text
u (SubredditName -> Text -> SubmissionOptions
mkSubmissionOptions SubredditName
sname Text
t)

-- | Post an image submission to the subreddit, uploading the image file. This
-- action does not currently return the posted submission upon success
submitImage :: MonadReddit m
            => FilePath -- ^ Must be a valid image file
            -> SubmissionOptions
            -> m ()
submitImage :: FilePath -> SubmissionOptions -> m ()
submitImage FilePath
fp SubmissionOptions
sos = do
    UploadURL
url <- UploadType -> FilePath -> m UploadURL
forall (m :: * -> *).
MonadReddit m =>
UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
LinkUpload FilePath
fp
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ (APIAction () -> m ())
-> (NewSubmission -> APIAction ()) -> NewSubmission -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Form -> APIAction ()
forall a. Form -> APIAction a
submissionAction (Form -> APIAction ())
-> (NewSubmission -> Form) -> NewSubmission -> APIAction ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NewSubmission -> Form
forall a. ToForm a => a -> Form
toForm (NewSubmission -> m ()) -> NewSubmission -> m ()
forall a b. (a -> b) -> a -> b
$ UploadURL -> SubmissionOptions -> NewSubmission
ImagePost UploadURL
url SubmissionOptions
sos

-- | Post an image submission to the subreddit, uploading the image file. This
-- action does not currently return the posted submission upon success
submitVideo :: MonadReddit m
            => Bool -- ^ If @True@, creates a silent \"videogif\"
            -> FilePath -- ^ Must be a valid video file
            -> Maybe FilePath
            -- ^ Must be a valid image file, for the video thumbnail. If
            -- @Nothing@, a PNG of the Haskell logo will be used
            -> SubmissionOptions
            -> m ()
submitVideo :: Bool -> FilePath -> Maybe FilePath -> SubmissionOptions -> m ()
submitVideo Bool
videogif FilePath
fp Maybe FilePath
thmbFP SubmissionOptions
sos = do
    UploadURL
url <- UploadType -> FilePath -> m UploadURL
forall (m :: * -> *).
MonadReddit m =>
UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
LinkUpload FilePath
fp
    UploadURL
thmbURL <- UploadType -> FilePath -> m UploadURL
forall (m :: * -> *).
MonadReddit m =>
UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
LinkUpload (FilePath -> m UploadURL) -> m FilePath -> m UploadURL
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< m FilePath
-> (FilePath -> m FilePath) -> Maybe FilePath -> m FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe m FilePath
defaultPNG FilePath -> m FilePath
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe FilePath
thmbFP
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ (APIAction () -> m ())
-> (NewSubmission -> APIAction ()) -> NewSubmission -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Form -> APIAction ()
forall a. Form -> APIAction a
submissionAction (Form -> APIAction ())
-> (NewSubmission -> Form) -> NewSubmission -> APIAction ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NewSubmission -> Form
forall a. ToForm a => a -> Form
toForm
        (NewSubmission -> m ()) -> NewSubmission -> m ()
forall a b. (a -> b) -> a -> b
$ UploadURL
-> UploadURL -> Bool -> SubmissionOptions -> NewSubmission
VideoPost UploadURL
url UploadURL
thmbURL Bool
videogif SubmissionOptions
sos
  where
    defaultPNG :: m FilePath
defaultPNG = IO FilePath -> m FilePath
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO FilePath -> m FilePath) -> IO FilePath -> m FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> IO FilePath
getDataFileName FilePath
"assets/haskell.png"

-- | Post a poll to the subreddit. See 'mkPoll' to create a new 'Poll'
submitPoll
    :: (MonadReddit m, Foldable t) => Poll t -> SubmissionOptions -> m ()
submitPoll :: Poll t -> SubmissionOptions -> m ()
submitPoll Poll t
poll SubmissionOptions
sos =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"submit_poll_post.json" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = Value -> WithData
WithJSON (Value -> WithData)
-> (PollSubmission t -> Value) -> PollSubmission t -> WithData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PollSubmission t -> Value
forall a. ToJSON a => a -> Value
toJSON (PollSubmission t -> WithData) -> PollSubmission t -> WithData
forall a b. (a -> b) -> a -> b
$ Poll t -> SubmissionOptions -> PollSubmission t
forall (t :: * -> *).
Poll t -> SubmissionOptions -> PollSubmission t
PollSubmission Poll t
poll SubmissionOptions
sos
               }

-- | Post a gallery to the subreddit, given a container of 'GalleryImage's. See
-- 'mkGalleryImage' to create the images with default values. This action also
-- ensures that the image container has at least two elements
submitGallery :: (MonadReddit m, Traversable t)
              => t GalleryImage
              -> SubmissionOptions
              -> m ()
submitGallery :: t GalleryImage -> SubmissionOptions -> m ()
submitGallery t GalleryImage
imgs SubmissionOptions
sos = do
    Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (t GalleryImage -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t GalleryImage
imgs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2) (m () -> m ())
-> (ClientException -> m ()) -> ClientException -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientException -> m ()
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM
        (ClientException -> m ()) -> ClientException -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> ClientException
InvalidRequest --
        Text
"submitGallery: Galleries must consist of at least 2 images"
    t GalleryUploadImage
gallery <- t GalleryImage
-> (GalleryImage -> m GalleryUploadImage)
-> m (t GalleryUploadImage)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for t GalleryImage
imgs ((GalleryImage -> m GalleryUploadImage)
 -> m (t GalleryUploadImage))
-> (GalleryImage -> m GalleryUploadImage)
-> m (t GalleryUploadImage)
forall a b. (a -> b) -> a -> b
$ \img :: GalleryImage
img@GalleryImage { FilePath
$sel:imagePath:GalleryImage :: GalleryImage -> FilePath
imagePath :: FilePath
imagePath } ->
        GalleryImage -> UploadURL -> GalleryUploadImage
galleryImageToUpload GalleryImage
img (UploadURL -> GalleryUploadImage)
-> m UploadURL -> m GalleryUploadImage
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> UploadType -> FilePath -> m UploadURL
forall (m :: * -> *).
MonadReddit m =>
UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
GalleryUpload FilePath
imagePath
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"submit_gallery_post.json" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  =
                     Value -> WithData
WithJSON (Value -> WithData)
-> (GallerySubmission t -> Value)
-> GallerySubmission t
-> WithData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GallerySubmission t -> Value
forall a. ToJSON a => a -> Value
toJSON (GallerySubmission t -> WithData)
-> GallerySubmission t -> WithData
forall a b. (a -> b) -> a -> b
$ t GalleryUploadImage -> SubmissionOptions -> GallerySubmission t
forall (t :: * -> *).
t GalleryUploadImage -> SubmissionOptions -> GallerySubmission t
GallerySubmission t GalleryUploadImage
gallery SubmissionOptions
sos
               }

-- | Submit a new submission, returning the 'Submission' that has been created.
-- This action allows for more fine-grained control over submission options. You
-- can use 'mkSubmissionOptions' for defaults and update it as needed. See also
-- 'submitImage', 'submitPoll', and 'submitGallery', which should be used when
-- submitting anything beyond a self-text or link post
submit :: MonadReddit m => NewSubmission -> m Submission
submit :: NewSubmission -> m Submission
submit (NewSubmission -> Form
forall a. ToForm a => a -> Form
toForm -> Form
n) = Text -> m Submission
forall (m :: * -> *). MonadReddit m => Text -> m Submission
getSubmissionByURL (Text -> m Submission)
-> (PostedSubmission -> Text) -> PostedSubmission -> m Submission
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PostedSubmission -> Text
forall s t a b. Wrapped s t a b => s -> a
wrappedTo
    (PostedSubmission -> m Submission)
-> m PostedSubmission -> m Submission
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< APIAction PostedSubmission -> m PostedSubmission
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction @PostedSubmission (Form -> APIAction PostedSubmission
forall a. Form -> APIAction a
submissionAction Form
n)

-- | Crosspost an existing submission. You must be a subscriber of the subreddit
-- you are posting into. See also 'mkCrosspostOptions'
crosspost :: MonadReddit m => SubmissionID -> CrosspostOptions -> m Submission
crosspost :: SubmissionID -> CrosspostOptions -> m Submission
crosspost SubmissionID
sid CrosspostOptions
cpos = SubmissionID -> m Submission
forall (m :: * -> *). MonadReddit m => SubmissionID -> m Submission
getSubmission
    (SubmissionID -> m Submission) -> m SubmissionID -> m Submission
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (APIAction PostedCrosspost -> m PostedCrosspost
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction @PostedCrosspost APIAction PostedCrosspost
r m PostedCrosspost
-> (PostedCrosspost -> SubmissionID) -> m SubmissionID
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> PostedCrosspost -> SubmissionID
forall s t a b. Wrapped s t a b => s -> a
wrappedTo)
  where
    r :: APIAction PostedCrosspost
r = Form -> APIAction PostedCrosspost
forall a. Form -> APIAction a
submissionAction
        (Form -> APIAction PostedCrosspost)
-> Form -> APIAction PostedCrosspost
forall a b. (a -> b) -> a -> b
$ CrosspostOptions -> Form
forall a. ToForm a => a -> Form
toForm CrosspostOptions
cpos Form -> Form -> Form
forall a. Semigroup a => a -> a -> a
<> [(Text, Text)] -> Form
mkTextForm [ (Text
"crosspost_fullname", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid) ]

submissionAction :: Form -> APIAction a
submissionAction :: Form -> APIAction a
submissionAction Form
form = APIAction Any
forall a. APIAction a
defaultAPIAction
    { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"submit" ]
    , $sel:method:APIAction :: Method
method       = Method
POST
    , $sel:requestData:APIAction :: WithData
requestData  = Form -> WithData
WithForm Form
form
    }

-- Calls an undocumented API endpoint to upload media to an s3 bucket. It returns
-- the URL of the uploaded media along with a a websocket URL that can be used
-- to detect the upload completion
uploadMedia :: MonadReddit m => UploadType -> FilePath -> m UploadURL
uploadMedia :: UploadType -> FilePath -> m UploadURL
uploadMedia UploadType
uty FilePath
fp = do
    Text
mimetype <- case FilePath -> FilePath
FP.takeExtension FilePath
fp of
        FilePath
ext
            | Just Text
mt <- FilePath -> Map FilePath Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup FilePath
ext Map FilePath Text
mimeMap -> Text -> m Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
mt
            | Bool
otherwise ->
                ClientException -> m Text
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ClientException -> m Text) -> ClientException -> m Text
forall a b. (a -> b) -> a -> b
$ Text -> ClientException
InvalidRequest Text
"uploadMedia: invalid media file type"

    S3UploadLease { Text
HashMap Text Text
UploadURL
$sel:assetID:S3UploadLease :: S3UploadLease -> UploadURL
$sel:websocketURL:S3UploadLease :: S3UploadLease -> Text
$sel:key:S3UploadLease :: S3UploadLease -> Text
$sel:fields:S3UploadLease :: S3UploadLease -> HashMap Text Text
$sel:action:S3UploadLease :: S3UploadLease -> Text
assetID :: UploadURL
websocketURL :: Text
key :: Text
fields :: HashMap Text Text
action :: Text
.. }
        <- APIAction S3UploadLease -> m S3UploadLease
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction APIAction Any
forall a. APIAction a
defaultAPIAction
                     { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"media", Text
"asset.json" ]
                     , $sel:method:APIAction :: Method
method       = Method
POST
                     , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData --
                           [ (Text
"filepath", FilePath -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
FP.takeFileName FilePath
fp)
                           , (Text
"mimetype", Text
mimetype)
                           ]
                     }

    (ByteString
url, [Text]
ps) <- Text -> m (ByteString, [Text])
forall (m :: * -> *).
MonadThrow m =>
Text -> m (ByteString, [Text])
splitURL Text
action
    m (Response (RawBody m)) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (Response (RawBody m)) -> m ())
-> (Request -> m (Response (RawBody m))) -> Request -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> m (Response (RawBody m))
forall (m :: * -> *).
MonadReddit m =>
Request -> m (Response (RawBody m))
runActionWith_
        (Request -> m ()) -> m Request -> m ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ByteString -> APIAction Any -> m Request
forall (m :: * -> *) a.
MonadIO m =>
ByteString -> APIAction a -> m Request
mkRequest ByteString
url
                      APIAction Any
forall a. APIAction a
defaultAPIAction
                      { $sel:pathSegments:APIAction :: [Text]
pathSegments = [Text]
ps
                      , $sel:method:APIAction :: Method
method       = Method
POST
                      , $sel:requestData:APIAction :: WithData
requestData  = [Part] -> WithData
WithMultipart
                            ([Part] -> WithData) -> [Part] -> WithData
forall a b. (a -> b) -> a -> b
$ (Text -> Text -> [Part] -> [Part])
-> [Part] -> HashMap Text Text -> [Part]
forall k v a. (k -> v -> a -> a) -> a -> HashMap k v -> a
HM.foldrWithKey Text -> Text -> [Part] -> [Part]
forall (m :: * -> *).
Applicative m =>
Text -> Text -> [PartM m] -> [PartM m]
mkParts
                                              [ Text -> FilePath -> Part
partFile Text
"file" FilePath
fp ]
                                              HashMap Text Text
fields
                      , $sel:rawJSON:APIAction :: Bool
rawJSON      = Bool
False
                      }
    UploadURL -> m UploadURL
forall (f :: * -> *) a. Applicative f => a -> f a
pure (UploadURL -> m UploadURL) -> UploadURL -> m UploadURL
forall a b. (a -> b) -> a -> b
$ case UploadType
uty of
        UploadType
LinkUpload -> Text -> UploadURL
forall s t a b. Wrapped s t a b => b -> t
wrappedFrom (Text -> UploadURL) -> Text -> UploadURL
forall a b. (a -> b) -> a -> b
$ Text -> [Text] -> Text
T.intercalate Text
"/" [ Text
action, Text
key ]
        UploadType
_          -> UploadURL
assetID
  where
    mimeMap :: Map FilePath Text
mimeMap               =
        [(FilePath, Text)] -> Map FilePath Text
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [ (FilePath
".png", Text
"image/png")
                   , (FilePath
".mov", Text
"video/quicktime")
                   , (FilePath
".mp4", Text
"video/mp4")
                   , (FilePath
".jpg", Text
"image/jpeg")
                   , (FilePath
".jpeg", Text
"image/jpeg")
                   , (FilePath
".gif", Text
"image/gif")
                   ]

    mkParts :: Text -> Text -> [PartM m] -> [PartM m]
mkParts Text
name Text
value [PartM m]
ps = Text -> ByteString -> PartM m
forall (m :: * -> *).
Applicative m =>
Text -> ByteString -> PartM m
partBS Text
name (Text -> ByteString
T.encodeUtf8 Text
value) PartM m -> [PartM m] -> [PartM m]
forall a. a -> [a] -> [a]
: [PartM m]
ps

-- | Enable/disable inbox replies for a submission
setSubmissionReplies :: MonadReddit m => Bool -> SubmissionID -> m ()
setSubmissionReplies :: Bool -> SubmissionID -> m ()
setSubmissionReplies Bool
p = Bool -> ItemID -> m ()
forall (m :: * -> *). MonadReddit m => Bool -> ItemID -> m ()
setInboxReplies Bool
p (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Select a 'FlairChoice' for a submission.
selectSubmissionFlair
    :: MonadReddit m => FlairSelection -> SubmissionID -> m ()
selectSubmissionFlair :: FlairSelection -> SubmissionID -> m ()
selectSubmissionFlair (FlairSelection FlairChoice { Bool
Maybe Text
Text
FlairText
$sel:cssClass:FlairChoice :: FlairChoice -> Maybe Text
$sel:textEditable:FlairChoice :: FlairChoice -> Bool
$sel:text:FlairChoice :: FlairChoice -> FlairText
$sel:templateID:FlairChoice :: FlairChoice -> Text
cssClass :: Maybe Text
textEditable :: Bool
text :: FlairText
templateID :: Text
.. } Maybe Text
_ SubredditName
sname) SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = SubredditName -> Text -> [Text]
subAPIPath SubredditName
sname Text
"flairselector"
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = Form -> WithData
WithForm
                     (Form -> WithData) -> Form -> WithData
forall a b. (a -> b) -> a -> b
$ [(Text, Text)] -> Form
mkTextForm [ (Text
"link", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid)
                                  , ( Text
"flair_template_id"
                                    , Text -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam Text
templateID
                                    )
                                  ]
               }

-- | 'Search' through Reddit 'Submission's, either side-wide or constrained to
-- one subreddit. See 'mkSearch' to create the initial 'Search'
search :: MonadReddit m
       => Search
       -> Paginator ResultID Submission
       -> m (Listing ResultID Submission)
search :: Search
-> Paginator ResultID Submission -> m (Listing ResultID Submission)
search sch :: Search
sch@Search { Maybe SubredditName
Maybe SearchSyntax
Text
$sel:syntax:Search :: Search -> Maybe SearchSyntax
$sel:subreddit:Search :: Search -> Maybe SubredditName
$sel:q:Search :: Search -> Text
syntax :: Maybe SearchSyntax
subreddit :: Maybe SubredditName
q :: Text
.. } Paginator ResultID Submission
paginator =
    APIAction (Listing ResultID Submission)
-> m (Listing ResultID Submission)
forall a (m :: * -> *).
(MonadReddit m, FromJSON a) =>
APIAction a -> m a
runAction APIAction Any
forall a. APIAction a
defaultAPIAction { [Text]
pathSegments :: [Text]
$sel:pathSegments:APIAction :: [Text]
pathSegments, WithData
requestData :: WithData
$sel:requestData:APIAction :: WithData
requestData }
  where
    pathSegments :: [Text]
pathSegments = [ Text
"search" ]
        [Text] -> ([Text] -> [Text]) -> [Text]
forall a b. a -> (a -> b) -> b
& ([Text] -> [Text])
-> (SubredditName -> [Text] -> [Text])
-> Maybe SubredditName
-> [Text]
-> [Text]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Text] -> [Text]
forall a. a -> a
id (\SubredditName
s -> [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
(<>) [ Text
"r", SubredditName -> Text
forall a. ToHttpApiData a => a -> Text
toUrlPiece SubredditName
s ]) Maybe SubredditName
subreddit

    requestData :: WithData
requestData  = Form -> WithData
WithForm (Form -> WithData) -> Form -> WithData
forall a b. (a -> b) -> a -> b
$ Paginator ResultID Submission -> Form
forall a. ToForm a => a -> Form
toForm Paginator ResultID Submission
paginator Form -> Form -> Form
forall a. Semigroup a => a -> a -> a
<> Search -> Form
forall a. ToForm a => a -> Form
toForm Search
sch

-- | Upvote a submission
upvoteSubmission :: MonadReddit m => SubmissionID -> m ()
upvoteSubmission :: SubmissionID -> m ()
upvoteSubmission = Vote -> ItemID -> m ()
forall (m :: * -> *). MonadReddit m => Vote -> ItemID -> m ()
vote Vote
Upvote (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Downvote a submission
downvoteSubmission :: MonadReddit m => SubmissionID -> m ()
downvoteSubmission :: SubmissionID -> m ()
downvoteSubmission = Vote -> ItemID -> m ()
forall (m :: * -> *). MonadReddit m => Vote -> ItemID -> m ()
vote Vote
Downvote (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Remove an existing vote on a submission
unvoteSubmission :: MonadReddit m => SubmissionID -> m ()
unvoteSubmission :: SubmissionID -> m ()
unvoteSubmission = Vote -> ItemID -> m ()
forall (m :: * -> *). MonadReddit m => Vote -> ItemID -> m ()
vote Vote
Unvote (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
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 :: Award -> SubmissionID -> m AwardingsSummary
awardSubmission Award
a = Award -> ItemID -> m AwardingsSummary
forall (m :: * -> *).
MonadReddit m =>
Award -> ItemID -> m AwardingsSummary
award Award
a (ItemID -> m AwardingsSummary)
-> (SubmissionID -> ItemID) -> SubmissionID -> m AwardingsSummary
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
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 :: SubmissionID -> m AwardingsSummary
gildSubmission = ItemID -> m AwardingsSummary
forall (m :: * -> *). MonadReddit m => ItemID -> m AwardingsSummary
gild (ItemID -> m AwardingsSummary)
-> (SubmissionID -> ItemID) -> SubmissionID -> m AwardingsSummary
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Report a submission to the subreddit\'s mods
reportSubmission :: MonadReddit m => Report -> SubmissionID -> m ()
reportSubmission :: Report -> SubmissionID -> m ()
reportSubmission Report
r = Report -> ItemID -> m ()
forall (m :: * -> *). MonadReddit m => Report -> ItemID -> m ()
report Report
r (ItemID -> m ())
-> (SubmissionID -> ItemID) -> SubmissionID -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SubmissionID -> ItemID
SubmissionItemID

-- | Mark a submission NSFW. The submission author can use this as well as the
-- subreddit moderators
markNSFW :: MonadReddit m => SubmissionID -> m ()
markNSFW :: SubmissionID -> m ()
markNSFW SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"marknsfw" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid) ]
               }

-- | Unmark a submission NSFW. The submission author can use this as well as the
-- subreddit moderators
unmarkNSFW :: MonadReddit m => SubmissionID -> m ()
unmarkNSFW :: SubmissionID -> m ()
unmarkNSFW SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"unmarknsfw" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid) ]
               }

-- | Mark a submission as original content. In order for normal users to use
-- this feature in addition to mods, the beta \"Original Content\" feature must
-- be enabled in the subreddit settings
setOC :: MonadReddit m => SubredditName -> SubmissionID -> m ()
setOC :: SubredditName -> SubmissionID -> m ()
setOC = Bool -> SubredditName -> SubmissionID -> m ()
forall (m :: * -> *).
MonadReddit m =>
Bool -> SubredditName -> SubmissionID -> m ()
setUnsetOC Bool
True

-- | Unmark a submission as original content. In order for normal users to use
-- this feature in addition to mods, the beta \"Original Content\" feature must
-- be enabled in the subreddit settings
unsetOC :: MonadReddit m => SubredditName -> SubmissionID -> m ()
unsetOC :: SubredditName -> SubmissionID -> m ()
unsetOC = Bool -> SubredditName -> SubmissionID -> m ()
forall (m :: * -> *).
MonadReddit m =>
Bool -> SubredditName -> SubmissionID -> m ()
setUnsetOC Bool
False

setUnsetOC :: MonadReddit m => Bool -> SubredditName -> SubmissionID -> m ()
setUnsetOC :: Bool -> SubredditName -> SubmissionID -> m ()
setUnsetOC Bool
shouldSet SubredditName
sname SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"set_original_content" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  =
                     [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", SubmissionID -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam SubmissionID
sid)
                                    , (Text
"fullname", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid)
                                    , ( Text
"should_set_oc"
                                      , Bool -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam Bool
shouldSet
                                      )
                                    , (Text
"execute", Bool -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam Bool
False)
                                    , (Text
"r", SubredditName -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam SubredditName
sname)
                                    ]
               }

-- | Mark the submission as containing spoilers
setSpoiler :: MonadReddit m => SubmissionID -> m ()
setSpoiler :: SubmissionID -> m ()
setSpoiler SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"spoiler" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid) ]
               }

-- | Unmark the submission as containing spoilers
unsetSpoiler :: MonadReddit m => SubmissionID -> m ()
unsetSpoiler :: SubmissionID -> m ()
unsetSpoiler SubmissionID
sid =
    APIAction () -> m ()
forall (m :: * -> *). MonadReddit m => APIAction () -> m ()
runAction_ APIAction Any
forall a. APIAction a
defaultAPIAction
               { $sel:pathSegments:APIAction :: [Text]
pathSegments = [ Text
"api", Text
"unspoiler" ]
               , $sel:method:APIAction :: Method
method       = Method
POST
               , $sel:requestData:APIAction :: WithData
requestData  = [(Text, Text)] -> WithData
mkTextFormData [ (Text
"id", SubmissionID -> Text
forall a. Thing a => a -> Text
fullname SubmissionID
sid) ]
               }
--
-- $vote
-- __Note__: According to Reddit\'s API rules:
--
-- votes must be cast by humans. That is, API clients proxying a human's
-- action one-for-one are OK, but bots deciding how to vote on content or amplifying
-- a human's vote are not. See the reddit rules for more details on what constitutes
-- vote cheating.
--