DEV Community

Cover image for So you want to make a Twitter bot (2.5/3)
Laura buns
Laura buns

Posted on

So you want to make a Twitter bot (2.5/3)

Sorry for going final harry potter movie on you. Anyway

OMG OMG it's ALMOST part 3 it's THE FUN PART TODAY – up until now all the steps we have made (see part 1 and part 2) are mostly plumbing, you gotta do these for each bot (there are a couple tricks to automate this. You can duplicate repos and heroku apps, and you can get all your keys using a single app like this).

However our actual posts leave a lot to be desired. i can't teach you to be funny (if i knew the secrets to that I'd be funny myself). In my own bots I use a lot of images to accomplish this. In cases like @readyforspoons or @fakefantas they are the whole point of the tweet. the dirty little secret here is that they are webpages:

There's a tool called puppeteer that i never know how to spell and that more serious people than me use for actual purposes that lets you automate spinning up Chrome (okay it's Chromium. Who cares, not the point), taking a screenshot of certain dimensions, and saving it to a file. This is a very neat closed loop, it means we can develop this in isolation* and then from our tweeting app, pick up the image and tweet it.

okay, almost isolation. In most cases you are gonna get some more stuff back from the page, like the text for the tweet or whatevers. More on this later

I'm sitting on a mcdonalds listening to fall out boy massively hungover thinking about deep stuff so let's make an aesthetics bot that makes these poster things you see on tumblr but it's lyrics from smash mouths 'all star' instead. (This is a terrible bot idea don't do this there's v little variation)

Tumblr aesthetics

Now, research is always an important first step, there's a lot of different aesthetic quotes and we gotta figure out how much variation we want. Do we wanna cycle through images or just have one? (we are gonna have just 2 bc im lazy). If we are using random images, where are we gonna credit the authors? (i mostly riff of brands and for this I'll just use photos I took, you too can use VSCO on your phone). How do we wanna show the text? italic arial is a classic but if you want a challenge, doing chat bubbles is a cool idea. (we aren't)

I had a play around in sketch and much to my despair this looks exactly like a normal aesthetic post. there's no joke here, no clever subversion. Just teenage angst. I don't care. We are doing this anyway, you find a way to make the content work on your own:

Websites

I have a bit of a confession to make, I got not clue how to make a webpage from scratch, the script and css tags elude me, so let's just copypaste some boilerplate from w3schools into a glitch sandbox because why not:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <h1>Okay this works</h1>
  </body>
  <link rel="stylesheet" href="/style.css" />
  <script src="/script.js"></script>
</html>
Enter fullscreen mode Exit fullscreen mode

And throw in some CSS for styles:

/*
so we cant actually use real arial! this 
is bc your computer probably has it 
but the remote computer running this doesn't.

what this means is that I went to 
google fonts and picked a look-alike. Close enough!
*/

@import url('https://fonts.googleapis.com/css?family=Archivo:600i&display=swap');

* {
  /*
  worlds barebonest css reset
  */
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  /*this centers our heading*/
  background: url(https://cdn.glitch.com/15a843df-5327-4b8b-b3ef-ca56f7e35e5e%2F46653054781_f96753b868_o.jpg?v=1576936807505);
  background-size: cover;
  width: 100vw;
  height: 100vh;
  /*
  vw and vh are viewport units, 
  they are 1/100th the size of the 
  browser window, so using 100 means
  we tell body to fill everything.
  */
}

h1 {
  color: #AF9C3E;
  font-family: "Archivo", sans-serif;
  font-weight: 600;
  font-style: italic;
  text-transform: uppercase;
  text-align: center;
  /*
  this is a design thing
  */
  text-shadow: 2px 2px 0 #000;
  filter: blur(.1vw);
  /*
  we can use viewport units 
  for font sizes too and this will 
  make them scale with the window!
  */
  font-size: 5vw;
  padding: 0 10vmax;
}
Enter fullscreen mode Exit fullscreen mode

And we got something that looks like this (I started this from scratch on glitch. At this point all we really care about is making a stand-alone webpage, we'll 'integrate' it into our 'pipeline' later:

Let's actually plug some quotes into our JS (Get them here)

const ALL_STAR = [
  `Somebody once told me the world is gonna roll me`,
  `I ain't the sharpest tool in the shed`,
  `...`
];

const quote = ALL_STAR[Math.floor(Math.random()*ALL_STAR.length)];
console.log(quote);
Enter fullscreen mode Exit fullscreen mode

If the console.log is actually working let's plug the quote into our heading and see if that works out okay

document.querySelector("h1").innerText = quote;
Enter fullscreen mode Exit fullscreen mode

Fun fact! We can use this oneliner because our script tag is at the end of our index.html, so the script is not gonna run until the browser has parsed the whole document, meaning at this point it already knows what our elements are.

Do you know what would be, like, real cool, right now? let's randomise the text color, i'm sick of that yellow already. I'm sure we can do npm i random-color-or-whatever but let's not do that because css is awesome when you are only doing websites for the last version of Chrome

In our CSS, let's switch our color up to use HSL. HSL rules, it stands for hue, saturation, and luminosity and what this means is that if we mess with the hue we get a whole rainbow of colors that all match the mood we are going for, and we can plug the hue as a css variable:

h1 {
    ...
  color: hsl(var(--hue, 0), 80%, 53%);
  ...
}
Enter fullscreen mode Exit fullscreen mode

That we then set from our javascript like madmen:

const $h1 = document.querySelector("h1");
$h1.innerText = quote;
$h1.style.setProperty("--hue", Math.random() * 360);
Enter fullscreen mode Exit fullscreen mode

(The second argument in var(), where we got a 0, is a fallback. if the CSS parser can't find a variable named --hue it'll use that value. For this use case it doesn't really matter because this is a closed loop and we know this variable is gonna be put in, but picking up good practices doesn't hurt.

Let's clean this up and do another for the text transform because omg this is FUN

const hue = Math.random() * 360;
const textTransform = Math.random() > 0.5 ? "lowercase" : "uppercase";
$h1.style.setProperty("--hue", hue);
$h1.style.setProperty("--text-transform", textTransform);
Enter fullscreen mode Exit fullscreen mode

You might have noticed that our hue thing is mostly working but also we got a problem, when the hue is on the blue range it kinda blends with our background. What's really cool about HSL is that we can sorta fix it? we know that when our hue is around a certain value (i fiddled with the devtools to find out which, if you don't wanna do it, it's 200-300) the luminosity is not enough and it just so happens that we have a luminosity value and it's another number we can mess with.

We can make something far nicer and maths based here but i don't have paper at hand and also i'm not a fucking nerd so I did this and it works out fine:

const hue = Math.random() * 360;
const textTransform = Math.random() > 0.5 ? "lowercase" : "uppercase";
$h1.style.setProperty("--hue", hue);
$h1.style.setProperty("--text-transform", textTransform);

if (hue > 200 && hue < 300) {
  $h1.style.setProperty("--lumi", "80%");
}
Enter fullscreen mode Exit fullscreen mode

And plugged the --lumi variable in the css

h1 {
    ...
  color: hsl(var(--hue), 80%, var(--lumi, 63%));
  ...
}
Enter fullscreen mode Exit fullscreen mode

There's like a lot you can do with just HTML and CSS to generate images for bots. Here's what I ended up with. Try out randomizing other values! If you wanna move your text flexbox is fantastic for that. Other things I've done myself is use 3D transforms or play a youtube video in the background to capture stills of it!

Let's deploy

This is gonna move on to a final fourth part because i'm running out of weekend but let's get this integrated into our bot anyway,
One last thing! Because of annoying things like 'security', or 'progress', or 'chrome being a little bitch' having some static HTML website locally just doesn't cut it for a lot of things, including screenshotting it. You might also wanna scale up your JS to import multiple files or use SASS. Three very straightforward ways to take what we've got and put it on a real URL instead of a file URL:

We keep it on glitch and do nothing else

It does have a URL and this is gonna work - only thing is this is all a bit fragile, normally you want your code yo be a closed system.

If it's only static like this we can use serve

If you download this stuff as a zip and open a terminal and type npx serve you will immediately get a localhost url pointing to this folder. This is powered by the serve NPM package

If it's a bit fancy and you want SASS or babel or whatever

Use parcel. use it like serve (npx parcel watch index.html). This will also serve your website on a local URL but behind the scenes it's compiling your js and css. You can read the docs to get an idea of the plugins you can use. This is what I do with most of my (newer) bots.

All set!

Next week we'll use puppeteer to take an image out of this and link it with the bot. have a good one!

#twitter

Top comments (2)

Collapse
 
mrrwmix profile image
Matthew Winemiller

Eagerly anticipating part 3, or 2.75 or whatever the next installment will be. Thanks for all you've done so far!

Collapse
 
johnstonmatt profile image
Matt Johnston • Edited

Re: Script tag placement fun fact
Now I understand why everyone does this and why sometimes my apps don't work, thanks!