snoowrap

repository·master·Indexed 21 days ago

https://github.com/not-an-aardvark/snoowrap

A fully-featured JavaScript wrapper for the Reddit API (version 1.23.0) that provides a non-blocking, asynchronous interface for Node.js and browser environments. It features a fluent API using lazy objects and ES6 Proxies, support for Reddit live threads via EventEmitters, and specialized classes for Reddit entities like Submission, Comment, Subreddit, and RedditUser.

Tokens
8.2K
Snippets
29
Records
36
Agent score
74%

What's inside snoowrap

  1. How RedditContent objects interact with the snoowrap instance

    master

    Most content objects (like Comment or Submission) inherit from the RedditContent class. A key feature of these objects is that they maintain a reference to the original snoowrap instance that created them via the _r property.

    This allows you to perform actions directly on the content object (e.g., comment.upvote()), which internally uses the parent snoowrap instance to execute the necessary OAuth requests.

    Additionally, RedditContent objects implement a lazy-loading pattern via the fetch() method. The first call to fetch() sets the _fetch property to a Promise; subsequent calls return that same Promise to ensure the object is not fetched multiple times unless refresh() is explicitly called.

    // Accessing the parent snoowrap instance from a content object
    r.getComment('abcdef')._r === r
  2. How snoowrap transforms Reddit data into Content objects

    master

    Snoowrap uses an internal method snoowrap#_populate to transform raw JSON responses from Reddit into rich, typed JavaScript objects (e.g., Comment, Submission, RedditUser, Subreddit). This process replaces raw data representations with specialized snoowrap objects that provide a more intuitive API for interacting with Reddit content.

    For example, a JSON response containing author and subreddit information is automatically populated into a Comment object where author and subreddit are themselves instances of RedditUser and Subreddit respectively.

    // Input JSON from Reddit
    {
        "kind": "t1",
        "data": {
            "author": "not_an_aardvark",
            "approved_by": "not_an_aardvark",
            "subreddit": "AskReddit",
        }
    }
    
    // Resulting snoowrap object
    Comment {
      author: RedditUser {name: 'not_an_aardvark'},
      approved_by: RedditUser {name: 'not_an_aardvark'},
      subreddit: Subreddit {display_name: 'AskReddit'}
    }
  3. How snoowrap's lazy objects and method chaining work

    master

    snoowrap uses lazy objects and ES6 Proxy to provide a highly consistent and fluent API. Most property accessors and method calls return Bluebird Promises.

    Fluent Syntax and Chaining

    You can chain actions together for a clean, readable syntax. For example, when creating a post, you can chain methods like .sticky(), .distinguish(), and .assignFlair().

    Lazy Loading

    Accessing a property like .author.name on a submission doesn't immediately fetch the data; it returns a Promise that resolves to the value. You can chain multiple API calls together in a single expression.

    ES6 Proxy Support

    In environments supporting ES6 Proxies (Node 6+, Chrome 49+, Firefox 18+), you can access nested properties directly. In older environments (like Node 4/5 without the --harmony_proxies flag), you must use the .fetch() method to resolve the object before accessing properties.

    // With Proxy support (cleaner)
    r.getSubmission('47v7tm').comments[0].upvote();
    
    // Without Proxy support (heavier)
    r.getSubmission('47v7tm').fetch().then(submission => {
      return submission.comments[0].upvote();
    });
  4. Set up snoowrap for local development and testing

    master

    To contribute to snoowrap or run its test suite locally, follow these steps:

    1. Clone and Install:

      git clone https://github.com/not-an-aardvark/snoowrap.git
      cd snoowrap/
      npm install
    2. Configure OAuth Credentials: Since tests run live on Reddit, you must create an oauth_info.json file in the project root. You can use reddit-oauth-helper to generate these credentials.

      oauth_info.json structure:

      {
        "client_id": "put_your_client_id_here",
        "client_secret": "put_your_client_secret_here",
        "refresh_token": "put_your_refresh_token_here",
        "user_agent": "put_a_descriptive_useragent_string_here",
        "username": "put a username here",
        "password": "put a password here",
        "redirect_uri": "put the redirect URI here",
        "installed_app_client_id": "put_your_installed_app_client_id_here"
      }
    3. Run Tests:

      npm test

      Note: Some tests (like retrieving private messages) may fail with a 403 error if your account lacks permission.

    git clone https://github.com/not-an-aardvark/snoowrap.git
    cd snoowrap/
    npm install
    # Create oauth_info.json then:
    npm test
  5. Install snoowrap

    master

    Node.js

    Install via npm:

    npm install snoowrap --save

    Then require it in your project:

    var snoowrap = require('snoowrap');

    Browsers

    You can use snoowrap with module bundlers like Browserify, or use prebuilt versions available via URL. When used in a browser, snoowrap is assigned to the global window.snoowrap variable. To avoid global state conflicts, you can call snoowrap.noConflict() to restore the previous value of window.snoowrap.

  6. Initialize a snoowrap requester

    master

    To use snoowrap, you must create a new instance with OAuth credentials. It is recommended to use environment variables or a separate config file rather than hardcoding credentials.

    Pass userAgent, clientId, clientSecret, and refreshToken to the constructor.

    Using Username and Password (Script-type apps)

    Pass userAgent, clientId, clientSecret, username, and password to the constructor.

    const snoowrap = require('snoowrap');
    
    // OAuth Refresh Token method
    const r = new snoowrap({
      userAgent: 'put your user-agent string here',
      clientId: 'put your client id here',
      clientSecret: 'put your client secret here',
      refreshToken: 'put your refresh token here'
    });
    
    // Username/Password method
    const otherRequester = new snoowrap({
      userAgent: 'put your user-agent string here',
      clientId: 'put your client id here',
      clientSecret: 'put your client secret here',
      username: 'put your username here',
      password: 'put your password here'
    });
  7. Understand the VoteableContent class

    master
    The VoteableContent<T> class represents Reddit content that can be voted on (upvoted/downvoted), saved, or gilded. It extends ReplyableContent<T>, meaning it also supports replies. This class is the base for objects like posts or comments. It provides metadata about the content's author, subreddit, score, and moderation status.
  8. Common snoowrap usage examples

    master

    Below are common patterns for interacting with the Reddit API using snoowrap:

    • Submitting a link: r.getSubreddit('name').submitLink({title, url})
    • Getting hot posts: r.getHot() returns a collection you can iterate over.
    • Expanding comments: r.getSubmission('id').expandReplies({limit, depth})
    • Moderation: Use methods like .remove(), .banUser(), and .approve() on items retrieved from the modqueue.
    • Wiki pages: r.getSubreddit('name').getWikiPage('page_name').content_md
    // Submitting a link
    r.getSubreddit('gifs').submitLink({
      title: 'Mt. Cameramanjaro',
      url: 'https://i.imgur.com/n5iOc72.gifv'
    });
    
    // Printing hot post titles
    r.getHot().map(post => post.title).then(console.log);
    
    // Automating moderation
    r.getSubreddit('some_subreddit_name').getModqueue({limit: 100}).filter(someRemovalCondition).forEach(flaggedItem => {
      flaggedItem.remove();
      flaggedItem.subreddit.banUser(flaggedItem.author);
    });
    
    // Creating a stickied thread with flair
    r.getSubreddit('some_subreddit_name')
      .submitSelfpost({title: 'Daily thread', text: 'Discuss things here'})
      .sticky()
      .distinguish()
      .approve()
      .assignFlair({text: 'Daily Thread flair text', css_class: 'daily-thread'})
      .reply('This is a comment that appears on that daily thread');
  9. Use internal helper methods for HTTP requests

    master

    The snoowrap base class provides helper methods for common HTTP verbs to facilitate custom requests. These methods are wrappers around request-promise and accept standard request options.

    Available helpers include:

    • _get({uri, ...options})
    • _post({uri, form, ...options})
    • _put({uri, ...options})
    • _delete({uri, ...options})

    By default, request_handler.js configures these requests with the following options:

    • auth: Bearer token using the user's access token.
    • headers: Includes the user's user-agent.
    • baseUrl: Defaults to https://oauth.reddit.com.
    • qs: Includes {raw_json: 1} to prevent Reddit from escaping HTML characters.
    • timeout: Uses the user's specified timeout.
    // r is an instance of the snoowrap class
    
    // Send a GET request to a specific endpoint
    r._get({uri: 'r/snoowrap/about/moderators'})
    
    // Send a POST request with form data
    r._post({uri: 'api/remove', form: {id: 't3_2np694'}})
  10. Access Reddit Live Threads

    master

    Reddit live threads use websockets instead of a RESTful API. snoowrap handles this by representing the content stream as an EventEmitter. You can listen for the update event to receive real-time updates.

    r.getLivethread('whrdxo8dg9n0').stream.on('update', console.log);
  11. Run snoowrap development commands

    master

    Use the following npm scripts for development tasks:

    CommandDescription
    npm run lintRuns only the linter
    npm run test:browserRuns the unit tests in your browser
    npm run smoketestRuns two tests and stops (useful for verifying setup)
    npm run compileCompiles source code using Babel (useful for Node REPL)
    npm run build-docsBuilds the documentation into a doc/ folder
  12. Expand replies on VoteableContent

    master

    To retrieve replies for a piece of VoteableContent, use the expandReplies method. This returns the replies of type T (the generic type defined when the content was instantiated).

    Options:

    • limit: The number of replies to retrieve.
    • depth: The depth of the reply tree to expand.
    // Example usage (assuming 'post' is an instance of VoteableContent)
    const replies = await post.expandReplies({ limit: 10, depth: 2 });