Bitbar Plugins for Github and ProductHunt Recognition

Few days back, a project named bitbar app was launched, which lets you put the output from any script or program right in your Mac OS X menu bar. The app was trending on many famous sites like HackerNews, ProductHunt, Panda, etc. and is worthy of such recognition. That day onwards, my web browsing behaviour has certainly changed. Being a technocrat and open-source enthusiast, I like browsing awesome trending stuff each day on various popular sites like Github, ProductHunt, Dribble, etc. I use Panda for this. But I have a bad habit of peeping whenever I build something and post it on the above mentioned sites. So, just to prevent opening and viewing stats in window each time, I really appreciate the idea of having the things which require your utmost attention directly into your viewport i.e. Mac OS X menu bar

Needed my own scripts

I couldn’t wait much for the third-party bitbar-plugins to have support for all my greedy needs. And thus, my thirst landed into coding one for myself. Please don’t judge me being obsessed with social media hypocrisy ;)

  1. Stars Count on Github Repositories - It lists all the created Github repositories(not the forked ones) with the stars count just right to it.

  2. Votes Count on ProductHunt Post - It lists all the users posts along with the number of votes till now.

Scripts for both the plugins also show a notification bell in the Mac OS X menu bar itself whenever there’s a change in number of stars on any repo and change in votes count on any producthunt post respectively. Crazy, isn’t it? :B

Screenshots

ProductHunt Posts’ Votes Count Github Repositories’ Stars Count

ProduchHunt Posts’ Votes Count Script

var https = require('https');
var fs = require('fs');
 
// Configurable params
// Either of userId or username should be provided
// Providing userId will save one extra call for getting userId from username since PH API works on userId
var config = {
accessToken: '', // add token here
username: s0ftvar, // change it
userId: 294870, // change it
filename: '/tmp/product-hunt-bitbar-votes-data.txt' // change if /tmp is not a writable folder
};
 

var userId;
var displayInMenuBar = 'PH| color=#da552f dropdown=false';
displayInMenuBar += '\n---\n';
 
function checkIfFileExists() {
fs.exists(config.filename, function (exists) {
if (exists) {
return;
} else {
fs.writeFile(config.filename, {flag: 'wx'}, 0, function (err, data) {
if (err) { throw err; }
});
}
});
}
 
function sortOn(arr, key, orderBy) {
arr.sort(function (a, b) {
return orderBy ? b[key] - a[key] : a[key] - b[key];
});
}
 
function fetchPostVotes() {
// Fetch votes on each posts
var options = {
hostname: 'api.producthunt.com',
path: '/v1/users/' + userId + '/posts',
method: 'GET',
headers: {
'Authorization': 'Bearer ' + config.accessToken
}
};
var output = '';
var req = https.request(options, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var name;
var votes = [];
var response = JSON.parse(body);
var posts = response.posts;
 
// sort response on votes_count to show posts having higher votes on top
sortOn(posts, 'votes_count', true);
 
for (var i = 0; i < posts.length; i++) {
name = posts[i].name;
output += '(' + posts[i].votes_count + ')' + name + ' | length=15 href=' + posts[i].discussion_url;
output += '\n---\n';
votes.push(posts[i].votes_count);
}
 
var oldData;
fs.readFile(config.filename, 'utf8', function (err, data) {
if (err) { throw err; }
oldData = data;
 
// SHow notification bell in OS X menu bar if current and previous data dont match
// Voilla! someone upvoted your post on ProductHunt
if (oldData.toString() !== votes.toString()) {
displayInMenuBar = 'PHπŸ””| color=#da552f dropdown=false';
displayInMenuBar += '\n---\n'
}
 
// Write all the votes count in the file
fs.writeFile(config.filename, votes.toString(), function (err) {
if (err) { throw err; }
});
 
// Finally log the entire output
console.log(displayInMenuBar + output);
});
});
});
req.end();
 
req.on('error', function(e) {
console.error(e);
});
}
 
function getUserId(options) {
var req = https.request(options, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var response = JSON.parse(body);
if (response.error) {
console.log('No such PH User Found');
return;
}
// Update userId
userId = response.user.id;
console.log(userId)
fetchPostVotes();
});
});
 
req.end();
 
req.on('error', function(e) {
console.error(e);
});
}
 
checkIfFileExists();
 
if (config.userId) {
userId = config.userId
fetchPostVotes();
} else if (config.username) {
// Since we need ProductHunt userId for fetching user related data, get userId
// API call options
var options = {
hostname: 'api.producthunt.com',
path: '/v1/users/' + config.username,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + config.accessToken
}
};
getUserId(options);
} else {
console.log('!PH| color=#da552f dropdown=false\n---\n Provide PH userId/username.');
}

JavaScript

View on Github as Gist

Github Repos’ Stars Count Script

// Configurable params
var config = {
accessToken: '', // add token here
username: 'softvar', // change username
hideZeroStarsRepo: false,
filename: '/tmp/github-repo-stars.txt'
};
 
var https = require('https');
var fs = require('fs');
 
var displayInMenuBar = 'Git| color=green dropdown=false';
displayInMenuBar += '\n---\n';
 
function checkIfFileExists() {
fs.exists(config.filename, function (exists) {
if (exists) {
return;
} else {
fs.writeFile(config.filename, {flag: 'wx'}, 0, function (err, data) {
if (err) { throw err; }
});
}
});
}
 
function sortOn(arr, key, orderBy) {
arr.sort(function (a, b) {
return orderBy ? b[key] - a[key] : a[key] - b[key];
});
}
 
function getRepoStars(options) {
var output = '';
var req = https.request(options, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var name;
var stars = [];
var response = JSON.parse(body);
 
// sort on stargazers_count to show repos having more stars on top
sortOn(response, 'stargazers_count', true);
 
for (var i = 0; i < response.length; i++) {
// show only `your` repos, not the forked ones
if (response[i].fork) { continue; }
 
if (response[i].stargazers_count === 0 && config.hideZeroStarsRepo) { continue; }
 
name = response[i].name;
output += '(' + response[i].stargazers_count + ')' + name + ' | length=15 href=' + response[i].html_url;
output += '\n---\n';
stars.push(response[i].stargazers_count)
};
 
var oldData;
fs.readFile(config.filename, 'utf8', function (err, data) {
if (err) { throw err; }
oldData = data;
 
// SHow notification bell in OS X menu bar if current and revious data doesnt match
// Voilla, someone starred your repo on Github
if (oldData.toString() !== stars.toString()) {
displayInMenuBar = 'GitπŸ””| color=green dropdown=false';
displayInMenuBar += '\n---\n';
}
 
// Write all the stars count in the file
fs.writeFile(config.filename, stars.toString(), {flag: 'w'}, function (err) {
if (err) { throw err; }
});
 
// Finally log the entire output
console.log(displayInMenuBar + output);
});
});
});
req.end();
 
req.on('error', function(e) {
console.error(e);
});
}
 
checkIfFileExists();
 
// API call options
var options = {
hostname: 'api.github.com',
path: '/users/' + config.username + '/repos',
method: 'GET',
headers: {
'Authorization': 'token ' + config.accessToken,
'User-Agent': 'Awesome-Octocat-App'
}
};
 
getRepoStars(options);

JavaScript

View on Github as Gist


comments powered by Disqus