Skip to content

Instantly share code, notes, and snippets.

@pikpik
Last active December 19, 2015 18:09
Show Gist options
  • Save pikpik/5996849 to your computer and use it in GitHub Desktop.
Save pikpik/5996849 to your computer and use it in GitHub Desktop.
An IRC bot written in JavaScript for Node.js. (MIT License)
#!/usr/local/bin/node
/*
Copyright (c) 2013 pikpik
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var myName = "pikpik-botbot";
var logFile = "logs/" + "log-" + Date.now () + ".txt";
function start () {
log ( "Hello!" );
var freenode = new Server ( myName, "irc.freenode.net", 6667 );
freenode.onSignIn = function () {
channelHelper ( this, "#minix" );
};
freenode.onFinish = function () {
log ( "Goodbye!" );
};
freenode.connect ();
}
var net = require ( "net" );
var fs = require ( "fs" );
var os = require ( "os" );
var patterns = {};
patterns.host = /[a-zA-Z0-9.-]+/;
patterns.nickname = /[a-zA-Z0-9.-]+/;
patterns.channel = /#[a-zA-Z0-9.-]+/;
patterns.signIn = /^:[^:\s]+ NOTICE \* :\*\*\* No Ident response/;
patterns.signedIn = /^:([^:\s]+) MODE [^:]+:.+?/;
patterns.ping = /^PING :(\S+)$/;
patterns.userAddress = /[a-zA-Z0-9\.\-]+@[]+/;
patterns.joined = /^:([^!]+)!.+? JOIN (#.+)$/;
patterns.userMessage = /^:([^!]+)!.+? PRIVMSG (#\S+) :(.+)$/;
// Clean up
fs.unlink (
logFile,
function ( e ) {
start ();
}
);
function channelHelper ( server, name ) {
var channel = new Channel ( server, name );
channel.onJoin = function () {
this.send (
CapitalizeSentence (
randomGreeting () + randomEnding ()
)
);
};
channel.onMessage = function ( message ) {
greet ( this, message );
};
channel.join ();
}
function greet ( channel, message ) {
greetVisitor ( channel, message );
respondToGreeting ( channel, message );
respondToGreetingMe ( channel, message );
}
function greetVisitor ( channel, message ) {
var userJoined = message.match ( patterns.joined );
if ( ! userJoined ) return;
var visitor = userJoined [ 1 ];
if ( visitor == myName ) return;
channel.send ( makeGreeting ( visitor ) );
}
function respondToGreeting ( channel, message ) {
var fromAUser = message.match ( patterns.userMessage );
if ( ! ( fromAUser ) ) return;
var sender = fromAUser [ 1 ],
theMessage = fromAUser [ 3 ];
var words = theMessage.match ( /(\w\w+)/g );
if ( ! (
( words && words.length == 1 )
&& isGreeting ( theMessage )
) ) return;
channel.send (
CapitalizeSentence (
randomGreeting () + randomEnding ()
)
);
}
function respondToGreetingMe ( channel, message ) {
var fromAUser = message.match ( patterns.userMessage );
var namePattern = new RegExp ( "\\b" + myName + "\\b" );
var userGreetedMe = message.match ( namePattern );
if ( ! ( fromAUser && userGreetedMe ) ) return;
var sender = fromAUser [ 1 ],
theirMessage = fromAUser [ 3 ];
if ( ! isGreeting ( theirMessage ) ) return;
channel.send ( makeGreeting ( sender ) );
}
function e ( o ) {
var l = [];
for ( var p in o )
l.push ( p + ": " + o [ p ] );
log ( l.sort ().join ( "\n\n" ) );
}
function hour () {
return ( new Date () ).getHours ();
}
var greetings = [
[ "hi", function () { return true } ],
[ "greetings", function () { return true } ],
[ "howdy", function () { return true } ],
[ "hello", function () { return true } ],
[ "lo", function () { return true } ],
[ "hey", function () { return true } ],
[ "morning", function () { var h = hour (); return ( h > 6 && h <= 12 ) } ],
[ "afternoon", function () { var h = hour (); return ( h > 12 && h < 18 ) } ],
[ "evening", function () { var h = hour (); return ( h >= 18 && h < 20 ) } ],
[ "night", function () { var h = hour (); return ( h >= 20 || h <= 6 ) } ],
[ "good morning", function () { var h = hour (); return ( h > 6 && h <= 12 ) } ],
[ "good afternoon", function () { var h = hour (); return ( h > 12 && h < 18 ) } ],
[ "good evening", function () { var h = hour (); return ( h >= 18 && h < 20 ) } ],
[ "good night", function () { var h = hour (); return ( h >= 20 || h <= 6 ) } ]
];
function isGreeting ( message ) {
for ( var i = 0, greeting; greeting = greetings [ i ]; i++ )
if ( message.match ( new RegExp ( "\\b" + greeting [ 0 ] + "\\b", "i" ) ) )
return true;
return false;
}
function randomGreeting () {
var greeting = false;
do {
var choice = Math.floor ( Math.random () * greetings.length );
if ( greetings [ choice ] [ 1 ] () )
greeting = greetings [ choice ] [ 0 ];
} while ( ! greeting );
return greeting;
}
function randomEnding () {
var endings = [ ".", ". :)", "! :D" ];
var randomEnding = Math.floor ( Math.random () * endings.length );
return endings [ randomEnding ];
}
function makeGreeting ( user ) {
var greeting = randomGreeting ();
var ending = randomEnding ();
return CapitalizeSentence ( greeting + ", " + user + ending );
}
function CapitalizeSentence ( sentence ) {
return sentence.charAt ( 0 ).toUpperCase () + sentence.slice ( 1 );
}
function Server ( name, host, port ) {
this.type = "server";
this.name = name;
this.host = host;
this.port = port;
this.connection;
this.onSignIn = false;
this.channels = [];
// For callbacks whose scopes Node.js mangles.
var thisServer = this;
this.connect = function () {
this.connection = net.connect (
{
host: this.host,
port: this.port
},
this.connected
);
this.connection.on ( "data", this.newMessage );
this.connection.on ( "end", this.onFinish );
};
this.connected = function () {
log ( "Connected to " + thisServer.host + ":" + thisServer.port + "." );
thisServer.signIn ();
};
this.newMessage = function ( message ) {
var line, lines = String ( message ).split ( "\r\n" );
while ( line = lines.shift () ) {
log ( "Me <- " + thisServer.host + ":" + thisServer.port + "\t" + line );
thisServer.signedIn ( line );
thisServer.onPing ( line );
thisServer.onChannelMessage ( line );
}
};
this.send = function ( message ) {
this.connection.write ( message + "\r\n" );
log ( "Me -> " + this.host + ":" + this.port + "\t" + message );
};
this.signIn = function () {
thisServer.send ( "USER " + thisServer.name + " 0 * :" + thisServer.name );
thisServer.send ( "NICK " + thisServer.name );
};
this.signedIn = function ( message ) {
var signedIn = message.match ( patterns.signedIn );
if ( ! signedIn ) return;
if ( this.onSignIn ) this.onSignIn ();
};
this.onPing = function ( message ) {
var replyName = message.match ( patterns.ping );
if ( ! replyName ) return;
thisServer.send ( "PONG " + os.hostname () + " " + replyName [ 1 ] );
};
this.onChannelMessage = function ( message ) {
var channelName = false,
isMessage = message.match ( patterns.userMessage ),
isJoinMessage = message.match ( patterns.joined );
if ( isMessage )
channelName = isMessage [ 2 ];
else if ( isJoinMessage )
channelName = isJoinMessage [ 2 ];
if ( ! channelName ) return;
for ( var channel in thisServer.channels )
if ( thisServer.channels [ channel ].name == channelName )
thisServer.channels [ channel ].newMessage ( message );
};
this.onFinish = function () {};
return this;
}
function Channel ( server, name ) {
this.type = "channel";
this.server = server;
this.name = name;
this.onJoin = function () {};
this.onMessage = function () {};
this.server.channels.push ( this );
var thisChannel = this;
this.send = function ( message ) {
thisChannel.server.send ( "PRIVMSG " + thisChannel.name + " :" + message );
};
this.newMessage = function ( message ) {
thisChannel.joined ( message );
if ( thisChannel.onMessage )
thisChannel.onMessage.apply ( thisChannel, [ message ] );
};
this.join = function () {
thisChannel.server.send ( "JOIN " + thisChannel.name );
};
this.joined = function ( message ) {
var joinedMessage = message.match ( patterns.joined );
if ( ! joinedMessage ) return;
var nickname = joinedMessage [ 1 ],
channel = joinedMessage [ 2 ];
if (
channel == thisChannel.name
&& nickname == thisChannel.server.name
&& thisChannel.onJoin
)
thisChannel.onJoin.apply ( thisChannel );
};
return this;
}
function log ( message ) {
console.log ( message );
message = Date.now () + "\t" + message;
fs.appendFile ( logFile, message + "\n" );
}
function say ( message ) {
console.log ( message );
log ( message );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment