-
-
Save CalvinRodo/2718664 to your computer and use it in GitHub Desktop.
Last.fm plugin for ircnode bot using mongodb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A last.fm nowplaying plugin. | |
// Author: thevdude ([email protected]) | |
// | |
// Uses installed node modules mongoose, lastfm, inflection, and bitly. | |
// | |
// Current functions include: | |
// Display user's currently or last played track (np) | |
// Compare lastfm users with the tasteometer (cp) | |
// Get a users lastfm url (url) | |
// Find a user's top ten artists for different time periods (topten) | |
// Register a lastfm name with the bot (reg) | |
// Register an authtenticated sesskey with the bot (lovereg) | |
// Love and Unlove lastfm tracks (love, unlove) | |
// this is an ircbot, so we'll want this stuff | |
var irc = global.irc; | |
// mongoose is a node module for easier interaction with mongodb, which works | |
// well for this plugin and most things I'd imagine an irc bot would be doing. | |
// This sets up the model that we'll be interacting with. | |
var mongoose = require('mongoose'), | |
db = mongoose.connect('mongodb://localhost/lastfm'), | |
Schema = mongoose.Schema, | |
User = new Schema({ | |
nick: String, | |
hmask: String, | |
lastfm: String, | |
skey: String | |
}); | |
var UserModel = mongoose.model('User', User); | |
// lastfm module simplifies the connection and API calls, and for all authenti- | |
// cation it generates the api_sig that lastfm looks for. | |
var LastFmNode = require('lastfm').LastFmNode; | |
var lastfm = new LastFmNode({ | |
api_key: irc.config.lfmapi, | |
secret: irc.config.lfmsecret, | |
useragent: irc.config.lfmuseragent, | |
}); | |
// inflection is used for ordinalization of the number of plays. Not mandatory, | |
// but it is nice to say "played SONG for the {1st, 2nd, 5th, 23rd} time" | |
var inflection = require('inflection'); | |
// bitly is a url shortening service. | |
var Bitly = require('bitly'); | |
var bitly = new Bitly(irc.config.bitlyuser, irc.config.bitlyapi); | |
// crypto is used for md5 hashing to create the authToken for lovereg | |
var crypto = require('crypto'); | |
function unlove(lfm, skey, act) { | |
var trckreq = lastfm.request('user.getRecentTracks', { | |
user: lfm, | |
handlers: { | |
//decalre this function elsewhere and pass it to this. | |
success: function (trck) { | |
var track = trck.recenttracks.track[0].name; | |
var artist = trck.recenttracks.track[0].artist['#text']; | |
var unloveit = lastfm.request('track.unlove', { | |
track: track, | |
artist: artist, | |
sk: skey, | |
handlers: { | |
success: function (a) { | |
irc.privmsg(act.nick, 'unloved "' + track + '" by ' + artist); | |
}, | |
error: function (e) { | |
irc.privmsg(act.nick, 'ERROR: ' + e.message); | |
} | |
} | |
}); | |
}, | |
error: function (e) { | |
console.log(e); | |
} | |
} | |
}); | |
} | |
var unlove_handler = function (act) { | |
UserModel.findOne({$or: [{ hmask: act.host }, { nick: act.nick.toLowerCase}] }, function (err, luv) { | |
if (!luv) { | |
irc.privmsg(act.source, 'Not registered for love/unlove, .lovereg [lastfmuser] [token], .token for more help with token.'); | |
return; | |
} | |
unlove(luv.lastfm, luv.skey, act); | |
}); | |
}; | |
function love(lfm, skey, act) { | |
var trckreq = lastfm.request('user.getRecentTracks', { | |
user: lfm, | |
//declare this function elsewhere | |
handlers: { | |
success: function (trck) { | |
var track = trck.recenttracks.track[0].name; | |
var artist = trck.recenttracks.track[0].artist['#text']; | |
var loveit = lastfm.request('track.love', { | |
track: track, | |
artist: artist, | |
sk: skey, | |
handlers: { | |
success: function (a) { | |
irc.privmsg(act.nick, 'loved "' + track + '" by ' + artist); | |
}, | |
error: function (e) { | |
irc.privmsg(act.nick, 'ERROR: ' + e.message); | |
} | |
} | |
}); | |
}, | |
error: function (e) { | |
console.log(e); | |
} | |
} | |
}); | |
} | |
var love_handler = function (act) { | |
UserModel.findOne({$or: [{ hmask: act.host }, { nick: act.nick.toLowerCase}] }, function (err, luv) { | |
if (!luv) { | |
irc.privmsg(act.source, 'Not registered for love/unlove, .lovereg [lastfmuser] [token], .token for more help with token.'); | |
return; | |
} | |
love(luv.lastfm, luv.skey, act); | |
}); | |
}; | |
function NowPlaying(lfm, nck, act) { | |
var lfmuser = lfm; | |
var trckreq = lastfm.request("user.getRecentTracks", { | |
user: lfmuser, | |
handlers: { | |
success: function (npdata) { | |
var trck = npdata.recenttracks.track[0]; | |
var trckinforeq = lastfm.request("track.getInfo", { | |
username: lfmuser, | |
track: trck.name, | |
artist: trck.artist['#text'], | |
//This handler is way too long, break it up into smaller functions that describe what it is they are doing. | |
//also move it to it's own function don't declare it here. | |
handlers: { | |
success: function (moredata) { | |
var trckinfo = moredata.track; | |
var msg = ''; | |
// Checks if track is Now Playing | |
if ('@attr' in trck) { | |
if ('nowplaying' in trck['@attr'] && trck['@attr'].nowplaying === 'true') { | |
msg = nck + ' is now playing'; | |
} | |
} | |
//Otherwise, last played | |
else { | |
msg = nck + ' last played'; | |
} | |
//Check if it's a loved track | |
if (trckinfo.userloved === '1') { | |
msg = msg + ' a loved track,'; | |
} | |
msg = msg + ' "' + trck.name + '" by ' + trck.artist['#text']; | |
//If album name is there, add that in too | |
if (trck.album['#text'] !== '') { | |
msg = msg + ' -- from the album "' + trck.album['#text'] + '" --'; | |
} | |
var usrcnt = trckinfo.userplaycount; | |
if (usrcnt !== undefined) { | |
usrcnt = parseInt(usrcnt, 10) + 1; | |
} else { | |
usrcnt = 1; | |
} | |
msg = msg + ' for the ' + inflection.ordinalize(usrcnt.toString()) + ' time.'; | |
//Have the bot say it. | |
if (act.source.indexOf('#') === -1) { | |
irc.privmsg(act.params[0], msg); | |
} else { | |
irc.privmsg(act.source, msg); | |
} | |
msg = ''; | |
//Get `listener`s and `playcount`, calculate `ratio`, and add it to `msg` | |
var listeners = parseInt(trckinfo.listeners, 10); | |
var playcount = parseInt(trckinfo.playcount, 10); | |
var ratio = playcount / listeners; | |
msg = playcount + ' plays by ' + listeners + ' listeners (' + ratio.toFixed(2) + ':1) ::'; | |
//If there are no tags, add that to `msg` | |
if (trckinfo.toptags === '\n ') { | |
msg = msg + ' No Tags.'; | |
} | |
//If there ARE tags, add the first 4 to `tags[]` and join them with ', ' and add them to `msg` | |
else { | |
var tags = []; | |
msg = msg + ' Top Tags - '; | |
for (var e in trckinfo.toptags.tag) { | |
if (e < 4) { | |
tags[e] = trckinfo.toptags.tag[e].name; | |
} | |
} | |
if (tags.length > 1) { | |
msg = msg + tags.join(', '); | |
} else { | |
msg = msg + trckinfo.toptags.tag.name; | |
} | |
} | |
if (trck.streamable === '1') { | |
bitly.shorten(trck.url, function (err, response) { | |
if (err) throw err; | |
msg = msg + ' :: Stream it at ' + response.data.url; | |
if (act.source.indexOf('#') === -1) { | |
irc.privmsg(act.params[0], msg); | |
} else { | |
irc.privmsg(act.source, msg); | |
} | |
}); | |
} else { | |
if (act.source.indexOf('#') === -1) { | |
irc.privmsg(act.params[0], msg); | |
} else { | |
irc.privmsg(act.source, msg); | |
} | |
} | |
}, | |
error: function (error) { | |
console.log(error); | |
} | |
} | |
}); | |
}, | |
error: function (error) { | |
console.log(error); | |
} | |
} | |
}); | |
} | |
//Alias for the NowPlaying function | |
function np(lfm, nck, act){ | |
NowPlaying(lfm, nck, act) | |
} | |
var np_handler = function (act) { | |
if (act.params.length === 0 || act.source.indexOf('#') === -1) { | |
UserModel.findOne({$or: [{ nick: act.nick.toLowerCase() }, { hmask: act.host }] }, function (err, user) { | |
if (!user) { | |
np(act.nick, act.nick, act); | |
return; | |
} | |
np(user.lastfm, act.nick, act); | |
}); | |
return; | |
} | |
UserModel.findOne({ nick: act.params[0].toLowerCase() }, function (err, user) { | |
if (!user) { | |
np(act.params[0], act.params[0], act); | |
} else { | |
np(user.lastfm, act.params[0], act); | |
} | |
}); | |
}; | |
function topten(lfm, nck, act) { | |
var lfmuser = lfm; | |
var periods = ['overall', '7day', '3month', '6month', '12month']; | |
if (act.params.length === 2) { | |
if (periods.indexOf(act.params[1]) < 0) { | |
irc.privmsg(act.source, 'Valid periods are overall, 7day, 3month, 6month, or 12month.'); | |
return; | |
} | |
var inforeq = lastfm.request("user.getTopArtists", { | |
user: lfmuser, | |
period: act.params[1], | |
handlers: { | |
success: function (usrinfo) { | |
var toparts = []; | |
for (var i in usrinfo.topartists.artist) { | |
if (i < 10) { | |
toparts[i] = usrinfo.topartists.artist[i].name + '(' + usrinfo.topartists.artist[i].playcount + ')'; | |
} | |
} | |
irc.privmsg(act.source, nck + '\'s top artists: ' + toparts.join(', ')); | |
}, | |
error: function (err) { | |
console.log(err); | |
} | |
} | |
}); | |
return; | |
} | |
var inforeq = lastfm.request("user.getTopArtists", { | |
user: lfmuser, | |
period: act.params[1], | |
handlers: { | |
success: function (usrinfo) { | |
var toparts = []; | |
for (var i in usrinfo.topartists.artist) { | |
if (i < 10) { | |
toparts[i] = usrinfo.topartists.artist[i].name + '(' + usrinfo.topartists.artist[i].playcount + ')'; | |
} | |
} | |
irc.privmsg(act.source, nck + '\'s top artists: ' + toparts.join(', ')); | |
}, | |
error: function (err) { | |
console.log(err); | |
} | |
} | |
}); | |
} | |
var topten_handler = function (act) { | |
if (act.params.length === 0) { | |
UserModel.findOne({$or: [{ nick: act.nick.toLowerCase() }, { hmask: act.host }] }, function (err, user) { | |
if (!user) { | |
topten(act.nick, act.nick, act); | |
} else { | |
topten(user.lastfm, act.nick, act); | |
} | |
}); | |
return; | |
} | |
if (act.params[0] === 'help') { | |
irc.privmsg(act.source, 'topten <user> <period>: Shows the top ten artists and playcounts for a user for a given time period. If left blank, user is self and time period is overall. :: Valid time periods - overall | 7day | 3month | 6month | 12month'); | |
return; | |
} | |
UserModel.findOne({ nick: act.params[0].toLowerCase() }, function (err, user) { | |
if (!user) { | |
topten(act.params[0], act.params[0], act); | |
} else { | |
topten(user.lastfm, act.params[0], act); | |
} | |
}); | |
}; | |
function TasteoMeterCompare(lfm2, nck2, lfm1, nck1, act) { | |
var cpreq = lastfm.request('tasteometer.compare', { | |
type1: 'user', | |
type2: 'user', | |
value1: lfm1, | |
value2: lfm2, | |
handlers: { | |
success: function (cpinfo) { | |
var matches = ''; | |
var artists = []; | |
var percent = Math.round(cpinfo.comparison.result.score * 100); | |
var bar = ''; | |
console.log(cpinfo.comparison.result.artists); | |
for (var e in cpinfo.comparison.result.artists.artist) { | |
artists[e] = cpinfo.comparison.result.artists.artist[e].name; | |
} | |
for (var i = 0; i < Math.round(percent / 10); i += 1) { | |
bar = bar + '|'; | |
} | |
while (bar.length < 10) { | |
bar = bar + '.'; | |
} | |
if (!cpinfo.comparison.result.artists['@attr']) { | |
matches = 'No matches!'; | |
} else if (cpinfo.comparison.result.artists['@attr'].matches === '10') { | |
matches = '10 or more matching artists, including ' + artists.join(', '); | |
} else if (cpinfo.comparison.result.artists['@attr'].matches > '1') { | |
matches = cpinfo.comparison.result.artists['@attr'].matches + ' matching artists, including ' + artists.join(', '); | |
} else { | |
matches = cpinfo.comparison.result.artists['@attr'].matches + ' matching artist, ' + cpinfo.comparison.result.artists.artist.name; | |
} | |
irc.privmsg(act.source, nck1 + ' and ' + nck2 + ' [' + bar + '] ' + percent + '% :: ' + matches); | |
}, | |
error: function (err) { | |
console.log(err); | |
} | |
} | |
}); | |
} | |
//Alias for the TasteometerCompare function | |
function cp(lfm2, nck2, lfm1, nck1, act){ | |
TasteoMeterCompare(lfm2, nck2, lfm1, nck1, act) | |
} | |
var cp_handler = function (act) { | |
if (act.params.length === 0) { | |
irc.privmsg(act.source, 'cp <user1> [user2]: Compare user1 and user2. If no user2 given, defaults to self.'); | |
return; | |
} | |
var action = act.params[0] | |
if (action === 'help') { | |
irc.privmsg(act.source, 'cp <user1> [user2]: Compare user1 and user2. If no user2 given, defaults to self.'); | |
return | |
} | |
if (act.params.length === 1 || act.params[1] === '') { | |
UserModel.findOne({$or: [{ nick: act.nick.toLowerCase() }, {hmask: act.host }] }, function (err, user) { | |
if (!user) { | |
UserModel.findOne({ nick: action.toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[0], action, act.nick, act.nick, act); | |
} else { | |
cp(user2.lastfm, action, act.nick, act.nick, act); | |
} | |
}); | |
return; | |
} | |
UserModel.findOne({ nick: action.toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(action, action, user.lastfm, act.nick, act); | |
} else { | |
cp(user2.lastfm, action, user.lastfm, act.nick, act); | |
} | |
}); | |
}); | |
return; | |
} | |
UserModel.findOne({ nick: action.toLowerCase() }, function (err, user) { | |
if (!user) { | |
UserModel.findOne({ nick: act.params[1].toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[1], act.params[1], action, action, act); | |
} else { | |
cp(user2.lastfm, act.params[1], action, action, act); | |
} | |
}); | |
return; | |
} | |
UserModel.findOne({ nick: act.params[1].toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[1], act.params[1], user.lastfm, action, act); | |
} else { | |
cp(user2.lastfm, act.params[1], user.lastfm, action, act); | |
} | |
}); | |
}); | |
}; | |
var reg_handler = function (act) { | |
if (act.params.length === 2) { | |
UserModel.update({ nick: act.params[0].toLowerCase() }, { lastfm: act.params[1] }, { upsert: true}, function () {}); | |
irc.privmsg(act.source, 'I have remembered ' + act.params[0] + ' by nick to be "' + act.params[1] + '" on last.fm'); | |
return; | |
} | |
if (act.params.length === 1) { | |
UserModel.update({ nick: act.nick.toLowerCase() }, { nick: act.nick.toLowerCase(), hmask: act.host, lastfm: act.params[0] }, { upsert: true }, function () {}); | |
irc.privmsg(act.source, 'I have remembered you by nick and host to be "' + act.params[0] + '" on last.fm'); | |
return | |
} | |
irc.privmsg(act.source, ".reg LASTFMUSER to register with me!"); | |
}; | |
var lovereg_handler = function (act) { | |
if (act.params.length < 2) { | |
irc.privmsg(act.source, '.lovereg [lastfmuser] [md5password] to register for love/unlove. This command should be given in private.'); | |
return; | |
} | |
var tkn = crypto.createHash('md5').update(act.params[0] + act.params[1]).digest('hex'); | |
// comment previous line and uncomment these two for plaintext password | |
//var md5 = crypto.createHash('md5').update(act.params[1]).digest('hex'); | |
//var tkn = crypto.createHash('md5').update(act.params[0]+md5).digest('hex'); | |
var authreq = lastfm.request('auth.getmobilesession', { | |
username: act.params[0], | |
authToken: tkn, | |
handlers: { | |
success: function (sesskey) { | |
UserModel.update({ nick: act.nick.toLowerCase() }, { nick: act.nick.toLowerCase(), hmask: act.host, lastfm: act.params[0], skey: sesskey.session.key }, { upsert: true }, function (err) { console.log(err); }); | |
irc.privmsg(act.source, 'I\'ve got you down and you\'re ready to .love and .unlove tracks, ' + act.nick); | |
} | |
} | |
}); | |
}; | |
var url_handler = function (act) { | |
if (act.params.length === 0) { | |
UserModel.findOne({$or: [{ nick: act.nick.toLowerCase() }, { hmask: act.host }] }, function (err, user) { | |
if (!user) { | |
irc.privmsg(act.source, 'URL for ' + act.nick + ': http://last.fm/user/' + act.nick); | |
} else { | |
irc.privmsg(act.source, 'URL for ' + act.nick + ': http://last.fm/user/' + user.lastfm); | |
} | |
}); | |
return; | |
} | |
UserModel.findOne({ nick: act.params[0].toLowerCase() }, function (err, user) { | |
if (!user) { | |
irc.privmsg(act.source, 'URL for ' + act.params[0] + ': http://last.fm/user/' + act.params[0]); | |
} else { | |
irc.privmsg(act.source, 'URL for ' + act.params[0] + ': http://last.fm/user/' + user.lastfm); | |
} | |
}); | |
}; | |
exports.name = ['np', 'topten', 'cp', 'reg', 'url', 'lovereg', 'love', 'unlove']; | |
exports.handler = [np_handler, topten_handler, cp_handler, reg_handler, url_handler, lovereg_handler, love_handler, unlove_handler]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment