Creating HorrorBot

An Instagram bot that utilizes DeepAI’s API

Connor
9 min readMar 14, 2022

05/02/2022 Update:
Changes to this bot will be reflected on GitHub. This bot has been stable for a few weeks now, and any small modifications will be pushed to GitHub.
https://github.com/connorkas/HorrorBot
https://github.com/connorkas/HorrorBot/commits/master

Introduction / Synopsis

If you know me, you know software/application development is NOT my thing. There’s a reason I major in Cybersecurity and not general Computer Science. Developing anything bigger than a quick Python script usually results in me staring at the screen for hours on end completely lost.

So why did I create this? It’s spring break and I’ve honestly gotten a bit tired with spending hours on TryHackMe and HackTheBox. So I decided to change some things up, create something a bit more interactive me and my friends can have some fun with, and strength my Python skills. The name HorrorBot doesn’t mean anything scary, but when using Deep Dream image generation sometimes you can get some pretty wild looking images.

I’ll go into a LOT more details breaking down exactly how this ‘bot’ (actually just a beefy Python script) actually works, but just to give you a brief synopsis as to what this is: Once a week, this bot will pull all of the comments from my most recent Instagram post and select any comments starting with the keyword imagine. It will then randomly choose one of the comments and run a Google image search query utilizing SerpAPI. After grabbing the output URL, it runs it through an API from DeepAI simply called Deep Dream for 5 iterations to give it a more ‘wild look’. Then the resulting image is posted, describing the input the AI was given and crediting the user who originally supplied it. That’s it! Just a fun bot I created to give myself some more practice working with public APIs and manipulating JSON data with Python.

How Does It Work?

To begin, this is a multi-level Python script that runs on a cron job once a week on Tuesday at 4pm CST. I purchased a cheap ($5/mo) Ubuntu 20.04 VPS over at HostWinds so once I completed the bot, I wouldn’t have to worry about doing anything manually.

To start, this script utilizes two APIs from a website called Apify. The first one is an Instagram profile scraper, which sends a GET request with the payload data of my public Instagram handle, with a result limit of ‘1’, which allows me to only deal with the most recent post. Once this request grabs the URL of my recent post, it saves it into a variable, which is then used to submit a POST request to grab the first 24 comments available. 24 is used due to rate-limiting that Instagram provides to requests made from an unauthenticated (not logged in) user.

cURL Example
POST request utilizing the Request PyLib

After retrieving the comments, the output is stored in JSON format in the temporary variable x, which is then put into a while() loop for a maximum of 24 iterations. Inside the while() loop, there is a try/except code block that loops through each JSON key called ‘text’, which compares each comment to see if the keyword imagine was used. If the keyword was found in the comment, it is put into an array containing the username and following input, which looks something like: “username:imagine: a dog”. The loop either will reach the maximum amount of iterations or when an IndexError is produced. An IndexError is produced when the increment of i is out of bounds (meaning there isn’t a key for the next caption). If you’re wondering why there isn’t a ‘cleaner’ approach to this, is because I couldn’t find a solution where the script didn’t know the quantity of JSON keys. So I have to run a maximum loop until an error-exception occurs.

After placing each comment into an array, the list of possible choices are logged and then randomly chosen using random.choice(array). The chosen comment and username are written to an arbitrary filename for logging purposes, and then we move onto image generation. After choosing a random input, the script passes on the comment to SerpAPI’s Google ‘isch’ API. It passes the original string containing the comment via a POST request, then with the dictionary that it received, I used next(iter()) to grab the first item from the dictionary since there isn’t a way to shorten the results. After this, the resulting image URL is then thrown into the Deep Dream DeepAI (via POST request) for 5 iterations to give it a more interesting look. Afterwards, the final URL is written to a file called ‘outputURL’ for logging purposes.

Grabs Image From Google With SerpAPI
Running Image Into Deep Dream For Five Iterations

The resulting image usually looks something like this:

Deep Dream AI image generation
Generated With Query, Then Fed Into Deep Dream AI

Once we’ve compiled our final image, it’s time to post to Instagram. Now, this actually turned out to be the most difficult part of creating this bot. Not because of any specific code that I had to write, it’s because working with Meta’s (Facebook) Instagram Graph API is really complicated and overly confusing in my opinion. First, I needed to link a Facebook page with my Instagram account that I had to convert into a ‘business’ account. Then, I needed to create a blank business page to be able to link my created App on Facebook Developers to my Instagram page, and after I got those accounts linked, I had to generate a short-term API token and make a GET request with more miscellaneous payload data via cURL, just to get a longer lasting API token since the short-term only lasts for about 60 days.

After gaining a long-life access token, we’re finally able to make a request to POST to our account. There’s probably a good reason for how this functionality works, but instead of making a direct request to post your newly created image, you need to make a request supplying the original image you’re using with a URL-encoded caption, and then supplying the access token you were just given. Afterwards, Meta will send you a ‘creation ID’ which you supply in your final request along with your access token to post your image. After this compiling this, I was able to put everything together into a feasible script.

Opens Comment From ‘word.dream’ and Generates Creation ID
Uses Creation ID to Post to Instagram

Now we’re finished with the bulk of the project! To summarize, we’ve pulled all of our comments from the most recent post using an API hosted on Apify, then put every comment with the keyword imagine into an array. Afterwards, we randomly chose a comment from that array, and stored the input and username of who created it. Then, we ran it through DeepAI’s API, grabbed the resulting URL, and submitted the request to Meta to generate a creation ID. After generation, we submit that creation ID along with the access token and boom! We’ve just created a fully automated bot. All that’s left is to put it into a cron job.

Cronjob which runs at 21:00 (UTC) every Tuesday

If you have any questions, comments, or input please reach out to me. I’d love to talk more about with you. If I do a complete revamp of my code, I’ll be sure to make a new post regarding my changes and what’s different.

Build v1.2

(03/18/2022)

05/02/2022 Update:
This functionality is still the same. There is optimization improvements but overall I am still pulling the same information from api.keys. Screenshots have been updated to reflect this.

I’ve officially made HorrorBot open-source on GitHub! I’ve also fixed a few issues that I had previously mentioned as well. The biggest problem I personally had with this script was the API keys being hard-coded into the application. If a threat actor was able to gain access to my VPS, they could use these keys (specifically the Instagram Graph API) to possibly extract personally identifiable information about me. But, I’ve implemented a solution which allows for the API keys to be stored in a separate file. While this doesn’t exactly ‘fix’ the problem of someone gaining root access to my server, it does allow for better security practices to be put into place.

Checks For api.keys File and Assigns to Value
Error Checking Before Script Starts
Contents of api.keys File

Personally, I believe that the way I checked for these keys could be improved, but sadly Python ≥3.9.XX doesn’t have any sort of alternative to switch/case which would have been my go-to in this situation. My other thought was to add some sort of dictionary based solution, but that’s for another time.

As previously written down in my ‘To-Do” list, I’ve implemented a method to prevent abuse, which would have allowed a user to up their chances by commenting multiple times. My solution was creating a temporary array called usernames[] and appended each new user throughout each iteration of the loop. Inside the loop, there is an if-statement checking to see if that username is already present in the previously mentioned array. If found a simple pass is invoked and the loop continues.

That was the main bulk of fixes, mostly I’m just happy it’s available to the public now. If you have any suggestions please let me know.

Limitations & To-Do

Now, this script/bot isn’t perfect by any means. If you’re familiar with Python or general coding practices you may have already caught a few things I could improve on with this code, and there’s a few ideas I’ve already had to completely revamp this script. I’m going to go over a few limitations/improvements I’m looking to make over the coming weeks. I’m releasing this to a small group of people initially to see how it works against a live audience and work out any kinks that may come up.

Implement saveBot() Function

If you take a look at the image/caption generation in the script, you’ll notice error checking for Aspect Ratios. Meta doesn’t allow certain aspect ratios to be posted directly, resulting in the script erroring out. I want to implement a saveBot() function where it automatically resizes the aspect ratio to 1:1 so the bot will be able to post the image regardless.

Fix Comment Limit

Earlier in my post, I mentioned how the API can only pull the safe amount of 24 comments while not being authenticated. As of right now, I’m not worried about the 24 comment limit being hit, but the main problem is that regardless of whether or not someone types the keyword, that still counts as 1 of the 24 comments. The comment checking is only done AFTER all of them have been pulled.

Improve Logging

As of now, logs that are generated each run have to be manually moved to their respective folder. I kept it in this state during its ‘beta’ to make sure things have been going well, but now that I’ve fixed most issues and it seems to be stable, this is very high on my list.

Please reach out to me if you have any questions.

-connor

--

--