heddit-0.0.2: README.org
#+TITLE: heddit
~heddit~ is a Reddit API client library for Haskell. It aims to be (almost) as comprehensive as libraries such as [[https://github.com/praw-dev/praw][praw]], from which it takes major inspiration. Although it is not quite there, it does offer access to various parts of the Reddit API, including:
- comments and submissions
- user accounts and private messages
- subreddits, multireddits, wikis, collections, and sub emojis
- live threads
- moderation and various mod actions
- ... and more!
To start using ~heddit~, visit [[file:./doc/start.org][the (very) quick start guide]] or browse the haddocks in [[file:./src/Network][src/Network]]
*WARNING*: ~heddit~ is /definitely/ alpha-quality at the moment. Expect bugs, instability, and incomplete documentation for now!
* Table of Contents
- [[Brief example]]
- [[file:./doc/start.org][Quick start]]
- [[file:./doc/auth.org][Authenticating]]
- [[Caveats]]
- [[Examples]]
- [[Helpful libraries]]
- [[Tests]]
- [[License]]
* Brief example
We can briefly demonstrate what you can do with ~heddit~ by getting a list of r/haskell's moderators, printing out some information about them, and then finding out which mod has the highest link karma. You can also directly run this example with [[file:./examples/Mods.hs][the mods example program]] that contains the same code.
#+begin_src haskell
{-# LANGUAGE OverloadedLabels #-}
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import Data.Foldable ( for_, maximumBy )
import Data.Generics.Labels ()
import Data.Generics.Wrapped
import Data.Ord ( comparing )
import qualified Data.Text.IO as T
import Lens.Micro.Platform
import Network.Reddit
-- This module is not exported by default from Network.Reddit, as it exports
-- quite a large number of actions and types
import Network.Reddit.Moderation ( getModerators )
-- This is a small example of how I would use the library. Note that I'm using
-- @generic-lens@ and @microlens-platform@ for record access. This isn't entirely
-- necessary, but I decided to hide record selectors by default. This library
-- defines a lot of record types, and exporting all of the selectors would
-- steal a lot of top-level names in calling code. If you want to use them,
-- you can import the relevant modules directly. For this small example, if
-- you didn't want to use the generic-lens-based approach I'm using, you
-- would need to import Network.Reddit.Types.Account (Account(..))
main :: IO ()
main =
-- First we'll use @loadClient@ to get a @Client@ by reading the auth
-- details from an @auth.ini@ file either in the working directory or in
-- $XDG_DATA_HOME/heddit. @Nothing@ indicates that we'll use the section
-- marked @[DEFAULT]@ in the ini file.
--
-- @runReddit@ takes a @Client@ and a @RedditT@ action to run, which
-- in this example is @modsInfo@ below
loadClient Nothing >>= (`runReddit` modsKarmaInfo)
where
modsKarmaInfo = do
mods <-
-- First, we'll get a @Seq ModAccount@, which is like an
-- @Account@ but has less information
(getModerators =<< mkSubredditName "haskell")
-- So we'll pipe each one into @getUser@ by extracting
-- the username
>>= traverse (getUser . (^. #username))
-- Then, we can print the @linkKarma@ for each account
-- (I'm using @wrappedTo@ from Data.Generics.Wrapped,
-- which generically unwraps single-constructor types,
-- to unwrap the @Username@ and get the @Text@ value)
for_ mods $ \m -> liftIO . T.putStrLn
$ mconcat [ m ^. #username & wrappedTo
, " has "
, m ^. #linkKarma . to show . packed
]
-- Finally, we can see which mod account has the most
-- link karma by comparing the sequence of mods using
-- the @topKarma@ function below
liftIO . T.putStrLn
$ mconcat [ "The mod with the most karma is "
, topKarma mods ^. #username & wrappedTo
]
topKarma = maximumBy (comparing (^. #linkKarma))
#+end_src
See the [[Examples]] section below for more.
* Caveats
There are some major caveats to using ~heddit~ that you may want to consider before deciding to use it, namely:
- 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.
* 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:
- The [[file:./examples/Mods.hs][mods example]] shown above
- An [[file:./examples/Paginating.hs][example]] showing how to use ~Listing t a~ and ~Paginator t a~ to paginate through data returned from Reddit (or avoid it altogether).
- Infinitely [[file:./examples/Streaming.hs][streaming]] paginable things as they are created
- Getting an initial [[file:./examples/RefreshTokens.hs][refresh token]]
All of the example programs live in the [[file:./examples][examples]] directory and can be run using, for example, ~cabal run exes:<name>~
* Tests
~heddit~ has two test suites: a simple test of JSON decoding of API responses that runs by default, and the ~io-tests~ that are disabled by default behind a flag of the same name. If you wish to run the ~io-tests~, be aware that these make real requests against Reddit's API endpoints using the authenticated account. After enabling the tests, make sure to create an ~auth.ini~ and ~profile.json~ in the working directory or in ~$XDG_CONFIG_HOME/heddit~. See the ~io-tests~ modules for more info.
* Helpful libraries
There are some other libraries that you may wish to use with ~heddit~:
- ~containers~, for ~Seq~
- ~conduit~, if you wish to [[file:./src/Network/Reddit.hs::stream][stream]] Reddit actions
- ~exceptions~, for ~MonadThrow~ and ~MonadCatch~
- Possibly ~unliftio~, for the ~MonadUnliftIO~ constraint, although this is also exported by ~conduit~. This is really only needed for initializing a ~Client~, which you could just do at the top-level in IO anyway
- ~generic-lens~ if you want to use the lens labels as demonstrated above. Also provides several other helpful lenses and functions for dealing with data generically. Note that the ~generic-lens~ labels are not used internally by ~heddit~, so it is safe to use with other libraries that rely on ~-XOverloadedLabels~
- ~microlens-platform~, ~lens~, or another lens-compatible lib to use with ~generic-lens~, should you choose to use the latter
* License
~heddit~ is distributed under a three-clause BSD license. See [[file:LICENSE][the license]] for details. Parts of this library are directly inspired by and/or adapted from ~praw~, which is distributed under a simplified BSD two-clause license. See [[https://github.com/praw-dev/praw/blob/master/LICENSE.txt]]