Created
May 17, 2012 04:16
-
-
Save theVDude/2716297 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: { | |
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.'); | |
} else { | |
unlove(luv.lastfm, luv.skey, act); | |
} | |
}); | |
}; | |
function love(lfm, skey, act) { | |
var trckreq = lastfm.request('user.getRecentTracks', { | |
user: lfm, | |
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.'); | |
} else { | |
love(luv.lastfm, luv.skey, act); | |
} | |
}); | |
}; | |
function np(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'], | |
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); | |
} | |
} | |
}); | |
} | |
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); | |
} else { | |
np(user.lastfm, act.nick, act); | |
} | |
}); | |
} else { | |
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]) > -1) { | |
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); | |
} | |
} | |
}); | |
} else { | |
irc.privmsg(act.source, 'Valid periods are overall, 7day, 3month, 6month, or 12month.'); | |
} | |
} else { | |
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); | |
} | |
}); | |
} else 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'); | |
} else { | |
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 cp(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); | |
} | |
} | |
}); | |
} | |
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.'); | |
} else if (act.params[0] === 'help') { | |
irc.privmsg(act.source, 'cp <user1> [user2]: Compare user1 and user2. If no user2 given, defaults to self.'); | |
} else 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: act.params[0].toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[0], act.params[0], act.nick, act.nick, act); | |
} else { | |
cp(user2.lastfm, act.params[0], act.nick, act.nick, act); | |
} | |
}); | |
} else { | |
UserModel.findOne({ nick: act.params[0].toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[0], act.params[0], user.lastfm, act.nick, act); | |
} else { | |
cp(user2.lastfm, act.params[0], user.lastfm, act.nick, act); | |
} | |
}); | |
} | |
}); | |
} else { | |
UserModel.findOne({ nick: act.params[0].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], act.params[0], act.params[0], act); | |
} else { | |
cp(user2.lastfm, act.params[1], act.params[0], act.params[0], act); | |
} | |
}); | |
} else { | |
UserModel.findOne({ nick: act.params[1].toLowerCase() }, function (err, user2) { | |
if (!user2) { | |
cp(act.params[1], act.params[1], user.lastfm, act.params[0], act); | |
} else { | |
cp(user2.lastfm, act.params[1], user.lastfm, act.params[0], 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'); | |
} else 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'); | |
} else { | |
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.'); | |
} else { | |
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); | |
} | |
}); | |
} else { | |
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