Chazona's Blog

Web design, development and business.

Get Involved! Forming a Local Coding Group for Growth and Community

Group working on computers

When learning to code, you’re often your own worst enemy. Speedy learning slows to a crawl and you start to wonder if you were cut out to code at all or if you were really an imposter all along. One good way to avoid this self-defeating spiral is to surround yourself with fellow coders as often as you can manage.

Of course, there’s no limit to the support you can find online, but there may be times when you really need a person there with you sharing their own struggles, helping you on bug hunting excursions, or to contribute to a project with you. If you don’t happen to live right near an existing code group, however, don’t despair. We’re makers; you can make your own!

Now, I’m sure someone out there is probably saying, “Gee, Chazona, I’m sure forming a group is easy in a big city, but there’s no way I can find interested people in my [insert suburb or town here].” And unless that person is in extremely remote conditions, they’re probably selling their area short. My advice is not to assume lack of interest before you actually try it because you’ll probably surprise yourself.

So what I’m going to do is share with you how I went about forming a local group when I moved to a village outside of Richmond, VA, and some of the things I learned along the way. While everything may not apply to every situation or location around the world, it should give you an idea of where to start if you’re feeling a little lost.

A Little Inspiration

I’m not going to claim that I came up with the idea to create a local group all on my own. As a student of Free Code Camp, one thing that was consistently recommended throughout the program was to code socially: seek out local groups, pair program, contribute to group projects. Around the same time, my family was in the process of moving from our Richmond suburb to a slightly more distant village. The nearest local coding groups were more than 30 minutes away and met late in the evening, which for a full-time parent without childcare was just not accessible. What was I going to do?

Well, Free Code Camp also had a listing of existing Free Code Camp unofficial groups and recommended that those who didn’t have a group within 15 - 20 minutes of their home just create their own. The recommended medium was Facebook Groups, largely because it’s free to use and an incredible number of people are already using Facebook for other things. So I went on Facebook, followed the Free Code Camp guide and set one up in a matter of minutes.

Free Code Camp Local Group Screenshot

A Change of Tactics

Once the Facebook group was up, it stagnated a bit, growing by one or two people at a lethargic pace. As a natural introvert, I was not well-suited to heavily marketing my group, and the tools available through Facebook were not always helpful. While I had only expected a handful of people to eventually show interest in the group, I was starting to wonder if there was no interest in code where I lived.

But I wasn’t ready to give up yet. Instead, I took a look at Meetup, a web app I had considered when I’d first moved to the Richmond area but had never gotten around to joining an area group. It seemed simple enough to set a group up, so I filled out the information up to the last screen, expanding the group to be called Midlothian Code and include coders learning a wider variety of languages. I stopped short of purchasing the organizer subscription, and you’d do well to do the same if you utilize Meetup: within 24 hours of abandoning my cart, I got an email offering 50% off my first payment, which I used to pre-pay for six months for $30.

The thing Meetup did for my local group was to bring in interested people. Three days after I formed the group on Meetup, they announced the group to users who had already indicated interest in things like web development and programming, and Midlothian Code grew to almost 30 members overnight.

Suddenly I was in the position of needing to get meetings and events up and running, and of course, I had no idea what I was doing.

I started offering Coffee and Code meetings, since that was the common start for Free Code Camp-based groups. Ultimately, these ended up initially being half general meetings and half chit-chat on development topics. We wanted to start working on projects right away, and I wanted to be able to offer more interesting meeting content. I set up a set of chat rooms on Gitter and an organization account on GitHub to store code. Meanwhile, Meetup was proving not to be as useful for group communication as it was for advertising. I was not getting prompt notifications of messages and comments, the mailing list was outdated and a challenge to use properly, and it was difficult for members to know when information was posted to the message board. Change was needed again.

A Little More Conversation

As we found Meetup slowing down our group’s growth, I started looking into communication tools. We would ultimately need a website, but I didn’t seek to put one together in case our web developer members wanted to contribute to the original site. So while I registered a domain name for Midlothian Code on Google Domains, it was mainly so we could add Google’s G Suite tools to have a professional email and suite of office tools.

Instead of building out our website early on, we went with Mobilize.io, which allows for participating in discussion topics, RSVPing for events, and answering polls right from email. I could filter members by things like languages they were interested in and skills they currently had to ensure what we did better fit them.

Meanwhile, I utilized Tailor Brands to obtain an affordable logo for about $20 and some graphic design materials. Between my own code study, organizing the group, and taking care of my kids, I was having to get a little less DIY with my local group than I would have liked.

Midlothian Code Logo

I also applied for room reservation privileges at my local library, which made our venue situation more secure. Libraries are often a neglected resource, but you should definitely take a look at what your library network offers if you’re starting a local group as it provides vital free services you’re unlikely to get anywhere else.

In a lot of ways, switching to Mobilize was an improvement. We had enough members learning JavaScript to start having JavaScript specific meetups, and I could gather opinions from a set of our members fairly well.

And yet, now we found our membership split with some still exclusively on Facebook, most exclusively on Meetup, and a few using the Mobilize site. It was difficult to coordinate members, to ensure information was up to date on all channels, and even to have an idea of how many of our more than 70 members were actually active. On top of this, as Midlothian Code was expanding, it was going to rapidly break the limits of Mobilize’s free plan and get too expensive to sustain.

A Home for Midlothian Code

It was becoming increasingly clear that getting some kind of coherent, functional website up for the group was going to need to take precedence over the pride of hand coding the site as a group. We needed one centralized place where the most up-to-date information and copies of any shared resources could be found. As much as I had tried to avoid it up until this point, it was time to look at Wordpress.org.

I had already reserved a domain name for Midlothian Code, but Google Domains did not provide hosting, SSL certificates or any of the other services you might look for in a DNS provider. Instead, I sought out an inexpensive hosting provider to transfer the DNS service to. Choosing a web host is a big decision, but because our group is still a bit on the smaller side and not for profit, I sought out the least expensive provider that offered month-to-month payments. Do not go the route I did as I ended up using 1&1 Hosting, which is a terrible provider with basically no customer service. I actually set up hosting with a new company and migrated my site over in the length of time I spent on their customer service phone line waiting for a human being. I can comfortably recommend WP Engine for managed WordPress hosting and Digital Ocean for nearly everything else, otherwise, you’ll need to do some research. Make sure you can find quality documentation for your prospective host and more than one point of contact for service.

From there, I pieced together a site that would benefit Midlothian Code while we grow. I’m currently in the process of ensuring its features meet our needs, and once it is done, we will start scheduling and hosting coding workshops. I can’t wait to see how the group grows from here.

Midlothian Code Website Screenshot

Takeaways

If I had to do things all over again, I would have started with Meetup and creating a group website from the beginning, and I would give no hesitation to using self-hosted WordPress for my group’s site. I would take the time to find a better host, though.

The attention that Meetup gives groups is valuable, although I would limit our use of it to a length of time that brings in new members since it doesn’t provide a satisfactory return on investment after that.

Building a site, meanwhile, is possible almost for free while the group is still small (I’m only paying for domain, inexpensive hosting, and email). The benefit of having one primary place to direct members and have them congregate can’t be overstated. Once members can be organized, that’s when they can really get to work on coded-from-scratch projects that can be highlighted on our site.

Are you thinking about starting a local coding group where you live? Share your concerns in the comments!

How to Deploy Your Twitter Bot (or Other Worker App) to Heroku

Heroku screenshot

Ok, so we recently walked through getting started building a Node.js Twitter bot and then actually putting together functions to make it work. When we left off you had a really cool Twitter bot that acts automatically. Hopefully you’ve added some functions or features that really make it your own. Problem is, it’s not much fun to have a Twitter bot that only works when your terminal is open.

So now we’re going to take that awesome Twitter bot you made and deploy it to a cloud platform called Heroku. This will enable your bot to always be working in the background, and we’re going to do it for free.

At this point, you should have a Twitter bot that works when you run it locally. If you haven’t already, go ahead and commit and push your most recent changes to your repository.

$ git add .
$ git commit -m "some incredible commit message"
$ git push origin master

From here, we’re going to go ahead and create a new local branch, so just on your machine, and give it some <BRANCH-NAME>. Branching essentially lets us work with one copy of your code without disturbing or changing the main copy. Git allows us to create and switch to the branch with one command in the terminal:

$ git checkout -b <BRANCH-NAME>

Go ahead and open your .gitignore file and remove its reference to config.js. While you don’t want your authentication credentials being easily accessible on GitHub, you will need this file to deploy your app to Heroku.

Now, of course, if you don’t already have a Heroku account, you can set one up for free here.

Once you’re in and viewing your dashboard, create a new app, calling it whatever you like. Or leave it blank, and Heroku will come up with an interesting name for you.

Back to your terminal, you’ll need to download the Heroku Command Line Interface (CLI) if you haven’t already. Note: You will need Ruby installed, as Heroku was originally designed for Ruby apps. There are a number of ways to do this, depending on what version you would like.

Once it’s installed, you will have access to your Heroku apps right from your terminal. Log into your account by entering the following into your terminal and following the prompts:

$ heroku login

Now we’re going to connect your working directory with your Heroku app:

$ heroku git:remote -a <YOUR-APP-NAME>

And finally, we can deploy your application to Heroku. This should look very familiar to those who use Git and GitHub:

$ git add .
$ git commit -am "add project files"
$ git push heroku <BRANCH-NAME>:master

Hmm, something about that was different, though. Remember that we want our master branch on Heroku to include the config.js file, but we don’t want our master branch on GitHub to do so. So what we’re doing in that last command is to tell Heroku that our local branch ` is the master branch.

Now technically, your Twitter bot has been deployed to Heroku. You’ll notice, however, that if you look at your app dashboard online after a few minutes, it may say your app is sleeping. This is because Heroku assumes that if you build an app in Node.js, it will be a web app with a front-end for users to look at. It then uses that assumption to decide what kind of dynos (Linux containers) it thinks you need.

This can be solved a few ways:

Heroku Web Dyno Off, Worker Dyno On

Aside from adjusting it in the online GUI, one method is to create a Procfile (no extension, just that). Inside this file, we’ll give instructions to Heroku about what this app should be doing:

While I suggest including this file, when working with Heroku, it’s a good idea to know how to change what dynos are being used on your app, so that you can correct or scale them if needed. This can be done right from your terminal:

$ heroku ps:scale web=0 worker=1

After correcting the dynos or pushing changes, I usually restart the dyno I’m using. Heroku restarts your whole app after these changes, but sometimes working with the individual dyno keeps it from crashing on you:

$ heroku restart worker.1

And from there, your app should work just like it did locally. If you need, you can use terminal commands to check on your app, like this one to check the status of your dynos:

$ heroku ps

Or this one that shows your app console, any errors and shutdowns or restarts:

$ heroku logs

Go ahead and check out your awesome app!

What kind of bot or app did you deploy? Feel free to share in the comments!

How to Build a Twitter Bot in Node.js - Part 2

Mom Bot Twitter Profile

This is the second part of a two-part series on creating a Twitter bot. In the first part, we reviewed setting up Twitter credentials for the bot, ensured we have Node and NPM available, and began working with our directory structure and Twitter API module. In this second part, we’ll go over using the API module to tweet, respond to tweets, follow and unfollow, and learn how to make the bot your own. Let’s get started!

Tweeting

At this point, we are beginning to use the Twit module to create tweets. Reviewing the Twit documentation, we can see that the first example uses a post method to create a new status - this is how we post a tweet.

Let’s take a look at what this might look like as a reusable function within our code:

var Twit = require('twit'); // Include Twit package
var config = require('./config'); // Include API keys
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
function tweetIt(txt) { //Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('statuses/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}
tweetIt('Hello world!'); // Tweet "Hello world!"

This will allow us to pass as a parameter the content of your tweet to the function that will post the update. When the tweet attempt is made, we’re going to check for errors, and print them to the console if they exist. In the case of our function call, the parameter we pass is "Hello world!"

You can open your terminal and run the bot by typing nodejs bot.js (or node bot.js for non-Linux systems). If you do, your bot should tweet “Hello world!” Go ahead and check it out.

This is a fairly basic tweet, as it only provides whatever text we select ahead of time, but with this structure, you can create functions to make the value you pass into tweetIt() more dynamic.

In order to be able to have any kind of interactive Twitter bot, though, we need to use some event listeners and respond accordingly. Let’s check out our first one.

Responding to Tweets

The first event we’re going to listen for is the “tweet” event, which will trigger a function to handle any processing that will take place. Our code will look like this:

var Twit = require('twit'); // Include Twit package
var config = require('./config'); // Include API keys
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  console.log(eventMsg);
}

Now, if you go into your terminal and restart the bot (type .exit to get out of the existing server before your nodejs bot.js command) and then tweet at your bot, your terminal console will fill with so much information that it can be hard to decipher. It contains things like details of who sent the message, any @mentions or geolocation information, information used to display a profile and other things. This information is actually very useful in creating a bot that can respond to tweets, so let’s try to make it look more legible:

var Twit = require('twit'); // Include Twit package
var config = require('./config'); // Include API keys
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var fs = require('fs'); // Include the File System module
  var json = JSON.stringify(eventMsg, null, 4); // Prettify the eventMsg
  fs.writeFile('tweet.json', json); // Write the prettified eventMsg to a local file
}

Now if you save your file, run the bot again, and tweet at it, a file will be created called tweet.json. In it, you’ll find what looks like a standard JSON file, and it becomes easier to tell what each piece of data actually is.

We’re going to need the user.screen_name, text, and in_reply_to_screen_name properties in particular:

var twit = require('twit'); // Include Twit package
var config = require('config'); // Include authentication credentials
var bot_screen_name = YOUR-BOT-NAME; // Set your bot's Twitter handle
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var from = eventMsg.user.screen_name; // Who sent the tweet
  var text = eventMsg.text; // Message of the tweet
  var reply_to = eventMsg.in_reply_to_screen_name; // Who tweet was @reply to
  if (from !== bot_screen_name) { // If bot didn't send the tweet
    text = text.replace(/[^a-zA-Z\s]/gi, '').toLowerCase(); // Remove non-letter characters and transform to lowercase
    var tweet_array = text.split(' '); // Create an array of each word in the tweet
    /*
    What to do to each tweet
    */
    if (reply_to !== null && reply_to === bot_screen_name) { // If the tweet was @reply to bot
      /*
      What to do to each @reply
      */
    }
  }
}

Let’s take a look at this for a moment. First is the addition of a new variable bot_screen_name. We’re going to include your bot’s handle in a few conditionals, so we may as well put it in a variable. In the body of the tweetEvent() function, we’re going to set who sent the tweet, what it actually said, and if the tweet was an @reply, who it was in response to. Then we’re going to check that the bot didn’t send the tweet. This seems silly, but bear in mind that your bot’s own tweets, follows and other events are a part of its stream. Inside that condition, we’re also going to check to see if the tweet is an @reply, and if so if it is directed at your bot. With these conditions we can create logic to handle any tweets by those your bot follows as well as handling @replies.

Before I go into an example of how I handled mine, we need to add the tweeting function we used before. Remember that it looked a bit like this:

function tweetIt(txt) { // Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('status/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}

Now to put it together, this is a simplified version of how I did my Mom Bot’s tweet logic:

var twit = require('twit'); // Include Twit package
var config = require('config'); // Include authentication credentials
var bot_screen_name = YOUR-BOT-NAME; // Set your bot's Twitter handle
var bad_words_list = require('badwords/array'); // Include Bad Words package
var unfollow_words_list = [
  'go away',
  /*
  ...
  */
  'leave me alone'
]
for (var k = 0; k < bad_words_list.length; k++ {
  bad_words_list[k] = bad_words_list[k].toLowerCase();
}
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var from = eventMsg.user.screen_name; // Who sent the tweet
  var text = eventMsg.text; // Message of the tweet
  var reply_to = eventMsg.in_reply_to_screen_name; // Who tweet was @reply to
  if (from !== bot_screen_name) { // If bot didn't send the tweet
    text = text.replace(/[^a-zA-Z\s]/gi, '').toLowerCase(); // Remove non-letter characters and transform to lowercase
    var tweet_array = text.split(' '); // Create an array of each word in the tweet
    for (var i = 0; i < tweet_array.length; i++) { // For each word in the tweet
      if (bad_words_list.indexOf(tweet_array[i]) != -1) { // If the word is in the bad words list
        tweetIt('@' + from + ' You tweeted a bad word! Mom Bot\'s not mad, she\'s just disappointed...'); // Bot tweets disappointment, example
      }
    }
    if (reply_to !== null && reply_to === bot_screen_name) { // If the tweet was @reply to bot
      for (var j = 0; j < unfollow_words_list.length; j++) { // For each word in the tweet
        if (text.indexOf(unfollow_words_list[j]) != -1) { // If an unfollow expression is in the tweet
          tweetIt('@' + from + ' Ok, I will leave you alone.'); // Tweet an unfollow response, example
        }
      }
    }
  }
}
function tweetIt(txt) { // Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('status/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}

Because of Mom Bot’s “personality” she will scan all tweets issued by followers for “bad words” and issue a warning to the user if they occur. For this, I used the Bad Words module to generate an array of words to check for.

Mom Bot tweet response to bad words

In addition, she also scans @replies sent to her to see if they have any phrases set as unfollow-triggers. If she receives one, she will send a response that she will unfollow the user.

Mom Bot Unfollow Tweet

Of course, in my actual bot’s code, I have a function randomizing the responses Mom Bot gives so that she’s not constantly repeating herself. If you would like an example of the full code, you can always check out the source here.

Note, she’s not actually unfollowing them here. To do that, we’re going to need to learn just a bit more.

Follow and Unfollow on Command

Now if we check the Twit documentation again, it has a couple of examples of using post methods to create and destroy friendships. Those are the methods we need, as those will enable the bot to follow and unfollow users.

In our case, we’ll go ahead and follow each follower who follows the bot. It will look a bit like this:

var twit = require('twit'); // Include Twit package
var config = require('config'); // Include authentication credentials
var bot_screen_name = YOUR-BOT-NAME; // Set your bot's Twitter handle
var bad_words_list = require('badwords/array'); // Include Bad Words package
var unfollow_words_list = [
  'go away',
  /*
  ...
  */
  'leave me alone'
]
for (var k = 0; k < bad_words_list.length; k++ {
  bad_words_list[k] = bad_words_list[k].toLowerCase();
}
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
stream.on('follow', followed); // Anytime a user follows bot, trigger followed
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var from = eventMsg.user.screen_name; // Who sent the tweet
  var text = eventMsg.text; // Message of the tweet
  var reply_to = eventMsg.in_reply_to_screen_name; // Who tweet was @reply to
  if (from !== bot_screen_name) { // If bot didn't send the tweet
    text = text.replace(/[^a-zA-Z\s]/gi, '').toLowerCase(); // Remove non-letter characters and transform to lowercase
    var tweet_array = text.split(' '); // Create an array of each word in the tweet
    for (var i = 0; i < tweet_array.length; i++) { // For each word in the tweet
      if (bad_words_list.indexOf(tweet_array[i]) != -1) { // If the word is in the bad words list
        tweetIt('@' + from + ' You tweeted a bad word! Mom Bot\'s not mad, she\'s just disappointed...'); // Bot tweets disappointment, example
      }
    }
    if (reply_to !== null && reply_to === bot_screen_name) { // If the tweet was @reply to bot
      for (var j = 0; j < unfollow_words_list.length; j++) { // For each word in the tweet
        if (text.indexOf(unfollow_words_list[j]) != -1) { // If an unfollow expression is in the tweet
          tweetIt('@' + from + ' Ok, I will leave you alone.'); // Tweet an unfollow response, example
        }
      }
    }
  }
}
function followed(eventMsg) { // Function to run on follow event
  var follower_screen_name = eventMsg.source.screen_name; // Follower's screen name
  if (follower_screen_name !== bot_screen_name) { // If follower is not bot
    tweetIt('Thank you for following me!');
    T.post('friendships/create', { screen_name: follower_screen_name }, function(err, data, response) { // Follow the user back
      if (err) { // If error results
        console.log(err); // Print error to the console
      }
    });
  }
}
function tweetIt(txt) { // Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('status/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}

So here we’ve added an event listener to listen for follow events, which will trigger our followed() function. From there we check that the user doing the follow wasn’t the bot, similar to how we checked tweets. If the event passes our check, the bot will follow the user and print an error if one is thrown.

Mom Bot Follow Tweet

Let’s use what we’ve learned to actually unfollow the users we tweeted that we would earlier:

var twit = require('twit'); // Include Twit package
var config = require('config'); // Include authentication credentials
var bot_screen_name = YOUR-BOT-NAME; // Set your bot's Twitter handle
var bad_words_list = require('badwords/array'); // Include Bad Words package
var unfollow_words_list = [
  'go away',
  /*
  ...
  */
  'leave me alone'
]
for (var k = 0; k < bad_words_list.length; k++ {
  bad_words_list[k] = bad_words_list[k].toLowerCase();
}
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, trigger tweetEvent
stream.on('follow', followed); // Anytime a user follows bot, trigger followed
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var from = eventMsg.user.screen_name; // Who sent the tweet
  var text = eventMsg.text; // Message of the tweet
  var reply_to = eventMsg.in_reply_to_screen_name; // Who tweet was @reply to
  if (from !== bot_screen_name) { // If bot didn't send the tweet
    text = text.replace(/[^a-zA-Z\s]/gi, '').toLowerCase(); // Remove non-letter characters and transform to lowercase
    var tweet_array = text.split(' '); // Create an array of each word in the tweet
    for (var i = 0; i < tweet_array.length; i++) { // For each word in the tweet
      if (bad_words_list.indexOf(tweet_array[i]) != -1) { // If the word is in the bad words list
        tweetIt('@' + from + ' You tweeted a bad word! Mom Bot\'s not mad, she\'s just disappointed...'); // Bot tweets disappointment, example
      }
    }
    if (reply_to !== null && reply_to === bot_screen_name) { // If the tweet was @reply to bot
      for (var j = 0; j < unfollow_words_list.length; j++) { // For each word in the tweet
        if (text.indexOf(unfollow_words_list[j]) != -1) { // If an unfollow expression is in the tweet
          tweetIt('@' + from + ' Ok, I will leave you alone.'); // Tweet an unfollow response, example
          T.post('friendships/destroy', { screen_name: from }, function(err, data, response) { // Unfollow the user
            if (err) { // If error results
              console.log(err); // Print error to the console
            }
          });
        }
      }
    }
  }
}
function followed(eventMsg) { // Function to run on follow event
  var follower_screen_name = eventMsg.source.screen_name; // Follower's screen name
  if (follower_screen_name !== bot_screen_name) { // If follower is not bot
    tweetIt('Thank you for following me!');
    T.post('friendships/create', { screen_name: follower_screen_name }, function(err, data, response) { // Follow the user back
      if (err) { // If error results
        console.log(err); // Print error to the console
      }
    });
  }
}
function tweetIt(txt) { // Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('status/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}

Now at least the bot is being honest!

Make it Your Own

Now the bot can follow and unfollow independently, it can respond to tweets, and you can create its own tweets fairly easily. But it’s not terribly creative at this point since it basically follows what my bot does, and even then, it doesn’t do quite as much.

The full example of my bot is below, and as you can see, it includes additional responses and randomizes them. It will respond to @replies with certain “sad” or “proud” words. It also includes some instances where it will notify me if the bot isn’t able to do what’s expected.

console.log('The bot is starting...');
var Twit = require('twit'); // Include Twit Package
var config = require('./config'); // Include authentication credentials
var bot_name = 'Mom Bot';
var bot_screen_name = 'the_mother_bot';
var bot_owner_name = 'otherconsolelog';
var disappointed_bot_list = [ // What Bot will say when user tweets a bad word
  bot_name + "'s not mad; she's just disappointed. 😞 Would you like to delete that tweet?",
  "You're an adult, but do you really want to keep that tweet? 🤔 You can delete it if you don't want a future boss to see.",
  "Do you kiss your Mother Bot with that mouth? 😠 You can always delete that tweet if you think it was foolish.",
  "Sweetie, your tweet had a bad word! 😲 Are you sure that's what you want people to see? You can delete it if not."
];
var thank_you_list = [ // What Bot will say when user follows Bot
  "Thank you for keeping in touch with " + bot_name + ", Sweetie. 😘",
  "Don't tell your brother, but of course you're my favorite, Sweetie. 😍"
];
var unfollow_bot_list = [ // What Bot will say when Bot unfollows user
  "Ok Sweetie, " + bot_name + " will give you space. Follow me again if you want to talk. 😥",
  bot_name + " always loves you, but I\'ll leave you alone. Follow me again if you want to talk. 😥",
  "Ok Sweetie, " + bot_name + " will miss you, but I'm glad you're having a good time. 😥"
];
var feel_better_bot_list = [ // What Bot will say when user tweets about sadness
  "I'm happy simply because you exist. Just thought you should know that. 😘",
  "You are the light of my world and the first thing I think about every day. 😘",
  "Always remember that you are needed and there is work to be done. 😉",
  "Keep in mind that this, too, will pass. In the meantime, I am here for you in whatever way you need. 🤗"
];
var proud_bot_list = [ // What Bot will say when user tweets about something great/to be proud of
  "I'm so proud of you. And even if you weren't so fantastic, I'd still be proud. 😆",
  "I believe in you, Sweetie. 😘",
  "You are one of the best gifts I've ever gotten. I am so proud and humbled. 😊",
  "I feel so proud when I'm with you. 😊",
  "You have some real gifts! 😆",
  "It is so cool to watch you grow up. 😆",
  "You make me so happy just by being you. 😊",
  "I love you so much!😘",
  "You were born to do this! 😆"
];
var unfollow_words_list = [ // Words user can include to request Bot to unfollow the user
  "all your fault",
  "dont care",
  "dont have to",
  "dont need you",
  "go away",
  "hate you",
  "leave me alone",
  "not my mom",
  "not my real mom",
  "run away"
];
var sad_words_list = [ // Words user can include to request cheering up
  "blue",
  "blah",
  "crestfallen",
  "dejected",
  "depressed",
  "desolate",
  "despair",
  "despairing",
  "disconsolate",
  "dismal",
  "doleful",
  "down",
  "forlorn",
  "gloomy",
  "glum",
  "heartbroken",
  "inconsolable",
  "lonely",
  "melancholy",
  "miserable",
  "mournful",
  "sad",
  "sorrow",
  "sorrowful",
  "unhappy",
  "woebegone",
  "wretched"
];
var proud_words_list = [ // Words user can include to express happiness/pride
  "accomplished",
  "accomplishment",
  "amazing",
  "awesome",
  "cheering",
  "content",
  "delighted",
  "glad",
  "glorious",
  "good",
  "grand",
  "gratified",
  "gratifying",
  "happy",
  "heartwarming",
  "inspiring",
  "joyful",
  "magnificent",
  "memorable",
  "notable",
  "overjoyed",
  "pleased",
  "pleasing",
  "proud",
  "resplendent",
  "satisfied",
  "satisfying",
  "splendid",
  "succeeded",
  "success",
  "thrilled"
];
var bad_words_list = require('badwords/array'); // Include Bad Words package
for (k = 0; k < bad_words_list.length; k++) {
  bad_words_list[k] = bad_words_list[k].toLowerCase(); // Transform Bad Words list to all lowercase
}
var T = new Twit(config);
var stream = T.stream('user'); // Setting up a user stream
stream.on('tweet', tweetEvent); // Anytime a tweet enters the stream, run tweetEvent
stream.on('follow', followed); // Anytime a user follows Bot, run followed
console.log('Entering the stream.');
function tweetEvent(eventMsg) { // Function to run on each tweet in the stream
  var from = eventMsg.user.screen_name; // Who sent the tweet
  var text = eventMsg.text; // Message of the tweet
  var reply_to = eventMsg.in_reply_to_screen_name; // Who tweet was @reply to
  if (from !== bot_screen_name) { // If Bot didn't send the tweet
    console.log('Bot received a tweet.');
    text = text.replace(/[^a-zA-Z\s]/gi, "").toLowerCase(); // Remove non-letter characters and transform to lowercase
    var tweet_array = text.split(' '); // Create an array of each word in the tweet
    for (var i = 0; i < tweet_array.length; i++) { // For each word in the tweet
      if (bad_words_list.indexOf(tweet_array[i]) != -1) { // If the word is included in bad words list
        var disappointed_text = randomSaying(disappointed_bot_list);
        console.log('That tweet had a bad word!');
        tweetIt('@' + from + ' ' + disappointed_text); // Bot tweets her disappointment
      }
    }
    if (reply_to !== null && reply_to === bot_screen_name) { // If the tweet was @reply to Bot
      for (var j = 0; j < unfollow_words_list.length; j++) { // For each word in the unfollow list
        if (text.indexOf(unfollow_words_list[j]) != -1) { // If an unfollow word is in the tweet
          var unfollow_text = randomSaying(unfollow_bot_list);
          console.log('Someone wanted to unfollow.');
          tweetIt('@' + from + ' ' + unfollow_text); // Tweet an unfollow response
          T.post('friendships/destroy', { screen_name: from }, function(err, data, response) { // Unfollow the user
            if (err) { // If error results
              console.log(err); // Print error to the console
              tweetIt('@' + from + ' Something\'s wrong with ' + bot_name + '\'s computer. Ask @' + bot_owner_name + ' to help me unfollow you, please.'); // Tweet a request for user to contact Bot Owner
            }
          });
        }
      }
      for (var l = 0; l < tweet_array.length; l++) { // For each word in the tweet
        if ('stop'.indexOf(tweet_array[l]) != -1) { // If 'stop' is in the tweet
          console.log('Someone\'s having a problem.');
          tweetIt('@' + from + ' ' + bot_name + ' seems to be upsetting you. Please ask @' + bot_owner_name + ' for help.'); // Tweet a request for user to contact Bot Owner
        } else if (sad_words_list.indexOf(tweet_array[l]) != -1) { // If a sad word is in the tweet
          var feel_better_text = randomSaying(feel_better_bot_list);
          console.log('Someone needs cheering up.');
          tweetIt('@' + from + ' ' + feel_better_text); // Tweet to cheer the user up
        } else if (proud_words_list.indexOf(tweet_array[l]) != -1) { // If a proud word is in the tweet
          var proud_text = randomSaying(proud_bot_list);
          console.log('Someone did something awesome.');
          tweetIt('@' + from + ' ' + proud_text); // Tweet to be proud of the user
        }
      }
    }
  }
}
function followed(eventMsg) { // Function to run on follow event
  console.log('Someone followed the bot.');
  var name = eventMsg.source.name; // Who followed
  var follower_screen_name = eventMsg.source.screen_name; // Follower's screen name
  if (follower_screen_name !== bot_screen_name) { // If follower is not Bot
    var thank_you = randomSaying(thank_you_list)
    tweetIt('@' + follower_screen_name + ' ' + thank_you); // Tweet a thank you expression
    T.post('friendships/create', { screen_name: follower_screen_name }, function(err, data, response) { // Follow the user back
      if (err) { // If error results
        console.log(err); // Print error to the console
      }
    });
  }
}
function tweetIt(txt) { // Function to send tweets
  var tweet = { // Message of tweet to send out
    status: txt
  }
  T.post('statuses/update', tweet, tweeted); // Send the tweet, then run tweeted function
  function tweeted(err, data, response) { // Function to run after sending a tweet.
    if (err) { // If error results
      console.log(err); // Print error to the console
    }
  }
}
function randomSaying(sayingList) { // Function to randomize the expression to use
  var saying_number = Math.floor(Math.random()*sayingList.length); // Give a random number within number of available responses
  var saying = sayingList[saying_number]; // Grab expression matching that number
  return saying; // Return the expression
}

Mom Bot Cheer Up Tweet

Mom Bot Proud Tweet

Go ahead and take some time to think about what kinds of things you would like your bot to do and how you might implement that logic.

Enjoy learning things visually? Daniel Shiffman, creator of Coding Rainbow has a fantastic YouTube video series you should check out that helped me a great deal with Twitter bots. If you feel extra awesome, you can help support his work on Patreon or buy one of his books, The Nature of Code: Simulating Natural Systems with Processing or Learning Processing, Second Edition: A Beginner’s Guide to Programming Images, Animation, and Interaction.

Let me know what kind of bot you’ve made, share it so I can follow it, or ask questions in the comments!

How to Build a Twitter Bot in Node.js - Part 1

Mom Bot Twitter Profile

When just starting out with Node.js, piecing together a front- and back-end and successfully deploying the app can be a bit intimidating. A great way to get your feet wet working with Node.js and deploying to Heroku without having a front-end to deal with is by making a Twitter bot. Not only are they incredibly fun, but because they’re only logic, they make a great stepping stone to bigger projects.

In this two part series, I’ll walk you through creating an interactive Twitter bot step by step, with examples from my own bot. My example bot, called Mom Bot, follows and unfollows users independently and scans tweets and @replies so it can respond accordingly. The source code and documentation for my bot can be viewed in its entirety here.

For the first part, we’ll review setting up Twitter credentials for the bot, ensure we have Node and NPM available, and begin working with our directory structure and Twitter API module. In the second part, we’ll go over using the API module to tweet, respond to tweets, follow and unfollow, and learn how to make the bot your own.

Getting Set Up on Twitter

First things first: please don’t use your primary Twitter account for this. There are exceptions, say if you are making a bot to handle follows or @replies for you, but for the most part, you don’t want to risk your main account being flagged as spam.

Once you’ve created an account for your bot, head over to the Twitter Developers page. This is the place for documentation on making all kinds of Twitter apps. In the navigation bar, navigate to “My Apps.”

Twitter Developers Navigation and Header

Log in as your bot if you aren’t already, and click to “Create an Application.” You can name your application anything you like, but try to keep your name and description relevant to your bot. Your website can be anything; my suggestion is your repository URL for the bot on GitHub. Read and accept the Twitter Developer Agreement and click to “Create your Twitter Application.”

Create an Application Form

From there, you’ll see several tabs of information and settings. The only one that matters to us for this bot is the “Keys and Access Tokens” tab.

Keys and Access Tokens Tab

Go ahead and generate your Access Token and Access Token Secret, and then leave this window open; we’ll need the four codes on this page in just a few minutes.

Node.js and NPM

Node JS Logo

Linux

For those of you running a Linux box, the great news is that your distro may come with Node.js and the Node Package Manager (NPM) already installed. A good way to check if you already have it is to open your terminal. Go ahead and enter:

$ nodejs

If it comes preinstalled, your terminal should become a console prompt, shown by the > symbol. You can work with JavaScript in this console just like you would in your browser’s console. Go ahead and check it out! When you’re ready to exit the console, simply type .exit or hit the Ctrl and C buttons twice.

You could also just as easily check for a man page about nodejs:

$ man nodejs

Similarly, to check if NPM is installed, you can check for its man page:

$ man npm

If you already have both of these tools installed, go ahead to the next section. Let the others catch up.

Mac

Mac users, while you have some extra installations ahead of you, they’re not all that difficult,

First, make sure you have XCode installed. This is a free download from the Apple App Store, and it will allow you to compile software for your Mac.

The next Mac-specific install will be Homebrew, which is another package manager. To install it, go to your terminal, type the following, and follow the prompts:

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once you have Homebrew, installing Node.js and the NPM are fairly simple. In your terminal, simply type the following:

$ brew install node

Homebrew should have installed both Node.js and NPM. To verify this, enter two commands just checking the version of each piece of software:

$ node -v
$ npm -v

If you received version numbers, congrats! Move ahead to the next section and let those running Windows get to here.

Windows

Windows users, we’re actually only going to use the shell or command line to test whether your installation was successful. Go ahead to the Node.js web site, download and run their Windows installer (either 32-bit or 64-bit ).

You should be finished at this point. Go ahead and check for version numbers by entering the following in your terminal:

$ node -v
$ npm -v

If you got version numbers, you’re ready to move on.

Starting the Actual Project

Now we can actually start building the app. As with almost any Node.js project, we want to start by creating our package.json file. NPM actually makes that easy for us. Go ahead and enter the following into your terminal, and follow the prompts to create this file.

$ npm init

This file now holds some key information about our app, and it will eventually include any dependencies as well.

The next file we’ll create is our bot.js file. This will take the place of the server.js file in most Node.js projects and will be the source of all the logic for our bot.

Before we start writing the logic, though, we’re going to need to include a library of functions for interacting with tweets and streams. In this case, we’re going to use Twit, an API module that is useful for bot-making. To install it in your project, go back to your terminal and enter:

$ npm install twit --save

That --save term will include the module in the dependencies of your package.json file.

NPM Twit Module

Of course, in order for your bot to do anything with a Twitter account, it needs to authenticate. You’ll need to create a config.js file to hold your API keys because you don’t want them to be in your main logic file. Remember the Twitter screen with the four codes? Enter them in the config.js file like this:

module.exports = {
  consumer_key: '...',
  consumer_secret: '...',
  access_token: '...',
  access_token_secret: '...'
}

Now, let’s start building the bot. In your bot.js file, first you need to actually include the Twit module you just installed and the config.js file you just created:

var Twit = require('twit'); // Include Twit package
var config = require('./config'); // Include API keys
var T = new Twit(config);
var stream = T.stream('user'); // Set up user stream

The last two lines get you set up to start listening to your account’s Twitter stream for events like tweets and follows.

When you’re ready to start piecing this bot together, go ahead to Part 2. In the meantime, feel free to get familiar with the Twitter API module we’re using, Twit.

Questions? Ask them in the comments!

When You Hit That Wall

Free Code Camp Activity Cart: 302 challenges completed

I keep having to remind myself that I’ve only actually been studying on Free Code Camp for ten days, in between taking care of kids and other daily necessities.

Sometimes, it’s to ensure I don’t get lured into a sense of hubris. I’ll be flying through tutorials, devouring them as though they were wrapped in bacon.

Free Code Camp Activity Cart: 302 challenges completed

Riding That High

With HTML, CSS, Bootstrap — even some JavaScript — all coming together and flowing in a way that makes sense, it’s easy to keep chasing the high of another finished project instead of diversifying study materials to ensure an understanding of the fundamentals. By the time I got through a challenge to build a random quote generator, I was so ready to whip some code into submission. I’d just come off a set of fascinating algorithms and had now completed my first dynamic web project of any kind.

https://codepen.io/chznbaum/pen/EyLoOv/

Crash And Burn

And I had been racing through tasks and challenges as quickly as I could access them. As I started getting into some of the more intermediate challenges, I started to encounter more and more JavaScript concepts that I had never heard of. I managed well with skillful Google queries and an eye for scanning documentation. But when I reached the first challenge where I needed to use an outside API to create a weather app, everything locked up.

As I poured through documentation about the recommended API, I could not make sense of how I was supposed to actually use it. And it hit me that I didn’t understand how to implement the API because I didn’t understand enough of the underlying JavaScript. By focusing only on the tutorials and challenges, I was learning at an incredible pace, but I was lacking in areas that would be critical in completing more advanced challenges and algorithms.

Useless! Empty! Brain!

Regroup and Diversify

I’m still fully committed to the path Free Code Camp lays out to full-stack proficiency, and I am dedicated to completing it within one year, despite the challenges of finding time with two small children running around. As someone who has been interested in learning code for quite some time but — seeing the explosion of languages being sought — had no idea where to start, I this is the single most useful individual tool I’ve come across.

But I’m realizing that while learning by doing is critical to retaining and being able to use the knowledge, it won’t all come from one place. I went through and started evaluating what additional resources would be helpful for my learning path:

  • The book JavaScript & JQuery: Interactive Front-End Web Development by Jon Duckett. Because it is designed to reach those with and without computer science backgrounds, it’s a great read with a lot of visuals.
  • JavaScript tutorials from CodeAcademy. These are a bit more in-depth than the Free Code Camp JavaScript tutorials (though FCC wins hands-down on HTML through Bootcamp, and their practical exercises are superior).
  • Flowcharts and maps. I’m a visual person in some respects, and especially when it comes to problem-solving, if I can separate myself from the problem and break it up into its parts, I’m golden.
  • Questions. I set up an account on Stack Overflow so as I get flummoxed, I can share my code and ask for techniques or flaws in my logic. It’s not easy to ask for help, but everyone has been a beginner at some point.
  • Journaling my progress. As I encounter problems or intriguing solutions, this will be a way to keep things fresh in my mind, especially since I am working with such a concentrated approach.

In addition to more specific resources, I am also taking advantage of practice that may come from day to day activities, like my blogging. Today, I needed to reconfigure my blog’s Twitter cards to enable photos and get the site whitelisted. It was frustrating, but I learned a great deal about Twitter’s integration, and I managed to create cards that should help drive engagement and conversation.

Of course, different forms of challenge and competition can keep things interesting, so finally I set up an account on HackerRank so I can progress through additional algorithms and participate in contests to show how my skills grow.

HackerRank 30 Days of Code Challenges, Day 0 Completed

Needless to say, I’m not nearly ready for even the “Newbie” contests — if you want to feel better about your own progress, I’ll be happy to tell you how I scored. Eventually, I’ll get there, and in the meantime, the challenges and algorithms are helpful in gaining some of the academic knowledge I’m missing out on by not having a computer science degree.

And of course, lastly, I’m going to need to get more involved with other coders: share, comment, and interact as part of a community. Conveniently, Free Code Camp recommends local meetup groups for Coffee and Code and other types of events. The nearest one to me is in a neighboring city 30 minutes away, which is a barrier to attending; FCC recommends having the groups as local as possible, so setting one up for my town will probably be on the docket for tomorrow or the next day. And that’ll provide a great opportunity for some leadership experience.

Have you hit a wall (or many) yet in your coding adventures? How are you adapting to move forward? Feel free to share in the comments!