npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

auto-snoo

v1.1.1

Published

A Generic, customisable reddit bot generator

Downloads

5

Readme

AutoSnoo

Build Status Coverage Status

Changelog
 - 1.1.0
   * Convert Module to typescript
   * Config change: Function Listener now requires a function named 'func' rather than 'function'

Pull Requests / Issues Welcome! Especially if you think there are any Listener types you think I should add!

AutoSnoo is a generic, customisable reddit bot generator, written in Node.

Before you begin

Reddit bots can be useful, but they can be very annoying if they are too active, or add little to a conversation. PLEASE read The bottiquete before deploying your bot, especially the sections about creating your own subreddit for testing.

Quick start

const AutoSnoo = require('auto-snoo');
const configObject = {}; // See below
const bot = AutoSnoo.create(configObject)
bot.listen();

Main functionality is created with AutoSnoo.create(), with a json object containing configuration.

The bot comes with a few built in predicates to avoid accidental spamming:

  • Only respond to comments posted after the bot has been started
  • Not respond to itself

It also provides a handful of utils for adding functionality to your customisations.

AutoSnoo.getParentComment(comment): Takes in a Snoowrap.Comment object and returns the parent of that comment. Useful when you require your bot to have knowledge not only of the comment it is replying to, but also the comment before that.

AutoSnoo.getCommentChain(comment): Returns an array of (simplified) Comment objects from the provided comment up to the thread root.
NOTE: using this option frequently will very quickly cause your bot to bump up against the ratelimits for the reddit API. Best option is to have any listener using this have more restrictive customPredicates or a long cooldown

Config Object

{
  snooWrapOpts: {
    clientId: '', // The clientId from reddit.com/prefs/apps
    clientSecret: '', // The clientSecret from reddit.com/prefs/apps
    username: '', // The username the bot will use
    password '', // Password for the above username 
    userAgent '', // (Optional) User-Agent the bot will use. See Reddit API rules for examples: https://github.com/reddit-archive/reddit/wiki/api#rules
  }
  subreddits: '' || [], // string or array of strings representing the subreddits the bot will listen on
  debug: false, // boolean indicating debug mode is on
  listeners: {
    // Object containing any number of listeners for your bot to pick up on
    // The keys will be used as a name in debug logging for indicating which listener triggered.
    example: {
      /* responseType for this listener
       *  random: selects a value at random from the provided corpus
       *  sequential: iterates through all values in the provided corpus in order
       *  function: generates the comment body using a provided function. See #functions
       */
      responseType: 'random' || 'sequential' || 'function',

      /* Array of values to use to generate responses. 
       * REQUIRED for 'random' and 'sequential', ignored for 'function'
       */
      corpus: [], 

      /* The function used to generate the response body. the function will be called with a snoowrap.Comment
       *  object representing the comment that matched the trigger phrase and all custom predicates. Useful
       *  if the bot should use part of the comment it is replying to in it's reponse.
       *  REQUIRED for 'function', ignored for 'random' and 'sequential'
       *  @returns {string} (or a serialisable variable) 
       */
      function: async (comment)=>{} 

      header: '' // String to open comments with. Remeber to add \n\n at the end if you want your generated content on a new line
      footer: '' // String to close comments with. Remeber to add \n\n at the start if you want your generated content seperate from the footer

      /* number of minutes to wait after a successful comment before commenting again.
       * This is configured per listener, so the bot may respond on different triggers within this time
       * HIGHLY RECCOMMENDED: Do not drop this value too low, as this will 
       *   likely annoy the mods of your subreddits and have your bot banned.
       * ALSO HIGHLY RECCOMENDED: Mention the rate limiting in your bot footer, this will reduce the amount
       *   of people attempting to repeatedly trigger the bot, causing spam
       * Defaults to 10 minutes
       */
      cooldown: 10 

      /* phrase or phrases bot is listening for. For array, any matches is considered a match.
       * Accepts a string, regex or array of either
       * HIGHLY RECCOMENDED: Make the value(s) relatively unique. using /u/{botName} is a good option, then
       *   the bot will only appear when summoned. Using something like 'the' is a great way to get your bot 
       *   banned very rapidly.
       */
      triggerPhrase: '' || [] || /^.$/ // 
      triggerCaseSensitive: false // should the capitalisation of the trigger word match? defaults to false

      /* Custom functions that allow for more nuanced tests for when the bot should reply
       * For example, you could have it never reply to a particular user, only between certain hours,
       * or only if the comment matches a particular regex.
       * The comment must pass ALL provided predicates in order to be responded to.
       */
      customPredicates:[
        async (comment)=>{ return true||false }
      ]
    }
  }
}

Customisation Functions

The customPredicates Array and the function listener type allow user-provided functions to enhance bot capability. The provided function MUST:

  • Expect the first and only argument to be a Snoowrap.Comment object
  • be an async function

The Snoowrap.Comment object

The comment object has many sub attributes, such as author, body, upvotes, controvertiality. See Snoowrap for more details.

The important point is that snoowrap was designed for chaining function calls, but that isn't really that helpful for a comment responder bot.

Best way to use the comment object is to use await whenever you access an attribute of the comment, to prevent gotchas such as:

const isUserMe = async (comment) => {
  if (comment.author.name === 'my_username'){
    // always false, as comment.author.name will be Promise<Pending> not yet a value
  }
}

const isUserMe = async (comment) => {
  if ((await comment.author.name) === 'my_username'){
    // should work as expected
  }
}

Example using customisation functions

const bot = AutoSnoo.create({
  snoowrapOpts: {
    clientId: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    username: process.env.REDDIT_USERNAME,
    password: process.env.REDDIT_PASSWORD,
  },
  subreddits: process.env.SUBREDDIT,
  debug: !!process.env.DEBUG,
  listeners: {
    dieRoll: {
      responseType: 'random',
      corpus: [
        '1',
        '2',
        '3',
        '4',
        '5',
        '6'
      ],
      header: '*clatter clatter*\n\n',
      footer: "\n\n^(I'm a simple bot for rolling d6s!)",
      triggerPhrase: '!!d6',
      customPredicates: [
        // increased specificity for what the comment must contain.
        async comment => (await comment.body).startsWith('.'), // not particularly useful, just example of syntax
        async comment => (await comment.body).endsWith('?'),
      ],
      cooldown: 15
    },
    echo: {
      // 
      triggerPhrase: `/u/${process.env.REDDIT_USERNAME}`,
      footer: '\n\nshut up',
      responseType: 'function',
      function: async comment => `> ${(await comment.body)}`,
      customPredicates: [
        // Only reply if the parent to the comment is not the bot
        async (comment) => {
          const commentParent = await AutoSnoo.getParentComment(comment); // use parentComment util
          // Could also call AutoSnoo.getParentComment(commentParent) if more context is needed,
          // or use AutoSnoo.getCommentChain(comment) if whole context is required
          // 
          return (await commentParent.author.name) !== process.env.REDDIT_USERNAME;
        },
      ],
      cooldown: 5 // 'echo' and 'dieRoll' will have independent timers
    },
  },
});

//Start the Bot
bot.listen();