February 12, 2021

How I automated our Instagram account with Outsystems

Bill Gates is often credited with the quote, “I will always choose a lazy person to do a difficult job because a lazy person will find an easy way to do it”. Despite this quote not actually coming from Bill, it’s still a great counter-intuitive thought. I often find myself in the position of the lazy person and end up automating tasks I don’t have the patience to do or that I feel waste my time… and it just happened again when I decided I wanted to have a business Instagram account. Although I might have bit more than I could chew, when all I wanted was a bot…

Don’t want to read through all this tech jargon? Click here to see the final considerations.

It has been a while since I last decided to ramble about random stuff online…
But hey, it’s February 2021 and we’re supposed to stay at home. I have to keep myself busy.

Obviously a post isn’t going to keep me busy for long, so I needed a bigger weekend project to make those hours go by a bit faster… and to make talking about it actually worthwhile. After going through a few dumb ideas and not liking any in particular, I finally kind of gave up and started mindlessly scrolling through Instagram… and that’s when it hit me:

Why the fu#k don’t we have an Instagram account at Base64?

Well, there’s no single answer for this. First, it’s not a very tech/business oriented platform, so we never really gave it a second thought. Who am I kidding…  the truth is we just don’t have time to keep up with all that social media action. We have our actual projects and teams to manage and we prefer to spend our time that way, because that’s what we do best.

A normal person would probably look at hiring someone for a social media manager position. A sane person wouldn’t even consider tackling such a project on his own. Well, I’m neither of those persons, and now all I could think of was

I want a goddamn Instagram  account!

This would probably end up as another one of those unfinished projects in my 7GB repositories folder, if it wasn’t for another driving factor I haven’t mentioned yet.

You see, I’ve been working with Outsystems for a while. I was there when ajax refresh was nowhere to be found, refresh queries were a myth and Avril Lavigne’s Girlfriend was the most watched music video on Youtube. And through all these changes, there’s an old friend of mine that has always been there for me. Whenever a customer made some outlandish request my friend rescued me, more than a couple of times, and helped me deliver whatever miracle I was being asked of. But that friend of mine has been struggling lately. A group of new kids took over his show, and now you barely even hear about him these days, even tho he’s still hard at work right under our noses.

That’s right, I’m talking about good old C#. All you hear about these days is react, cordova,  mobile apps, client side distributed computing, and all those trendy tech words. That’s all fine, I love those toys as much as the next nerd, but I feel my old friend deserves a bit more credit. This is my way of reminding everyone there’s still a powerful ally running the show. 😈 And that some of us used to have dinosaurs for pets, I guess…

 

THE REQUIREMENTS

I started by writing down some basic requirements:

  1. I want automated posting to Instagram
  2. By automated, I mean I don’t even want to worry about generating the content – No time, remember? (Also, I’m lazy AF)
  3. I want to post stories as well (Instagram API’s documentation clearly says they don’t support stories, but we’ll see about that)
  4. I don’t want to infringe on Instagram’s Terms & Conditions – It wouldn’t be a very useful project if the account got banned, would it?

Delivering these will not only give us a permanently active social media front page, but also, showcase a bit of the technical prowess Base 64 prides itself on.  😏 And maybe, just maybe, by the end of this whole endeavor it might actually turn into something that can evolve into a product, it’s just a PoC for now anyway.

 

INSTAGRAM’S API

I started with the easy part. Getting connected to Instagram.

To do this, I take some time to read up on Instagram’s Graph API documentation.
Turns out, that to make a simple post I will need a few things, according to Zuckerberg’s minions.

Before I even start coding, I will need a Facebook Business Page, an Instagram Business profile, get those two linked with each other and finally an app created at developers.facebook.com. I quickly get all of that done, so I can finally start coding.

Next, I will need to get an authorization token from facebook – Yes, Instagram is owned by facebook, remember? I will use this token to access Instagram’s API later. This token is obtained through OAuth. It’s fairly simple to connect with OAuth, but why bother? There are quite a few ready components on the forge for this. So I’ll just download one of those and make a few modifications for my needs.
Specifically, I need to store the resulting {AccessToken} into a datababase table instead of session or client. Why? Because the whole posting operation will be made by a timer, and that means I won’t have access to session or client side stuff. So now, with a little software re-engineering I have a table to store access tokens that I can use later.

With the access token in my possession, I can go on to actually start calling Instagram’s API’s. It’s just a sequence of fairly simple REST calls.

GET https://graph.facebook.com/v9.0/me/accounts?access_token={access-token}
This will return all my Pages, including {facebook-pageId} for each.
GET https://graph.facebook.com/v9.0/{facebook-pageId}?fields=instagram_business_account
This will return the {InstagramId} account Id of the linked instagram account.
POST https://graph.facebook.com/{InstagramId}/media
To create a media object on Instagram, getting the {MediaObjectId}.
POST https://graph.facebook.com/{MediaObjectId}/media_publish
To make the post public

Because these are so simple and I have the access token already, it’s almost a direct  copy and paste from the documentation into Service Studio’s interface to get those suckers running.

And just like that… BAM! In less than an hour I have my Instagram Connector ready and tested, complete with Open Authorization.

 

GETTING SOME CONTENT

Now that I can post stuff to Instagram, I need something to actually post there.

I’ll start by deciding on what kind of content I want to post.
It needs to be something simple, something that’s related (at least remotely) to what we do and something that people can identify with. But who will be my audience? Being the hardcore nerd and internet aficionado that I am, I think I want my fellow coders to be my audience. One particular area I think Instagram is lacking, is a steady stream of jokes based on our pain. It’s decided then.

I’m going with programmer memes.

There’s no REST API for this, of course, but who needs that kind of modern day witchcraft anyway? Time to recruit my dear old friend, C#.

So I select a single online source for my content. Their content is user submitted. Users submit a short video or GIF, a caption for that video and that’s it. They’ve been online for years, and that means there’s tons of content to choose from.

What I need to do here is pretty simple. I need to GET their HTML then select the parts of it I want, Caption and VideoURL.

For this, all I need is a NuGet package called HTMLAgilityPack. It allows you to load URLs, then select parts of the HTML through xPath selectors. *old man voice* This is called web scraping, kids.


* this is a simplified version of it

To finish it up, I’ll just paste this code into Integration Studio, and a few minutes later, here’s a little orange orb I can drag around for my Outsystems needs.

That was easy. Now I can post, and I have content to post.

By now you’re probably thinking “You must be almost done then”

No no no, my bot loving amigo!

* see what I did there? Already putting the scraper to good use.

We still have “a few” issues to address. Did you think I’d bother writing a “love letter” to C# if all I had to show was a scraper…? I find that kind of offensive.

It’s time to make another list of requirements

  • Instagram posts are 600×600. The videos we’re getting have random sizes and ratios. To make them look good, I need to resize the videos to correctly fit into Instagram’s post format.
  • The videos are only a second or two long sometimes. If I want to add story support, because stories don’t loop on Instagram, my videos actually need to stretched. I want all videos to last at least 15 seconds.
  • I could just put the caption on the post’s text field, but then our followers would have to read that tiny font everytime. And that takes away from the meme experience. So I want to add the Caption to the video itself.
  • I also want to add a watermark to the video, so that if someone shares it, there’ll always be a Base64 logo on it.

Do you see where I’m going with this…? That’s right.. *ahem*

And for my next trick… I will be turning an Outsystems server into a video editing robot

Sorry, I couldn’t help it… that’s just not something you get to do or say often.

 

VIDEO EDITING THE SH#T OUT OF THOSE VIDEOS

Let me start with a question. What is a video?
To put it in simple terms, a video is a collection of images, called frames, that play through at a fixed rate of X per second – frames per second.
If we start thinking of a video like that, suddenly the task seems a little bit less daunting because we stopped talking about “video editing” and “downgraded” it to “image editing” an array.

Before getting into the methods used to accomplish those video edits, let me just mention the C# libraries I chose for the task

  • System.Drawing – For image editing. Since I’ll be doing simple image resizing and drawing, the included .NET libraries are more than enough for this task
  • Emgu.cv – For video editing. I’ll be using this to extract video data and add/replace frames to the video. Pulled from NuGet

* deep breath *

The first thing I need to do is open the video with one of the libraries. That will allow to me read and rewrite the original video.
With this, I’ll extract the frames as an array and read the original video’s FPS and Length – These will be useful later.

Now, how do you resize a variable size image into a fixed square (600 x 600) without distorting it?
If the original video was square, all I would need to do would be to resize both dimensions (width and height) by the same value. But videos are rarely square, they’re rectangles, sometimes wider than tall and sometimes taller than wide. I need to fix this.

I start by checking which dimension is larger, width or height. This way I can resize the frame to the maximum size of 600 on that larger dimension, and because the remaining dimension is smaller, if I apply the same multiplier to the shortest one, it will never reach 600. This will generate a maxed out image into either the width or height dimension, depending on the original video’s aspect ratio. (Resizing ✔️)

Next, I need to position the new resized image into the center of the frame. Using the smaller dimension (because the other one will be maxed out) I paint the new frame into the center of a black rectangle (600 x 600) (Positioning ✔️)

Since I’m editing the frame, I’ll just use this same cycle to draw something over it and just paint a watermark.png over the new frame at one of the corners. (Watermark ✔️)

Now that I have a new scaled and positioned frame, I’ll simply replace the old frame with the improved one. Then repeat the whole process for every frame in my array.

But the video still doesn’t have a caption. I decided to add it to the beginning of the video, as some sort of intro. If you’ve been paying attention, you probably have an idea of how I’ll be doing this.

I’ll start by painting a single frame as a black rectangle with white text in it.
(I have a function to calculate font size based on frame size, font face and string length, but I don’t think it’s worth getting into detail)
I want to show that text for X seconds at the start of each video. So I just add that single frame to the beginning of my array for as many times as needed. I’ll need a fixed amount of these frames per second. But how many frames in total you ask? FPS times X. (Caption ✔️)

Finally I have a correctly sized video with a caption at the start. But it still needs to loop. Because the original video may actually be just 2 seconds longm, that’d be shitty to put on Instagram. I need to calculate how many times that original video can loop without exceeding the 15 second limit. That’s a simple calculation. Math.floor((15 – X) / OriginalLength). That’s MaxVideoLength (15) divided by the OriginalLength, and rounded to the lower nearest integer. I subtracted X to the time limit because, before, I added those X seconds as the caption intro. The result of this expression is how many times I’ll repeat all the frames of the original video.  (Video length ✔️)

I love it when a plan comes together
– You know who said this

This solves the video issues for my post contents. But remember I also wanted to generate videos for the story?
I won’t get into too much detail on that, because the math is roughly the same, but I modified the generating functions to generate a second video simultaneously, using the story dimensions (1080 x 1920), and changed the layout a little. Because of the huge portrait height, I have a bit more of screen real estate, and I can actually overlay the text on every frame of the video. That way stories won’t look like lazy copy/paste crap.

This isn’t as easy to show as the scraper function… but I’ll try and simplify it.

Making this compatible with an extension was a challenge in itself, suffice to say it didn’t follow the traditional methods. The details on how I got around those issues are boring. I’ll just say Integration Studio’s 32 bit process didn’t like the amount of files for that extension, and didn’t let me compile into a specific x64 build I needed. Poor thing got overwhelmed. Despite that I still made it work in a convoluted awesome way.

This is the result:
* if you’re wondering why these aren’t 15 seconds long, it’s because the original video was actually 8 seconds long. Looping it would make the generated video exceed 15 seconds, so the generator skipped that. That just means my algorithm is working properly.

Original Video Video for Post Video for Story

If anyone ever tells me bots are unfair or whatever bullsh#t, I’ll just direct them to this story, so they can see how much work a bot actually is, then tell them to fuck off.

It’s not cheating if you’re the one actually coding the bot! Suck it!

 

PUTTING IT ALL TOGETHER

This’ll be easy. Just setup a timer to post three times a day, execute all those pieces in the correct order and add an intermediate step to store those videos in the server’s File system so Instagram can access them.

And my basic bot breathes for the first time! *evil laugh*

Hold on, it’s too early to see it yet.

WHAT ABOUT THE STORIES?

Remember I wanted stories as well. It says clearly in the documentation

Oh noooo. What will I do? 😱😱😱😱😱😱😱 

Heh. Just kidding,

I am a man with a plan!

I could use InstagramSharpApi to make another extension and post stories. But that’s not allowed by Instagram, and I’ve set myself a requirement that getting banned wasn’t acceptable. (That library works, in case you’re interested)

There’s a legal alternative, and one that I actually prefer as it will give me a bit more control over the bot… and also increase the scope of the project by some degree… but never mind that.

Apparently, stories are allowed from mobile devices.
Outsystems is also amazing at doing mobile apps quickly. This will be way easier than video editing.

So now I have a new list of features

  • Have a native App
  • Have that Native App support sharing stories through Instagram
  • Have that app integrated with Firebase

By developing a companion app for my bot, I’ll be able to do a couple more things

  • Review content before it gets posted (I’ll change that timer to only generate but not post the content)
  • Receive notifications when content is generated (Hence the need for Firebase integration)
  • Post stories legally

 

POCKET VERSION

Fuck me… When I started this I didn’t think it’d take these many parts… Oh well, this isn’t the first time I’ve had to deal with scope creep  anyway, I’m just acting so surprised.

Setting aside the feeling this will keep growing forever…

Behold, my  awesome Paint .NET skills


Yes, that’s a drunk robot. I don’t see the problem.

I fire up Service Studio, create a new Mobile App, and immediately paint it black. You know, ♫ I see a white app and I want it painted black, no bright screens anymore, I want them to turn black.♫

Pull the reference for the table I was saving the MEDIA metadata on, create a list screen over that data and a detail screen so I can review the media. This took me like 10 minutes to put together.

For that share button, I’ll need to integrate with the Instagram App itself. So a quick search on google takes me to an existing cordova plugin that (sort of) does what I need. I fork it, make a couple of changes in the Java code to make sure it compiles with Outsystems MABS and supports videos (the original only supported pictures), and just like that I got a brand new native plugin for my purposes. (I relied on the official documentation for the changes).
* maybe I’ll get into the details of that one at another time, I don’t want to take the spotlight too much from C#

Time to implement my “Share this” button logic with my new awesome plugin.


* I really love simple actions… <3

Now I can share automatically generated stories. I never knew being “an influencer” could be this easy… 😏


*took a while to load the Instagram app cause I have to download the video into the device in the background, and my internet was awfully slow that day

The last thing I have to do, is to get some Firebase notifications going. This will warn me whenever new content is Generated. I’ve done that several times before, and even built my own Firebase plugin by now, so that will be really fast.

There’s not much code to show here to be honest. I’ll just give you a quick and dirty run down of the steps for this, using an existing Firebase module from the forge.
I went into console.firebase.google.com and created a new project for my App. After that, inside that project I created an Android App with the package name matching the one I used in the Outsystems build settings and made sure to download the configuration JSON when given the option. Before I left the console, I went into the project settings and copied the FCM Server Key for later use. Back at my computer I zipped the .JSON file into a google-services.zip, added it as a resource in my App’s main module, set the deploy options to “Deploy to target directory” and set the directory as firebase.myAppPackageName/. Don’t expect screenshots for that, this is not a tutorial and the plugin I used has pretty good documentation. Sorry about that, reading documentation is an important part of practice haha.

Finally, I drop a sendNotification call at the end of the media generating process, use the FCM Server Key I got from Firebase console and…


FINAL CONSIDERATIONS

Yes, there are better ways to do this. But I wanted to do it ONLY with a free Outsystems personal area.
Limiting yourself in this way, allows you to get creative and focus on problem solving with what you have.

I’m fluent in Outsystems, C#, Java, JavaScript and CSS (And a few more things that I didn’t need for this specific endeavor…).
This project needed all these skills to be used together, within the Outsystems platform. Outsystems is a wonderful tool but, as you can see, there’s no harm in knowing other technologies. They add to your problem solving toolkit, they add to what you can do with Outsystems and consequently to what you’ll be able to deliver with it. (I’ve always been a huge supporter of platform extensibility)

TOTAL PROJECT TIME: 32 hours (about 4 weekends)

This project has the potential to grow in many ways.
Right now I added a single content source, but nothing is stopping me from adding as many as I want or need – and they don’t need to be silly like this one. The Instagram API provides a a few more possibilities (metrics, comment moderation, analytics, etc) for businesses that might be worth exploring, probably coupled with AI (maybe next time). Scalability would be an issue with the current architecture as it was designed as a PoC on a free infrastructure I don’t control, so it would need some refactoring there. The video editor extension in itself, if I expanded the input parameters to a full blown data structure and expanded internal capabilities, beyond resizing and text drawing, has the potential to even make it to a custom UI web based video editing tool.

So now I have an Instagram account that basically runs itself content wise, and won’t take me more than 2 minutes every day. To do it, I integrated with JSON APIs to make an Instagram Connector, built a scraper and a video editor with C# to be encapsulated within extensions and built a mobile App, complete with Firebase integration and Instagram interoperability achieved through the modification of a Java cordova plugin that also required me to do some Javascript.

And all this, because I was bored and decided to fool around… 🙂

Oh… I almost forgot… 

Now that you know there’s actual work and skill behind it all, follow us for the occasional laugh