dialect 3.1.0
IRC parsing library
To use this package, run the following command in your project's root directory:
Manual usage
Put the following dependency into your project's dependences section:
This package provides sub packages which can be used individually:
dialect:assertgen - Tool for generating asserts for unittest blocks
dialect
IRC parsing library.
API documentation can be found here.
struct IRCEvent
{
enum Type { ... } // IRC event types
Type type;
IRCUser sender;
IRCUser target;
string channel;
string subchannel;
string content;
string[16] aux;
Nullable!long[16] count;
string tags;
uint num;
long time;
string raw;
string errors;
version(TwitchSupport)
{
string emotes;
string id;
}
}
struct IRCUser
{
version(BotElements)
{
enum Class { ... } // user roles in a channel; operator, staff, ...
Class class_;
}
string nickname;
string realName;
string ident;
string address;
string account;
long updated;
version(TwitchSupport)
{
string displayName;
string badges;
string colour;
uint id;
}
}
struct IRCChannel
{
static struct Mode { ... }
string name;
string topic;
string modechars;
Mode[] modes;
bool[string] users;
bool[string][char] mods;
long created;
}
struct IRCServer
{
enum Daemon { ... }
Daemon daemon;
string address;
ushort port;
// [...]
}
struct IRCClient
{
string nickname;
string user;
string realName;
}
struct IRCParser
{
IRCClient client;
IRCServer server;
this(IRCClient, IRCServer);
@disable this(this);
IRCEvent toIRCEvent(const string); // <-- entry point of use
}
Available build configurations
library
twitch
includes extra parsing needed to interface with Twitch serversbot
includes some code specifically useful for bot applicationstwitchbot
combinestwitch
andbot
It is pure
and @safe
in the library
and bot
configurations.
How to use
See the examples directory for a simple bot client that connects to an IRC server and joins a channel.
This project is bring-your-own-client and is not a bot framework. For that you're likely better off with the reference implementation bot and writing a plugin that suits your needs.
Longer story
- Write a client that connects to an IRC server.
- Create an
IRCClient
and configure its members. (required for context when parsing) - Create an
IRCServer
and configure its members. (it may work without but just give it at minimum a host address) - Create an
IRCParser
by passing your client and server to its constructor. Pass it between functions byref
. - Read a string from the server and parse it into an
IRCEvent
withparser.toIRCEvent(stringFromServer)
. - Switch on the
IRCEvent.type
member and handle the event accordingly. Remember toPONG
onPING
. - Draw the rest of the owl.
Like so
IRCClient client;
client.nickname = "...";
IRCServer server;
server.address = "...";
IRCParser parser = IRCParser(client, server);
{
const fromServer = `:zorael!~NaN@address.tld MODE #channel +v nickname`;
auto event = parser.toIRCEvent(fromServer);
with (event)
{
assert(type == IRCEvent.Type.MODE);
assert(sender.nickname == "zorael");
assert(sender.ident == "~NaN");
assert(sender.address == "address.tld");
assert(channel == "#channel");
assert(content == "nickname");
assert(aux[0] == "+v");
}
}
{
const fromServer = ":cherryh.freenode.net 435 oldnick newnick #d :Cannot change nickname while banned on channel";
auto event = parser.toIRCEvent(fromServer);
with (event)
{
assert(type == IRCEvent.Type.ERR_BANONCHAN);
assert(sender.address == "cherryh.freenode.net");
assert(target.nickname == "oldnick");
assert(channel == "#d");
assert(content == "Cannot change nickname while banned on channel");
assert(aux[0] == "newnick");
assert(num == 435);
}
}
{
const fromServer = `@badge-info=;badges=;color=;display-name=AnAnonymousGifter;emotes=;flags=;id=01af180f-5efd-40c8-94fb-d0a346c7fg86;login=ananonymousgifter;mod=0;msg-id=subgift;msg-param-fun-string=FunStringFour;msg-param-gift-months=1;msg-param-goal-contribution-type=SUB_POINTS;msg-param-goal-current-contributions=15624;msg-param-goal-target-contributions=20000;msg-param-goal-user-contributions=1;msg-param-months=24;msg-param-origin-id=54\s41\s9a\s69\s6c\sb4\s3c\s8b\s0b\se4\sdf\s4c\sba\s5b\s9b\s23\s4c\sa7\s9b\sc4;msg-param-recipient-display-name=SomeoneOnTwitch;msg-param-recipient-id=547202201;msg-param-recipient-user-name=someoneontwitch;msg-param-sub-plan-name=Channel\sSubscription\s(some_streamer);msg-param-sub-plan=1000;room-id=4920718204;subscriber=0;system-msg=An\sanonymous\suser\sgifted\sa\sTier\s1\ssub\sto\sSomeoneOnTwitch!\s;tmi-sent-ts=1685982143345;user-id=274518607;user-type= :tmi.twitch.tv USERNOTICE #some_streamer`;
auto event = parser.toIRCEvent(fromServer);
with (event)
{
assert(type == IRCEvent.Type.TWITCH_SUBGIFT);
assert(sender.nickname == "ananonymousgifter");
assert(sender.address == "tmi.twitch.tv");
assert(sender.account == "ananonymousgifter");
assert(sender.displayName == "AnAnonymousGifter");
assert(sender.badges == "*");
assert(sender.id == 274518607);
assert(target.nickname == "someoneontwitch");
assert(target.account == "someoneontwitch");
assert(target.displayName == "SomeoneOnTwitch");
assert(target.id == 547202201);
assert(channel == "#some_streamer");
assert(content == "An anonymous user gifted a Tier 1 sub to SomeoneOnTwitch!");
assert(aux[0] == "1000");
assert(aux[1] == "FunStringFour");
assert(aux[2] == "Channel Subscription (some_streamer)");
assert(aux[5] == "SUB_POINTS");
assert(count[0] == 1);
assert(count[2] == 20000);
assert(count[3] == 15624);
assert(count[4] == 1);
assert(tags == "badge-info=;badges=;color=;display-name=AnAnonymousGifter;emotes=;flags=;id=01af180f-5efd-40c8-94fb-d0a346c7fg86;login=ananonymousgifter;mod=0;msg-id=subgift;msg-param-fun-string=FunStringFour;msg-param-gift-months=1;msg-param-goal-contribution-type=SUB_POINTS;msg-param-goal-current-contributions=15624;msg-param-goal-target-contributions=20000;msg-param-goal-user-contributions=1;msg-param-months=24;msg-param-origin-id=54\\s41\\s9a\\s69\\s6c\\sb4\\s3c\\s8b\\s0b\\se4\\sdf\\s4c\\sba\\s5b\\s9b\\s23\\s4c\\sa7\\s9b\\sc4;msg-param-recipient-display-name=SomeoneOnTwitch;msg-param-recipient-id=547202201;msg-param-recipient-user-name=someoneontwitch;msg-param-sub-plan-name=Channel\\sSubscription\\s(some_streamer);msg-param-sub-plan=1000;room-id=4920718204;subscriber=0;system-msg=An\\sanonymous\\suser\\sgifted\\sa\\sTier\\s1\\ssub\\sto\\sSomeoneOnTwitch!\\s;tmi-sent-ts=1685982143345;user-id=274518607;user-type=");
assert(id == "01af180f-5efd-40c8-94fb-d0a346c7fg86");
}
}
See the tests
directory for more example parses.
Unit test generation
Compiling the assertgen
dub subpackage builds a command-line tool with which it is easy to generate unit test assert
blocks like the ones above. These can then be pasted into an appropriate file in the tests
directory, and ideally submitted as a GitHub pull request for upstream inclusion. You can use it to contribute known-good parses and increase coverage of event types.
Simply run dub run :assertgen
and follow the on-screen instructions.
Enter daemon (plus optional daemon literal) [solanum]: unreal UnrealIRCd
Enter network [Libera.Chat]: foobar
Enter server address [irc.libera.chat]: irc.server.tld
// 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8<
[...]
// Paste a raw event string and hit Enter to generate an assert block. Ctrl+C to exit.
:irc.server.tld PRIVMSG #channel :i am a fish
{
enum input = `:irc.server.tld PRIVMSG #channel :i am a fish`;
immutable event = parser.toIRCEvent(input);
with (event)
{
assert(type == IRCEvent.Type.CHAN);
assert(sender.address == "irc.server.tld");
assert(channel == "#channel");
assert(content == "i am a fish");
}
}
The output will by default also be saved to an assertgen.log
file in the current directory.
See the --help
listing for more flags, passed through dub
with dub run :assertgen -- --help
.
Caveats
Starting with v3.0.0
, a more recent compiler version is required. This is to allow for use of named arguments. You need a compiler based on D version 2.108 or later (April 2024). For ldc this translates to a minimum of version 1.38, while for gdc you broadly need release series 14.
If your repositories (or other software sources) don't have compilers recent enough, you can use the official install.sh
installation script to download current ones, or any version of choice.
Releases of the library prior to v3.0.0
remain available for older compilers.
Note that while IRC is standardised, servers still come in many flavours, some of which outright conflict with others. Supporting conflicting event types is a challenge and requires manually adding special-casing to the parser. The groundwork for this is in place and is working (see dialect.defs.Typenums
), but it is understandably not fully exhaustive. If you encounter an event that is not parsed correctly, please file an issue.
Please report bugs. Unreported bugs can only be fixed by accident.
Roadmap
- nothing right now, ideas needed
Built with
License
This project is licensed under the Boost Software License 1.0 - see the LICENSE10.txt file for details.
- 3.1.0 released 3 days ago
- zorael/dialect
- www.github.com/zorael/dialect
- BSL-1.0
- Copyright © 2016+, JR
- Authors:
- Sub packages:
- dialect:assertgen
- Dependencies:
- lu
- Versions:
-
3.1.0 2025-Feb-01 3.0.1 2025-Jan-13 3.0.0 2024-Aug-11 2.2.0 2024-Jan-27 2.1.0 2024-Jan-05 - Download Stats:
-
-
0 downloads today
-
128 downloads this week
-
349 downloads this month
-
25456 downloads total
-
- Score:
- 2.9
- Short URL:
- dialect.dub.pm