Bot-Tutorial: Automatically create Twitter tweets for new Posts (nodeJS, steem-js, bitly, twitter)
After the great response to my first steemit bot tutorial, I want to present you a second bot implementation based on "steem-js".
You can find my first bot tutorial here:
https://steemit.com/steemdev/@schererf/tutorial-create-a-bot-that-sends-you-an-email-on-received-votes-and-comments-nodejs-steem-js-nodemailer
The Mission
The requirements for the bot:
- Listen the blockchain for new posts of a specified author
- Shorten the posts URL using BITLINK
- Create a new tweet on Twitter for the new post. The text of the Twitter tweet should include the title of the post, the BITLINK URL, the tag "steemit" and all tags of the post. The maximum length of a Twitter tweet is 140 characters. If the combined text is longer than 140 characters, the title of the post is shortened using "...".
The idea for this bot is originally from @pollux.one, many thanks for that!
System Requirements
Visual Studio Code
For developing the bot I used the free Code Editor "Visual Studio Code".
You can download it here: https://code.visualstudio.com/
nodeJS
As the runtime system for the bot I used "nodeJS" (LTS version).
You can download nodeJS here: https://nodejs.org
Write the bot
Preparations
First choose a folder on your filesystem where the source-code file of your new bot should be saved. Then install the required npm packages (nodepackage manager):
- Open the command line console
- Change to the folder of your bot
- Install npm packages
npm install steem --save
npm install bitly
npm install twitter
Create the bot file "steemBitlyTwitterBot.js"
Add a new file "steemBitlyTwitterBot.js" to your folder and open it with Visual Studio Code.
Here is the complete source-code of the bot. Please copy it to the "steemBitlyTwitterBot.js".
/*
** steemBitlyTwitterBot v0.0.1, by @schererf
** Feel free to use and modify.
*/
// INITIALIZE
var config = {
bitly : { access_token: "todo" },
twitter : {
consumer_key: 'todo',
consumer_secret: 'todo',
access_token_key: 'todo',
access_token_secret: 'todo'
}
}
// REQUIREMENTS
// nodeJS: https://nodejs.org
// steem-js: npm install steem --save
// bitly-js: npm install bitly
// twitter-js: npm install twitter
var steem = require('steem');
var Bitly = require('bitly');
var bitly = new Bitly(config.bitly.access_token);
var Twitter = require('twitter');
var twitterClient = new Twitter({
consumer_key: config.twitter.consumer_key,
consumer_secret: config.twitter.consumer_secret,
access_token_key: config.twitter.access_token_key,
access_token_secret: config.twitter.access_token_secret
});
// FUNCTIONS
// helper function to identify if memo has a valid steemit link
// (thanks to npm steem-bot for function 'isValidSteemitLink')
function isValidSteemitLink(link) {
return link.indexOf("steemit.com") > -1;
}
/** (thanks to npm steem-bot for function 'extractUsernameFromLink')
* Should input a full steemit article link and return the username of the author
* @param {string} steemitLink
*/
function extractUsernameFromLink(steemitLink) {
if (isValidSteemitLink(steemitLink)) {
const usernamePos = steemitLink.search(/\/@.+\//);
if (usernamePos === -1) return;
const firstPart = steemitLink.slice(usernamePos + 2); // adding 2 to remove "/@"
return firstPart.slice(0, firstPart.search('/'));
}
}
/** (thanks to npm steem-bot for function 'extractPermlinkFromLink')
* Should input a full steemit article link and return the permlink of the article
* @param {string} steemitLink
*/
function extractPermlinkFromLink(steemitLink) {
if (isValidSteemitLink(steemitLink)) {
const usernamePos = steemitLink.search(/\/@.+\//);
if (usernamePos === -1) return;
const firstPart = steemitLink.slice(usernamePos + 1); // adding 1 to remove the first "/"
return firstPart.slice(firstPart.search('/') + 1).replace('/', '').replace('#', '');
}
}
function twitterUpdateAsync(tweetMessage){
return new Promise(function (resolve, reject) {
try {
twitterClient.post(
'statuses/update',
{ status: tweetMessage },
function(error, tweet, response) {
if (error) { reject(error); }
console.log("> Tweet created for user '" + tweet.user.screen_name + "':\n" + tweet.text);
resolve(response);
});
} catch(ex) {
console.log("> Error occured on creating Tweet:\n" + ex);
reject(ex);
}
});
}
function getTwitterTextAsync(postData, shortUrl){
return new Promise(function (resolve, reject) {
try {
// get list of tags
var tagsText = "#steemit";
var jsonMetadata = JSON.parse(postData.json_metadata);
var tags = jsonMetadata.tags;
for(tagIndex=0; tagIndex<tags.length; tagIndex++){
tagsText += " #"+tags[tagIndex];
}
// get title
var titleText = postData.title;
// build result text
const MAX_TWEET_LENGTH=140;
const TWEET_URL_LENGTH=23;
const NEWLINE = "\n";
const TITLE_SHORTEN_TEXT = "...";
var remainingLength = MAX_TWEET_LENGTH-NEWLINE.length-NEWLINE.length-tagsText.length-TWEET_URL_LENGTH;
var tweetText =
((titleText.length > remainingLength) ? titleText.substring(0,remainingLength-TITLE_SHORTEN_TEXT.length)+TITLE_SHORTEN_TEXT : titleText) +
NEWLINE +
shortUrl +
NEWLINE +
tagsText;
console.log("> Tweet text prepared:\n" + tweetText);
resolve(tweetText);
} catch(ex) {
console.log("> Error occured on preparing Tweet text:\n" + ex);
reject(ex);
}
});
}
function checkBitlyTwitterAndLog(steemLink){
var author = extractUsernameFromLink(steemLink);
var permlink = extractPermlinkFromLink(steemLink);
console.log("> Getting post from steemit and creating a short link using BITLINK...");
Promise.all(
[
steem.api.getContentAsync(author, permlink),
bitly.shorten(steemLink)
]
).then( (resultArray) => {
var postData = resultArray[0];
var shortUrl = resultArray[1].data.url;
return getTwitterTextAsync(postData, shortUrl); }
).then((twitterText) => { return twitterUpdateAsync(twitterText); }
).catch(function (err) {
console.log("> Error: " + err);
});
}
function listenForPosts(pAuthor){
steem.api.streamOperations((err, operations) => {
if (err) {
throw(new Error('Something went wrong with streamOperations method of Steem-js'));
console.log(err);
}
const opType = operations[0];
const op = operations[1];
if (opType === "comment" && op.author === pAuthor && op.parent_author === ""){
// post received
var url = "https://steemit.com/"+op.parent_permlink+"/@"+op.author+"/"+op.permlink;
console.log( "Post received @" + op.author + "/" + op.permlink + "\n" + url);
checkBitlyTwitterAndLog(url);
}
});
}
// MAIN
// check if program was called with correct command line arguments
if (process.argv.length === 3 && isValidSteemitLink(process.argv[2])) {
// create tweet for specified steemit URL
var steemLink = process.argv[2];
checkBitlyTwitterAndLog(steemLink);
} else if (process.argv.length === 3 && process.argv[2][0]==="@") {
// start the bot that listens for new posts of the specified author
var author = process.argv[2].substring(1);
console.log("Listening for posts of @" + author)
listenForPosts(author);
} else {
console.log("usage to create single tweet: process.argv[1] https://steemit.com/test/@author/permlink-post");
console.log("usage to start bot to tweet new posts of a user: process.argv[1] @author");
}
Example Visual Studio Code
Configuration by replacing placeholders
No you have to replace the placeholders in the source-code marked with "todo".
STEEM
For accessing the Steemit post no configuration is needed, because the bot only reads data from the blockchain.
BITLINKS
Placeholder Name | Usage |
---|---|
config.bitly.access_token | The BITLINKS access token |
You can register an OAuth BITLINKS application here: https://bitly.com/a/oauth_apps
Placeholder Name | Usage |
---|---|
config.twitter.consumer_key | The Twitter consumer key |
config.twitter.consumer_secret | The Twitter consumer secret |
config.twitter.access_token_key | The Twitter access tokenkey |
config.twitter.access_token_secret | The Twitter access token secret |
Your Twitter applications are managed under https://apps.twitter.com/.
If not already done, create a new app and copy & paste the required keys from "Key and Access Tokens".
Run the bot
To run the bot using node you have to do the following steps:
- Open the command line console
- Change to the folder where your bot is present
- Start the bot using nodeJS
You can use this nodeJS bot in two different ways:
- As a bot that listens for new posts of the specified user
- To create a Twitter tweet for a single specified Steemit post
Run bot that listens for new Steemit posts
node steemBitlyTwitterBot.js @author
Example:
node steemBitlyTwitterBot.js @steem.test
Result on Twitter:
Run once to create a tweet for a single Steemit post
node steemBitlyTwitterBot.js steemitPostUrl
Example:
node steemBitlyTwitterBot.js https://steemit.com/steemdev/@schererf/tutorial-create-a-bot-that-sends-you-an-email-on-received-votes-and-comments-nodejs-steem-js-nodemailer
Result on Twitter:
Enjoy the new bot.
Please give me feedback if this tutorial was helpful for you.
Thanks for reading and watching,
@schererf
Great work! I forget to post my stuff to Twitter so many times. THX for working it out! Works perfectly for me!
100% and Resteemed!
(thx for mentioning that this was my idea ;-) )
You're welcome and thanks for sharing the idea!
I'm glad that my tutorial is helpful for you...
At last I have found a genius, will be following you like a bee after honey, I actually understand your lesson which is a first for me with bots
Thats great i like it very nice post.
Valuable post you sharing @schererf. I want to more studying this tutorials. So i saved this article for further learning
Upvoted/ Resteemed
@schererf,
This is very useful article for me! Do you have any idea about how to write a bot system for STEEMIT? Thank you very much for sharing such great post with us!
Cheers~
Now that is something really cool :D
thanks for the information, very helpful.
Thanks to you @schererf.
img credz: pixabay.com
Steem is behaving weird in low voting percentages, so actual votes can diverge by up to 1 voting percent point.
Nice, you got a 3.0% @minnowbooster upgoat, thanks to @schererf
Want a boost? Minnowbooster's got your back!
The @OriginalWorks bot has determined this post by @schererf to be original material and upvoted it!
To call @OriginalWorks, simply reply to any post with @originalworks or !originalworks in your message!
To enter this post into the daily RESTEEM contest, upvote this comment! The user with the most upvotes on their @OriginalWorks comment will win!
For more information, Click Here! || Click here to participate in the @OriginalWorks sponsored writing contest(125 SBD in prizes)!!!
Special thanks to @reggaemuffin for being a supporter! Vote him as a witness to help make Steemit a better place!