Create a reddit bot

How to Write a Reddit Bot in C# using RedditSharp

Reddit bots are small programs that automatically respond to comments in any given Reddit sub. If you’re a frequent Redditor, you’ve probably seen a bot. They can be annoying at times, but they can also be fun. If you own a sub or want to create a bot, you can have some fun with other Redditors.

Reddit has an API that you use to interact with their software. You can use any language you prefer, but I’m mainly a C# developer, so I wrote a bot that compiles to an EXE and I run it at as a service on my desktop. This isn’t the best way to schedule execution, but my bot is simply a hobby bot and nothing serious.

I’m going to show you how to create a Reddit bot from start to finish. The entire code for my bot is at the end of this post if you just want to skip to the full code.

Preliminary Steps

Reddit requires you to register for its API. Click the register link and fill out the form. They only ask you a few simple questions and you’re all set. You receive confirmation in email quickly, so there is no wait time.

After you sign up, click the “preferences” link on your main account page and you see an app tab at the top of the preferences page (see image). This takes you to a page where you can create and register your bot with the Reddit system. Click “Create App” and then fill out the information.

 

 

Take note of the “secret” value that Reddit gives to you. If you lose it, you can always go back and get this secret token from your apps page. Click “Edit” under your app name and the “secret” token is there. Keep this safe, and don’t publish it anywhere. You need this value when you code your bot.

Finally, you need a username for your bot. You don’t want to use your regular account, but you can register your bot account with the same email you used to register your personal accounts. Having multiple accounts on Reddit is allowed, so don’t worry about having a personal account with a bot account registered to the same person.

Note: Reddit limits the frequency at which new users can post. If you have your own sub, create a new account and add it as an approved submitter in the moderator tools.  Create a thread in your sub and use it for testing. Adding the account as an approved submitter will allow you to test without the post limitations. If you use a new account and try to test your bot, Reddit will give you an error that you’re posting too quickly.

The final preliminary step is importing RedditSharp into your C# solution. Go to Tools > Nuget Package Manager > Manage Nuget Packages for Solution. From here, search for RedditSharp and add it to your solution.

Don’t forget to reference it in your using statements. Add the following code to the top of your solution page.

 

using RedditSharp;
using RedditSharp.Things;

 

Coding Your Reddit Bot

This bot was set up as an executable to run every 3 minutes, so I will show you just the bot code. You can get the full code at the end of the article. The entire bot runs in a method called RunRedditComments, but you can make a method with any name that you choose. I will show you just the code within my method.

First, we need to set up the Reddit connection and authenticate our bot.

 

bool authenticated = false;
Reddit reddit = null;
var token = "<your token from registering your app>";
reddit = new Reddit("<your bot account name>", "<password>");
reddit.InitOrUpdateUser();
authenticated = reddit.User != null;
Subreddit sub = null;
if (!authenticated)
{
sub = reddit.GetSubreddit("<your sub name>");
}

 

Code Explanation

Most of this code is specific to RedditSharp and the way Reddit requires you to log in to the API. Replace the secret token with your own that you received in the preliminary steps.

The RedditSharp constructor asks for a username and password. This is the bot account you created earlier. The next three lines identify if the user is authenticated, and if the account is successfully logged in, the last 1000 comments are retrieved from the sub name in the GetSubreddit() method.

Note: You need to set up your try-catch statements around the authentication process. I found that Reddit sometimes fails and returns a 502 error.

Next, we want to go through each comment and store it in a model. I created a model that stores several of the properties returned by the Reddit API. You can either step through the code and view the properties or get a list of them here. I’ve found that the API documentation does not list everything returned, so it’s best to step through and take a snapshot of the properties returned in Visual Studio.

In my bot, I want to respond to a comment that has a specific word in it. Instead of working directly with all the data retrieved from the sub, I store only comments that have the word “<my value>” into a ViewModel. This makes it a bit more efficient instead of storing all 1000 comments.

 

var comments = sub.Comments.Take(50);
List&amp;lt;RedditCommentModel&amp;gt; model = new List&amp;lt;RedditCommentModel&amp;gt;();
foreach (var comment in comments)
{
if (comment.Body.ToLower().Contains(&quot;&amp;lt;my value&amp;gt;&quot;))
{
RedditCommentModel newModel = new RedditCommentModel(comment);
model.Add(newModel);
}
}

 

Code Explanation

My sub is slow, so I only check the first 50 comments. Reddit returns the most recent 1000 comments though. For each comment, I look for a specific value. If the value is found, then I store it in my ViewModel. My model has a constructor method that takes the values I want from the Reddit comment and loads it into the model’s properties.

Next, I set up my bot’s response string. I keep track of the number of posts the bot has made, so I need to set up a template that uses the letter “X” to replace with the current number. I’ll get into how to keep track later. The string template is named truck due to the nature of the message, but your variable can be named anything. Remember that this template can be any string you want with any formatting allowed on Reddit. The hash symbol formats the message as bold. Just for my example, I’m creating a string that shows the number of responses made by the bot.

 

 StringBuilder truck = new StringBuilder(); truck.Append(&quot;#X POSTS&quot;); 

 

Next, I create a text file that keeps track of the number of posts by storing the last iteration value in it. It’s a simple text file that will be saved in the same directory that your executable runs.

 

FileStream fs3 = new FileStream(&quot;bot-truckcount.txt&quot;, FileMode.OpenOrCreate);
fs3.Close();
string postCount = File.ReadAllText(&quot;bot-truckcount.txt&quot;);
int newPostCount = 0;
if (string.IsNullOrEmpty(postCount))
{
newPostCount = 1;
}
else
{
newPostCount = Convert.ToInt32(postCount);
}
truck.Replace(&quot;X&quot;, newPostCount.ToString());

 

Code Explanation

The code above creates an integer to keep track of the number of posts, but if this is the first time the code is run, it defaults to 1. If the bot file isn’t present, we create a new one and default the post value to 1. We also load the first value into the bot’s string template.

Now, we need to know what comments we’ve already replied to. Again, we keep a text file with all of the comment IDs that we’ve stored in the ViewModel. This stops you from continually spamming the same comment with a reply. My sub is small, so I automatically delete any stored comments after the text file reaches 500 IDs. This is so that my file doesn’t continue to grow out of control with millions of comments. Since we only grab the first 50, we don’t need to store all comments replied to indefinitely.

 

FileStream fs = new FileStream(&quot;bot-alreadycommented.txt&quot;, FileMode.OpenOrCreate);
fs.Close();
string alreadyCommented = File.ReadAllText(&quot;bot-alreadycommented.txt&quot;);
alreadyCommented.Replace(&quot;\r\n&quot;, &quot;&quot;);
string[] commentIds = null;
if (!string.IsNullOrEmpty(alreadyCommented))
{
commentIds = alreadyCommented.Split(',');
}
if (commentIds != null &amp;amp;&amp;amp; commentIds.Length &amp;gt; 500)
{
File.Delete(&quot;bot-alreadycommented.txt&quot;);
}

 

Code Explanation

This code works in the same way as the text file that stores the number of posts. If the file doesn’t exists, it’s created. We read the IDs stored in the file and split the string into an array variable named “commentIds.” We’ll use this list of IDs to avoid double posting a bot reply.

The final section of code does the actual posting.

 

foreach (var comment in model)
{
bool commented = false;
if (commentIds != null)
{
foreach (var id in commentIds)
{
if (id.TrimEnd('\r', '\n').TrimStart('\r', '\n') == comment.Id)
{
commented = true;
}
}
}
if (!commented &amp;amp;&amp;amp; comment.Id != &quot;\r\n&quot;)
{
try
{
Comment redditMainComment = (Comment)reddit.GetThingByFullname(comment.Id);
truck.Replace(newPostCount.ToString(), (newPostCount+1).ToString());
newPostCount = newPostCount + 1;
try
{
redditMainComment.Reply(truck.ToString());
FileStream fs1 = new FileStream(&quot;bot-alreadycommented.txt&quot;, FileMode.OpenOrCreate);
fs1.Close();
StreamWriter file = File.AppendText(&quot;bot-alreadycommented.txt&quot;);
file.WriteLine(comment.Id + &quot;,&quot;);
file.Close();
}
catch (Exception ex)
{
newPostCount = newPostCount - 1;
}
}
catch (Exception ex)
{
//something
}
}
}

 

Code Explanation

This foreach loop goes through each comment and replies. The first section checks to make sure that the comment isn’t stored in the “alreadycommented.txt” file, because that would meant that we already replied.

If we haven’t replied to the comment, we then call the GetThingByFullname method to create an instance variable of the main Reddit comment. We pass the comment ID contained in the ViewModel to the GetThingByFullName method. Once the variable is created, we then reply to it using the Reply method. Notice that we reply with our template with the new number.

Each time we reply to a comment, we store it in the “alreadycommented.txt” file.

The final part of the code deletes the current post count value and replaces it with the most recent we calculated as we ran through the foreach loop. This was a quick hack, so it’s not the most efficient but it does the trick.

 

File.WriteAllText(&quot;bot-truckcount.txt&quot;, String.Empty);
File.WriteAllText(&quot;bot-truckcount.txt&quot;, newPostCount.ToString());

That’s it! You can get much more complex with your bots, but this is a general bot that simply replies to a comment. Use this code as a template to create your own.

Full Code

If you want to learn more about creating bots, check out this book.

Designing bots book

 

Here is the full code for our bot.


bool authenticated = false;
Reddit reddit = null;
var token = &quot;&amp;lt;your token from registering your app&amp;gt;&quot;;
reddit = new Reddit(&quot;&amp;lt;your bot account name&amp;gt;&quot;, &quot;&amp;lt;password&amp;gt;&quot;);
reddit.InitOrUpdateUser();
authenticated = reddit.User != null;
Subreddit sub = null;
if (!authenticated)
{
sub = reddit.GetSubreddit(&quot;&amp;lt;your sub name&amp;gt;&quot;);
}

var comments = sub.Comments.Take(50);
List&amp;lt;RedditCommentModel&amp;gt; model = new List&amp;lt;RedditCommentModel&amp;gt;();
foreach (var comment in comments)
{
if (comment.Body.ToLower().Contains(&quot;&amp;lt;my value&amp;gt;&quot;))
{
RedditCommentModel newModel = new RedditCommentModel(comment);
model.Add(newModel);
}
}

StringBuilder truck = new StringBuilder();
truck.Append(&quot;#X POSTS&quot;);

FileStream fs3 = new FileStream(&quot;bot-truckcount.txt&quot;, FileMode.OpenOrCreate);
fs3.Close();
string postCount = File.ReadAllText(&quot;bot-truckcount.txt&quot;);
int newPostCount = 0;
if (string.IsNullOrEmpty(postCount))
{
newPostCount = 1;
}
else
{
newPostCount = Convert.ToInt32(postCount);
}
truck.Replace(&quot;X&quot;, newPostCount.ToString());

FileStream fs = new FileStream(&quot;bot-alreadycommented.txt&quot;, FileMode.OpenOrCreate);
fs.Close();
string alreadyCommented = File.ReadAllText(&quot;bot-alreadycommented.txt&quot;);
alreadyCommented.Replace(&quot;\r\n&quot;, &quot;&quot;);
string[] commentIds = null;
if (!string.IsNullOrEmpty(alreadyCommented))
{
commentIds = alreadyCommented.Split(',');
}
if (commentIds != null &amp;amp;&amp;amp; commentIds.Length &amp;gt; 500)
{
File.Delete(&quot;bot-alreadycommented.txt&quot;);
}

foreach (var comment in model)
{
bool commented = false;
if (commentIds != null)
{
foreach (var id in commentIds)
{
if (id.TrimEnd('\r', '\n').TrimStart('\r', '\n') == comment.Id)
{
commented = true;
}
}
}
if (!commented &amp;amp;&amp;amp; comment.Id != &quot;\r\n&quot;)
{
try
{
Comment redditMainComment = (Comment)reddit.GetThingByFullname(comment.Id);
truck.Replace(newPostCount.ToString(), (newPostCount+1).ToString());
newPostCount = newPostCount + 1;
try
{
redditMainComment.Reply(truck.ToString());
FileStream fs1 = new FileStream(&quot;bot-alreadycommented.txt&quot;, FileMode.OpenOrCreate);
fs1.Close();
StreamWriter file = File.AppendText(&quot;bot-alreadycommented.txt&quot;);
file.WriteLine(comment.Id + &quot;,&quot;);
file.Close();
}
catch (Exception ex)
{
newPostCount = newPostCount - 1;
}
}
catch (Exception ex)
{
//something
}
}
}

File.WriteAllText(&quot;bot-truckcount.txt&quot;, String.Empty);
File.WriteAllText(&quot;bot-truckcount.txt&quot;, newPostCount.ToString());

2 Comments

  • Josh

    Hi Jennifer. Thank you for the article and write up here! I was wondering… What exactly is the RedditCommentModel? I did not see it defined anywhere. I basically created a new .NET project (windows forms, c#) and pasted the full code section at the bottom of the article into the form_load event just to check it out. I then pulled RedditSharp down with NuGet and added it to the using statements at the top of the class. I also added the System.IO to the top of the class. It recognized everything in the code except RedditCommentModel, so I was curious as to what that is exactly?

    Reply

    • jennifer

      Hi Josh, that is a separate ViewModel class that I created to hold the comment details. You can make your own and fill it with the details that you want to store. So you would create a class and name it RedditCommentModel and then the error will stop, but you need to create a method in the model to store the data from what reddit returns.

      Reply

Leave a Reply