Permalink
Browse files

Added new files

  • Loading branch information...
1 parent bfac020 commit f7c0a73a6d36753187b793053535cf4cc0034fda @peol committed Feb 25, 2012
View
@@ -0,0 +1,2 @@
+config.js
+.DS_Store
View
@@ -0,0 +1,35 @@
+socialhapy, the social developer irc bot
+========================================
+This IRC bot features a few useful modules and features that can help
+developers in discussions, references etc. It'll pick up on URLs to
+github commits, look up tweets and/or stream tweets to specific channels.
+It also sports a spotify module that automatically picks up on spotify
+URIs and lets everyone know what artist, track or album that was linked.
+
+How to get started
+------------------
+This project expects 0.4.0 of node.js or newer, but it'll probably work
+work with lower versions if you fix the `node_modules` reference.
+
+1. Go into your Terminal application and do
+ `git clone git@github.com:peol/socialhapy.git`
+2. `cd socialhapy` and then initialize the required submodules needed by
+ running `git submodule init` and `git submodule update --recursive`
+3. Edit the `socialhapy/config.js` file to your liking
+4. Run socialhapy by either running it with `node socialhapy.js` or by
+ the recommended way, with `node-forever` or `nohup sh socialhapy.sh &`
+
+Create your own socialhapy module
+---------------------------------
+socialhapy has its own module system that leverages the `require` system but
+demands that an API is returned with at least a `register` method in it, it's
+also required that you add the module in `config.js`, see other entries under
+the `modules` object.
+
+For a more in=depth guidance, see `examples/simple-module.js`.
+
+Roadmap
+-------
+There's not much but refactoring on the roadmap right now, but I'm happy to
+merge in any optimizations/useful socialhapy modules that I see fit, just
+send a pull request.
View
@@ -0,0 +1,168 @@
+var config = require('./config'),
+ functions;
+
+module.exports = functions = {
+ // Simple string format tool, {0}, {1} etc.
+ // From http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format/4673436#4673436
+ format: function() {
+ var args = Array.prototype.slice.call( arguments ),
+ str = args.shift(),
+ usePrefix = str === true;
+
+ if ( usePrefix ) {
+ str = config.prefix + args.shift();
+ }
+
+ return str.replace(/{(\d+)}/g, function(match, number) {
+ return typeof args[ number ] != 'undefined' ?
+ args[ number ] :
+ '{' + number + '}';
+ });
+ },
+
+ // Parse weeks, days, hours, minutes and seconds from seconds
+ _duration: function(seconds, opts) {
+ opts = opts || {
+ asArray: false,
+ filterValues: false,
+ compress: false
+ };
+
+ function subtract(div) {
+ var v = Math.floor( seconds / div );
+ seconds %= div;
+
+ return v;
+ }
+
+ var val = [
+ [subtract( 31536000 ), ' year'],
+ [subtract( 2628000 ), ' month'],
+ [subtract( 604800 ), ' week'],
+ [subtract( 86400 ), ' day'],
+ [subtract( 3600 ), ' hour'],
+ [subtract( 60 ), ' minute'],
+ [seconds, ' second']
+ ],
+ i = 0,
+ l = val.length,
+ v;
+
+ for (; i < l; i += 1) {
+ v = val [ i ];
+
+ if ( opts.compress ) {
+ // Take first char after space
+ v[ 1 ] = v[ 1 ][ 1 ];
+ }
+ else {
+ if ( val[ i ][ 0 ] !== 1 ) {
+ // Pluralize
+ val[ i ][ 1 ] += 's';
+ }
+ }
+ }
+
+ if ( opts.filterValues ) {
+ val = val.filter(function(i) {
+ return i[ 0 ] > 0;
+ });
+ }
+
+ if ( !opts.asArray ) {
+ val = val
+ .map(function(i){return i.join('');})
+ .join(', ');
+ }
+
+ return opts.compress && !opts.asArray ?
+ val.replace(/,/g,''):
+ val;
+ },
+
+ duration: function(secs, useSuffix, doCompress) {
+ // Round the value if it's more than a minute, otherwise, round to three decimals
+ secs = secs > 59 ? Math.round( secs ) : Math.round(secs * 1000) / 1000;
+
+ var dur = this._duration(secs, { filterValues: true, compress: doCompress, asArray: true }),
+ i = 0,
+ // Adjust this, by default we just want 2, e.g. "2 years, 3 months"
+ l = Math.min(dur.length, 2),
+ arr = [],
+ str = '';
+
+
+ for (; i < l; i += 1) {
+ arr.push( dur[ i ].join('') );
+ }
+
+ str = arr.join(', ');
+
+ return useSuffix ? str + ' ago' : str;
+ },
+
+ // Extend an object with another object, just a shallow extend
+ extend: function(extendee, extender) {
+ var o;
+
+ for (o in extender) {
+ extendee[ o ] = extender [ o ];
+ }
+ },
+
+ // Normalize a string for IRC output (remove unnecessary white space, new
+ // lines etc.)
+ normalize: function(str) {
+ return str.replace(/\r\n|\n/g, '');
+ },
+
+ // Helper for checking if `v1` has a value, and if the value is identical
+ // to `v2`, defaults to `true`
+ isMatchOrEmpty: function(v1, v2) {
+ return typeof v1 != 'undefined' ?
+ v1 === v2:
+ true;
+ },
+
+ // Checks whether a specific user is an admin or not
+ isAdmin: function(user) {
+ var adminUser = config.admins[ user.nick ],
+ userMatch = adminUser && this.isMatchOrEmpty( adminUser.user, user.user ),
+ hostMatch = adminUser && this.isMatchOrEmpty( adminUser.host, user.host );
+
+ return userMatch && hostMatch;
+ },
+
+ // Filters an array and returns a new array with unique values
+ unique: function(arr) {
+ var frequency = {},
+ uniques = [];
+
+ arr.forEach(function(v) { frequency[ v ] = 0; });
+
+ uniques = arr.filter(function(val) {
+ return ++frequency[ val ] == 1;
+ });
+
+ return uniques;
+ },
+
+ // Add a watcher to a specific jerk instance, it also checks if the watcher
+ // is a admin-only command and validates the user
+ addWatcher: function(jerk, w) {
+ jerk.watch_for(w.pattern, function(m) {
+ var that = this,
+ isAdmin = functions.isAdmin( m.userData );
+
+ if ( w.adminOnly && !isAdmin ) {
+ m.say(m.user + ': Sorry, admins only');
+ }
+ else {
+ w.hollaback.call(that, m);
+ }
+ });
+ }
+};
+ });
+ }
+};
View
@@ -0,0 +1,57 @@
+var functions = require('../functions'),
+ Bitly = require('node-bitly').Bitly,
+ urlp = require('url'),
+ hollabacks,
+ bitly;
+
+hollabacks = {
+ bitly: function(m) {
+ var url = m.match_data[ 1 ];
+
+ // Add http:// if it's not there
+ if ( !/^http?(s)?:\/\//.test( url ) ) {
+ url = 'http://' + url;
+ }
+
+ bitly.createLink(url, function(bitlyURL) {
+ if ( bitlyURL ) {
+ m.say( functions.format(true, 'URL has been shortened: {0}', bitlyURL) );
+ }
+ });
+ }
+};
+
+bitly = module.exports = {
+ _isLoaded: false,
+ _sh: null,
+ _bitly: null,
+
+ register: function(socialhapy) {
+ this._isLoaded = true;
+ this._sh = socialhapy;
+
+ var credentials = socialhapy.config.modules.bitly;
+
+ this._bitly = new Bitly(credentials.user, credentials.token);
+
+ functions.extend(socialhapy.watchers, this.watchers);
+ },
+
+ createLink: function(url, hollaback) {
+ this._bitly.shorten(url, function(result) {
+ if ( !result.data.url ) {
+ console.log('*** Warning: bit.ly module encountered an error while shortening a link: ' + result.status_txt);
+ }
+
+ hollaback( result.data.url || '' );
+ });
+ },
+
+ watchers: {
+ // .bitly <url>
+ bitly: {
+ pattern: /^\.bitly (.+?)$/i,
+ hollaback: hollabacks.bitly
+ }
+ }
+};
View
@@ -0,0 +1,82 @@
+var functions = require('../functions'),
+ startDate = new Date(),
+ watchers,
+ core;
+
+watchers = {
+ // .restart
+ restart: {
+ adminOnly: true,
+ pattern: /^\.restart$/i,
+ hollaback: function(m) {
+ m.say( functions.format(true, 'Restarting... I was up for {0}', functions.duration( ( +new Date() - startDate ) / 1000 ) ) );
+ // Note: This requires node-forever (forever-node?) or some sort
+ // of auto-startup script
+ // TODO: Send disconnect to the IRC server somehow, we'd need the jerk instance
+ // for that...
+ setTimeout(function() {
+ core._sh.jerk.quit("I'm just restarting!");
+ }, 50);
+
+ setTimeout(function() {
+ process.exit();
+ }, 100);
+ }
+ },
+
+ // Add an idle channel temporary (handy when you want it to idle in a new
+ // channel, but don't want to restart the bot - YOU DO NEED TO ADD THAT CHANNEL
+ // MANUALLY IN THE CONFIG)
+ join: {
+ adminOnly: true,
+ pattern: /^\.join (\#*)?(.+)$/i,
+ hollaback: function(m) {
+ var channel = '#' + m.match_data[ 2 ];
+
+ core._sh.jerk.join( channel );
+
+ m.say( functions.format(true, 'I (temporarily) joined {0}', channel) );
+ }
+ },
+
+ // Same as above, but leaves a channel
+ part: {
+ adminOnly: true,
+ pattern: /^\.part (\#*)?(.+)$/i,
+ hollaback: function(m) {
+ var channel = '#' + m.match_data[ 2 ];
+
+ core._sh.jerk.part( channel );
+
+ m.say( functions.format(true, 'I (temporarily) left {0}', channel) );
+ }
+ },
+
+ // .uptime
+ uptime: {
+ pattern: /^\.uptime$/i,
+ hollaback: function(m) {
+ m.say( functions.format(true, 'I have been up for {0}', functions.duration( ( +new Date() - startDate ) / 1000 ) ) );
+ }
+ },
+
+ // .duration
+ duration: {
+ pattern: /^\.duration (\d+)$/i,
+ hollaback: function(m) {
+ m.say( functions.format(true, '{0}', functions.duration( parseFloat( m.match_data[ 1 ] ) )) );
+ }
+ }
+};
+
+core = module.exports = {
+ register: function(socialhapy) {
+ functions.extend(this, {
+ _isloaded: true,
+ _sh: socialhapy
+ });
+
+ functions.extend(socialhapy.watchers, this.watchers);
+ },
+ watchers: watchers
+};
Oops, something went wrong.

0 comments on commit f7c0a73

Please sign in to comment.