DEV Community

loading...

Tweet From GitHub Actions

David Dal Busco
Creator of DeckDeckGo | Organizer of the Ionic Zürich Meetup
Originally published at daviddalbusco.Medium ・3 min read

Photo by Ravi Sharma on Unsplash


I launched recently DiscoverWeekly.dev a website that shares each Wednesday the new music playlists made by the developers.

To spread the information and let people subscribe, in addition to a RSS feed, I opened a Twitter account and developed a Bot which tweets once a week about the news playlists as well.

Here is how you can also create a Twitter Bot that runs periodically in GitHub Actions.


Twitter API

Tweets are posted with the help of the Twitter API. To get access to such a feature, you need to create a developer account and request access. You can start your application here.

The process takes several steps but, overall, can be solved in a couple of minutes.

Once you get access to your Dashboard, create a new project.

In your “App permissions”, request the “Read and Write” permissions.

Finally, under “Keys and Tokens”, generate all “Access token & secret”.

Note that if you have generated your secrets before requesting the "read & write" permission, you have to regenerate your secrets.


Library

Even though consuming the Twitter API does not look like rocket science, using an already well made Node.js library ease the process. In addition, Twitter is currently developing a new API (v2) but, the one we are looking to use, statuses/update, is not yet migrated (v1). Therefore, using a library as an extra layout might help us in that case not to have to rewrite everything in the future.

That is why we are adding the twitter-api-client library to our project.

npm i twitter-api-client --save-dev
Enter fullscreen mode Exit fullscreen mode

Twitter Bot

To develop the Twitter Bot, we create a new file twitter.js at the root of our project. It contains a function which takes care of the tweet itself, using the twitter-api-client , and a top level async block which triggers it when the script is run.

In order to not expose publicly the tokens we have created previously, we are accessing these through environments variables (see next chapter).

Finally, in following example, the tweet is a constant message. As any tweet, should be maximal 280 characters long, can tag usernames and point to Url.

const {TwitterClient} = require('twitter-api-client');

const tweet = async (status) => {
  const twitterClient = new TwitterClient({
    apiKey: process.env.TWITTER_API_KEY,
    apiSecret: process.env.TWITTER_API_SECRET,
    accessToken: process.env.TWITTER_API_ACCESS_TOKEN,
    accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
  });

  await twitterClient.tweets.statusesUpdate({status});
};

(async () => {
  try {
    const myTweet = 
          `Checkout @discoverweekly_ https://discoverweekly.dev`;

    await tweet(myTweet);
  } catch (err) {
    console.error(err);
  }
})();
Enter fullscreen mode Exit fullscreen mode

To run the Bot, we add a related scripts target in our package.json .

"scripts": {
  "twitter": "node ./twitter.js"
},
Enter fullscreen mode Exit fullscreen mode

Environment Variables

To make our tokens and secrets, we are handling through environment variables, available to our GitHub Actions, go to your GitHub repo > Settings > Secrets and add these with their respective values.


GitHub Actions

Finally, add a GitHub Actions such as .github/workflows/twitter.yml to your project which run the Node.js script we created before periodically.

In my case, as I publish once a week new playlists and tweet about these only once too, the workflow is scheduled to run at specific UTC times.

name: Twitter bot

on:
  schedule:
    - cron: "0 15 * * 3"

jobs:
  build:
    name: Tweet about the new playlists
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@master
      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '14.x'
      - name: Install Dependencies
        run: npm ci
      - name: Run Twitter bot
        run: npm run twitter
        env:
          TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
          TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
          TWITTER_API_ACCESS_TOKEN: ${{ secrets.TWITTER_API_ACCESS_TOKEN }}
          TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
Enter fullscreen mode Exit fullscreen mode

Summary

I was surprised to notice how straight forward it was to develop such a Twitter Bot and it was actually kind of fun. I hope this article will help you too.

To infinity and beyond!

David


You can reach me on Twitter or my website.

Give a try to DeckDeckGo for your next slides!

DeckDeckGo

Discussion (15)

Collapse
michaelcurrin profile image
Michael

I was inspired by this post to make my own repo that is Python-based.

If you have a copy of the repo, you can use a button to start a workflow run and enter some text you want to appear in the tweet.

github.com/MichaelCurrin/tweet-gh-...

Collapse
daviddalbusco profile image
David Dal Busco Author

That's awesome Michael 🚀👍

Collapse
ben profile image
Ben Halpern

Ha, neat.

Collapse
lucasew profile image
Lucas Eduardo

I've made a Twitter bot that replies to tweets that match a keyword.

IFTTT is the trigger and sends a webhook to a pipedream endpoint.

I just appear sometimes here to see what is going on.

Months without problems. Pipedream has its own managed authentication so I don't need a Twitter developer account.

Now the bot is fully autonomous: twitter.com/ntemsinaltimbot

Collapse
daviddalbusco profile image
David Dal Busco Author

For sure nowadays with no-code or low-code solutions there are plenty of options.
Yours is a nice solution Lucas 👍

Collapse
michaelcurrin profile image
Michael

In addition to cron, you could setup your workflow to run on a button click, if you don't want to wait for the cron time to roll around. You can even specify custom text input to type in each time like Tweet message.

michaelcurrin.github.io/dev-cheats...

Collapse
daviddalbusco profile image
David Dal Busco Author

Cool and handy 👍

Collapse
michaelcurrin profile image
Michael

Thanks for sharing.

In latest Node, you can use await at the top level without making a function to wrap it.

Collapse
daviddalbusco profile image
David Dal Busco Author • Edited

Yes indeed. In my case I did not use it. In the Actions you can specify the LTS version as following:

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
  with:
    node-version: '14.x'
Enter fullscreen mode Exit fullscreen mode
Collapse
michaelcurrin profile image
Michael

Oh yes good point

Collapse
zwacky profile image
Simon Wicki

This is inspiring me to use the gist with other things like changelogs or releases, that's really sweet! Thanks for the post

Collapse
daviddalbusco profile image
David Dal Busco Author • Edited

That's a genius idea @zwacky 🤯

Something like following (not tested) would tweet the release notes

const {TwitterClient} = require('twitter-api-client');
const fetch = require('node-fetch');

const tweet = async (status) => {
  const twitterClient = new TwitterClient({
    apiKey: process.env.TWITTER_API_KEY,
    apiSecret: process.env.TWITTER_API_SECRET,
    accessToken: process.env.TWITTER_API_ACCESS_TOKEN,
    accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
  });

  await twitterClient.tweets.statusesUpdate({status});
};

(async () => {
  try {
    const {body} = await fetch
          `https://api.github.com/repos/deckgo/deckdeckgo/releases/latest`;

    await tweet(body);
  } catch (err) {
    console.error(err);
  }
})();
Enter fullscreen mode Exit fullscreen mode
Collapse
mustapha profile image
Mustapha Aouas

Really nice!

Collapse
omril321 profile image
Omri Lavi

That's really cool, thanks! :)

Collapse
daviddalbusco profile image
David Dal Busco Author

My pleasure 😃

Forem Open with the Forem app