GithubHelp home page GithubHelp logo

havvy / irc-socket Goto Github PK

View Code? Open in Web Editor NEW
16.0 7.0 9.0 103 KB

A simple IRC socket for use with Node IRC libraries.

License: ISC License

JavaScript 100.00%
javascript irc irc-socket nodejs

irc-socket's Introduction

IRC Socket - Socket wrapper to emit irc messages and handle server startup.

We provide for you the following benefits:

  • One "data" event per IRC message.
  • Pings are automatically ponged too..
  • The startup handshake (before the RPL_WELCOME) message is handled.
  • A raw method to send raw messages to the server.

Installation

npm install irc-socket --save

Instantiation

var NetSocket = require("net").Socket;
var IrcSocket = require("irc-socket");

var netSocket = new Socket();
var ircSocket = IrcSocket({
    socket: netSocket,

    port: 6667,
    server: "irc.someircnetwork.net",
    nicknames: ["freddy", "freddy_"],
    username: "freddy",
    realname: "Freddy",

    password: "server-password",

    // For transparent proxies using
    // the webirc protocol.
    proxy: {
        password: "shared-webirc-password",
        username: "users-username",
        hostname: "users-hostname",
        ip: "users-ip"
    },

    // For anybody wanting IRC3 capabilities.
    capabilities: {
        requires: ["multi-prefix", "userhost-in-names"],
        wants: ["account-notify"]
    },

    // Passed as the options parameter to socket's
    // connect method.
    // Shown here are example options.
    connectOptions: {
        localAddress: "a-local-address",
        localPort: "a-local-port",
        family: 6, // for ipv6 with net.Socket
    },

    // optional number of milliseconds with no 
    // response before timeout is fired
    timeout: 5 * 60 * 1000 
});

IrcSocket(config, socket)

The IrcSocket constructor is overloaded to provide a convenience form that takes the socket separately. This is provided so that if the config is created apart from the Socket, you don't need to modify the config object, especially since the rest of the configuration values can be serialized as JSON.

var NetSocket = require("net").Socket;
var IrcSocket = require("irc-socket");
var fs = require("fs");

var config = fs.readFileSync("config.json");
var netSocket = new NetSocket();
var ircSocket = IrcSocket(config, netSocket);

Configuration

The configuration options are as follows.

  • socket: [required] A net.Socket that IrcSocket wraps around.

  • server: [required] The server/host to connect to. e.g. "irc.mibbit.net"

  • port: [required] Which port to connect to. Normally 6667, or 6697 for TLS.

  • nicknames: [required] Array of nicknames to try to use in order.

  • username: [required] Username part of the hostmask.

  • realname: [required] "Real name" to send with the USER command.

  • password: Password used to connect to the network. Most networks don't have one.

  • proxy: WEBIRC details if your connection is acting as a (probably web-based) proxy.

  • capabilities: See the Capabilities section below..

  • connectOptions: Options passed to the wrapped socket's connect method. Options host and port are overwritten. See io.js's net.Socket.prototype.connect for options when using net.Socket in either Node.js or io.js. (Node.js's documentation is incomplete.)

Capabilities

Capabilities are a feature added in IRCv3 to extend IRC while still keeping IRCv2 compatibility. You can see the specification and well-known capabilities at their website.

Should you want to use IRCv3 features, pass an object with the requires property listing which features you absolutely require and wants for features that you can handle not being there. Both properties are optional.

Proxy

The proxy object has the following four fields, all required:

  • password: Shared secret password between you and the network you are connecting with.

  • username: User or client requesting spoof.

  • hostname: Hostname of user connecting to your proxy.

  • ip: IP Address of user connecting to your proxy.

Starting and Closing the Socket

You start and end the socket like a normal net.Socket with the connect and end methods. You shouldn't call end yourself though. Instead, you should write a QUIT message to the server (see tnext section).

The connect method returns a Promise<Result<{capabilities, nickname}, ConnectFailure>, Error>.

You can either use the "ready" event or use the promises returned by the connect method.

var client = IrcSocket(...);
client.once('ready', function () {
    client.end();
}
client.connect();
var client = IrcSocket(...);
client.connect().then(function (res) {
    // If connect failed, it already closed itself (or was force killed).
    // Otherwise, it succeeded, and we want to end it ourself.
    if (res.isOk()) {
        client.end();
    }
});

Writing to the Server

To send messages to the server, use socket.raw(). It accepts either a string or an array of Strings. The message '''must''' follow the [IRC protocol](https://irc-wiki.org/RFC 1459).

var details = {...};
var client = Ircsocket(details);

mySocket.connect().then(function (res) {
    if (res.isFail()) {
        return;
    }

    // Using a string.
    mySocket.raw("JOIN #biscuits");
}

mySocket.on('data', function (message) {
    message = message.split(" ");

    // Numeric 333 is sent when a user joins a channel.
    if (message[1] === "333" &&
        message[2] === details.nick &&
        message[3] === "#biscuits")
    {
        // Using an array instead of a string.
        mySocket.raw(["PRIVMSG", "#biscuits", ":Hello world."])
    }
});

mySocket.on('data', function (message) {
    // This is sent when you send QUIT too.
    if (message.slice(0, 5) === "ERROR") {
        mySocket.end();
    }
});

The raw method does not allow the usage of newline characters.

If an array is passed to raw, the message will be joined with a space.

Reading from the Server

All messages are emitted via a 'data' event. Receiving callbacks to this event will receive the message as the first parameter.

Examples of reading messages are in the previous example. Messages generally look like the following:

:irc.uk.mibbit.net 376 Havvy :End of /MOTD command.
:[email protected] QUIT :Quit: http://www.mibbit.com ajax IRC Client
ERROR :Closing Link: Havvy[127-00-00-00.redacted.com] (Quit: Custom quit message.)

All PING messages sent from the server are automatically PONGed too. You do not need to handle them yourself, but you still receive them, should you wish to log them.

Timeouts

The IRC socket will listen to ping messages and respond to them appropriately, so you do not need to handle this yourself.

Furthermore, if no message from the server is sent within five minutes, the IrcSocket will ping the server. If no response is thenn sent in five more minutes, the socket will close and emit a "timeout" event.

Should that not be good enough, you can always use setTimeout(number, optionalCallback) to use the implementing socket's (usually a net.Socket) timeout mechanisms.

You can listen to the "timeout" event for when this occurs.

Utility Methods

isStarted()

This method will return true if the socket was ever started.

isConnected()

This method will return true when the socket is started, but not ended. It will otherwise return false.

isReady()

This method will return true if the RPL_WELCOME message has been sent and the socket is still open. It will otherwise return false.

getRealname()

This method returns the realname (sometimes called gecos) of the connection.

Events

The irc-socket is an event emitter. It emits five events.

  • ready(): Once the first 001 message has been acknowledged.
  • data(message: String): Every message (including the 001) from the sender (inclusive) the the newline (exclusive).
  • close(): Once the implementing socket has been closed.
  • timeout(): When either this or the implenting socket time out.
  • end(): Once the implementing socket emits an 'end' event.

Testing

We install mocha as a developer dependency, and run tests with that.

npm install
npm test

Upgrading from v.2.0.0

All of the legacy compatibility has been removed.

A lot of things have changed/been added.

  • Changed: "nickname" property is now "nicknames" and takes an array.
  • Changed: You cannot restart an irc-socket Socket.
  • Changed: you must pass in your own Socket under the "socket" property. All socket related configuration has been removed.
  • Changed: Real support for IRC3 capabilities. "capab" property changed to "capabilities", takes an object now.
  • Added: Support for the (Webirc)[http://wiki.mibbit.com/index.php/WebIRC] protocol.
  • Added: isStarted, isReady methods. for more fine grained status tracking.
  • Added: connect method returns a (Bluebird) Promise that either resolves to a Result of Ok([capabilities]) or Fail(FailReason).
  • Removed: Auto-addition of colons in .raw(Array) are gone. Put them in yourself as needed.
  • Added debug property which takes a function like console.log.

Configuration

You must rename the nickname property to nicknames and change it to an array. This was done so that we can have multiple nicknames. Should all of them be not accepted, the socket will close itself.

If you were using the (practically useless) capab property, you probably want to use the capabilities property which takes an object now.

If you were using ipv6 you now want to pass family: 6 to the connectOptions property object now. Likewise, other Socket connect options should go there.

If you were using secure, you now want to create a net.Socket and then upgrade it to a tls.Socket and finally upgrade that to an irc-socket. See the next section for details.

Initialization

We now upgrade your socket into an irc-socket instead of instatiating it ourself.

This means that you need to instantiate the socket you want. Once you upgrade the socket, you give up ownership of it, but gain ownership of the upgraded socket.

var NetSocket = require("net").Socket;
var IrcSocket = require("irc-socket");

var netSocket = new Socket();
var ircSocket = IrcSocket({
    port: 6667,
    server: "irc.someircnetwork.net",
    nicknames: ["freddy", "freddy_"],
    username: "freddy",
    realname: "Freddy"
});

Even though port and server are part of the Socket connect options, you must pass them to the IrcSocket options object. For every other connect option that was supported, you should instead pass the option in the connectOptions object.

var NetSocket = require("net").Socket;
var IrcSocket = require("irc-socket");

var netSocket = new Socket();
var ircSocket = IrcSocket({
    socket: netSocket,
    port: 6667,
    server: "irc.someircnetwork.net",
    nicknames: ["freddy", "freddy_"],
    username: "freddy",
    realname: "Freddy",
    connectOptions: {
        localAddress: aLocalAddress,
        family: 6 // was `ipv6` option in old version, is `family` in net.Socket.
    }
});

For what was a secure socket, you must instead wrap a NetSocket around a TLS socket.

var NetSocket = require("net").Socket;
var TlsSocket = require("tls").TLSSocket;
var IrcSocket = require("irc-socket");

var netSocket = new Socket();
var tlsSocket = new TlsSocket(netSocket, {rejectUnauthorized: false});
var IrcSocket = IrcSocket({
    socket: tlsSocket,
    ...
});

raw([String]) breaking change

If you were using the raw method with an array and relying on it to put in colons for you, you must go back and add those colons in yourself. Just grep for raw([ and you should find all of them.

connect() returns a Promise

Instead of listening to the ready event and doing your own startup handling there, the connect method return a Promise. The promise resolves to a r-result result (mostly equivalent to Rust's Result type) where it is either Ok({capabilities, nickname}) or Fail(ConnectFailureReason). The connect failure reasons are located at IrcSocket.connectFailures.

See Also

The irc-message package will quickly parse the strings you pass into objects. The new version also merges with irc-message-stream to provide a stream.

For a full IRC Bot framework, take a look at Tennu.

For long-running IRC Clients, take a look at IRC Factory.

irc-socket's People

Contributors

havvy avatar hohl avatar rickihastings avatar silverbucket avatar ubsan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

irc-socket's Issues

What's the maximum number of sockets that can be opened?

I see that it is not using sockets from http or https's sockets by looking at http.globalAgent,naxSockets and sockets. Is there a way to set the maximum number of sockets for simple-irc-socket?

The problem I am having is ECONNRESET error, which I suspect is due to the maximum number of sockets per host on either my irc bot server or the actual irc server. I still have to look into this issue but I was just wondering if there is any way to increase the maximum number of sockets on my node side.

Tls not working

I noticed that TLS connections to an IRC server don't work. Haven't had a chance to have a look at the code but wanted to make a note of it here.
Blocks sockethub/sockethub#319

`socket.impl.end` called before `socket.impl` emits 'connect' causes Write After End

After triggering ircSocket.end() there still seems to be written something to the socket:

Uncaught Error: Uncaught, unspecified "error" event.
      at Error (native)
      at EventEmitter.<anonymous> (index.js:78:63)
      at writeAfterEnd (_stream_writable.js:169:10)
      at Socket.Writable.write (_stream_writable.js:214:5)
      at Socket.write (net.js:634:40)
      at TCPConnectWrap.afterConnect [as oncomplete] (net.js:991:10)

(see build log or test source)

setTimeout(function onTimeout() {})

Add a method setTimeout that takes a function that will be called if the server does not send a PING within thrice the normal interval between pings.

Note: The normal interval needs to be measured at startup, as it differs from network to network.

Unspecified error

events.js:74
        throw TypeError('Uncaught, unspecified "error" event.');
              ^
TypeError: Uncaught, unspecified "error" event.
    at TypeError (<anonymous>)
    at EventEmitter.emit (events.js:74:15)
    at Socket.<anonymous> (/home/ielectric/node_modules/irc-bridge-bot/node_modules/tennu/node_modules/irc-socket/irc-socket.js:105:20)
    at Socket.EventEmitter.emit (events.js:95:17)
    at net.js:441:14
    at process._tickDomainCallback (node.js:459:13)

Allow passing in instatiated socket.

Basically, allow:

var irc = require('irc-socket');
var net = require('net');
var config = require('./config.json');
var socket = new net.Socket(config);
var irc_socket = irc(config, socket);

Socket.connect doesn't accept options

For some reason I always end with ECONNREFUSED when trying your release 3.0.1. So I had a look into the source and found

this.impl.connect(this.connectOptions);

in line 371. However net.Socket class documentation doesn't mention any connect method which takes an object.

When replacing that line with

this.impl.connect(this.connectOptions.port, this.connectOptions.server);

everything seems to work for me.

Updated Raspbian and now getting Unhandled 'error' event every hour or so and I am not sure how to troubleshoot.

node:events:353
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:213:20)
Emitted 'error' event at:
    at Socket.<anonymous> (/home/mervin/mervin-3/node_modules/irc-socket/irc-socket.js:337:16)
    at Socket.emit (node:events:376:20)
    at emitErrorNT (node:internal/streams/destroy:188:8)
    at emitErrorCloseNT (node:internal/streams/destroy:153:3)
    at processTicksAndRejections (node:internal/process/task_queues:80:21) {
  errno: -104,
  code: 'ECONNRESET',
  syscall: 'read'
}
error: Forever detected script exited with code: 1

I have my bot set to restart on errors, this happens every 1.5-2 hours. It started happening after i did an apt upgrade on Raspbian. Since the error is cryptic I am not sure how to go about troubleshooting it.

Any help would be appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.