Skip to content

Instantly share code, notes, and snippets.

@BenGoldberg1
Created August 5, 2016 03:09
Show Gist options
  • Save BenGoldberg1/30077fa2e89a863ac7221fdcaa866455 to your computer and use it in GitHub Desktop.
Save BenGoldberg1/30077fa2e89a863ac7221fdcaa866455 to your computer and use it in GitHub Desktop.

This is an idea for an irc client. It would consist of several separate and distinct programs, each running asynchronously from each other.

The main program does the following:

  1. it connects to one or more irc servers
  2. it creates a listening network socket on the loopback interface, so only processes on the user's computer can connect to said socket.
  3. it spawns one or more "tasks", using popen, and passes the port of the socket (from step 2) to each task, either on the commandline or via stdin. Don't forget to escape specials in the executable's name. Maybe I'm the only person in the world with a space in my user name (and thus my home directory), but maybe there're others.
  4. it uses select or poll or libevent or libuv or twisted (etc) to do multiplexed io. 4a) When the listening socket it readable, it accept()s a (task) connection, and adds it to the list of sockets to check for readability. 4b) When any irc socket becomes readable, read data from it, and append it's data to it's input buffer. As long as the buffer contains a newline, remove everything up to and including the first newline, pass the extracted line(s) to an irc protocol parser. Generate events, encode them as json-rpc notifications, send the notification to every task which has asked to be informed. Make sure to check return values from send(), and append a copy of any unsent data to the task's outgoing buffer. Also, if the outgoing buffer is not empty, don't bother trying to send(), just append. If need be, add the task to a list which needs to be select()ed upon for writability. 4c) When any task's socket becomes readable, read data from it, and append it's data to that task's input buffer. As long as the buffer contains a newline, remove everything up to and including the first newline, pass it to a json-rpc 1.0 handler, which turns this into a notificaiton or a response or a request from the task. Any task may request that the main program open an irc connection, send various irc commands, ask to be notified of various events, query the server about various things, and may generate events to be sent to other interested tasks, etc, etc.
  5. For all tasks which have a non-empty outgoing buffer, test (using select/poll/whatever) whether their sockets are writable; for each which is, use send() on the entire outgoing buffer, and remove the appropriate number of leading bytes from the buffer. If there are no more remaining bytes in the outgoing buffer, remove the task from the appropriate list. 6a) When we get an eof from a task socket, use pclose() on the handle created by popen(). Possibly generate some events and send them to other tasks. 6b) When we get an eof from an irc socket, send out a notification event to interested tasks.

Events can be irc events, gui events, or other artificial events.

One task can create the gui, Another task could load and save configure files, Another task collects low-level irc events (like WHO responses), and provides high-level data (a single json object contain a list of users on a channel), in response to queries from other tasks, etc.

Some tasks are more like the plugins of more traditional irc clients; such plugin tasks can run scripts written in perl, python, javascript, lua, etc. Interfaces similar to that of hexchat should be very doable. In fact, being able to use existing hexchat scripts would be an important goal.

Because everything is connected via sockets and json-rpc, instead of dlls and a binary abi, there is much less risk of one badly written user script accidentlly bringing down the whole program.

Not only can the user scripts be in any language, but so can the core tasks. A tcl/tk gui? No problem!

Bikeshedding: Ideally, whenever the main program sends a request to a particular task, no valid response will be 0, "", or null, as these return values can happen in several scripting languages simply due to a user forgetting an explicit return statement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment