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 requirements for the bot:
The idea for this bot is originally from @pollux.one, many thanks for that!
For developing the bot I used the free Code Editor "Visual Studio Code".
You can download it here: https://code.visualstudio.com/
As the runtime system for the bot I used "nodeJS" (LTS version).
You can download nodeJS here: https://nodejs.org
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):
npm install steem --save
npm install bitly
npm install twitter
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");
}
No you have to replace the placeholders in the source-code marked with "todo".
For accessing the Steemit post no configuration is needed, because the bot only reads data from the blockchain.
| 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".
To run the bot using node you have to do the following steps:
You can use this nodeJS bot in two different ways:
node steemBitlyTwitterBot.js @author
Example:
node steemBitlyTwitterBot.js @steem.test
Result on Twitter:
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