dank-twitch-irc
Node.js-only Twitch IRC lib, written in TypeScript.
Requires Node.js 10 (LTS) or above.
Table of Contents
Usage
const { ChatClient } = require("dank-twitch-irc");
let client = new ChatClient();
client.on("ready", () => console.log("Successfully connected to chat"));
client.on("close", (error) => {
if (error != null) {
console.error("Client closed due to error", error);
}
});
client.on("PRIVMSG", (msg) => {
console.log(`[#${msg.channelName}] ${msg.displayName}: ${msg.messageText}`);
});
client.connect();
client.join("forsen");
Available client events
-
client.on("connecting", () => { /* ... */ })
: Called when the client
starts connecting for the first time.
-
client.on("connect", () => { /* ... */ })
: Called when the client
connects for the first time. This is called when the transport layer
connections (e.g. TCP or WebSocket connection is established), not when login
to IRC succeeds.
-
client.on("ready", ()) => { /* ... */ })
: Called when the client becomes
ready for the first time (login to the chat server is successful.)
-
client.on("close", (error?: Error) => { /* ... */ })
: Called when the
client is terminated as a whole. Not called for individual connections that
were disconnected. Can be caused for example by a invalid OAuth token (failure
to login), or when client.close()
or client.destroy()
was called. error
is only non-null if the client was closed by a call to client.close()
.
-
client.on("error", (error: Error?) => { /* ... */ })
: Called when any
error occurs on the client, including non-fatal errors such as a message that
could not be delivered due to an error.
-
client.on("message", (message: IRCMessage) => { /* ... */ })
: Called on
every incoming message. If the message is a message that is further parsed (I
called these "twitch messages" in this library) then the message
passed to
this handler will already be the specific type, e.g. PrivmsgMessage
if the
command is PRIVMSG
.
-
client.on("PRIVMSG", (message: PrivmsgMessage) => { /* ... */ })
: Called
on incoming messages whose command is PRIVMSG
. The message
parameter is
always instanceof PrivmsgMessage
. (See the API documentation for what
properties exist on all PrivmsgMessage
instances)
For example:
client.on("CLEARCHAT", (msg) => {
if (msg.isTimeout()) {
console.log(
`${msg.targetUsername} just got timed out for ` +
`${msg.banDuration} seconds in channel ${msg.channelName}`
);
}
});
Other message types that have specific message parsing are:
CLEARCHAT
(maps to ClearchatMessage
) - Timeout and ban
messagesCLEARMSG
(maps to ClearmsgMessage
) - Single message
deletions (initiated by /delete
)HOSTTARGET
(maps to HosttargetMessage
) - A channel
entering or exiting host mode.NOTICE
(maps to NoticeMessage
) - Various notices, such as
when you /help
, a command fails, the error response when you are timed
out, etc.PRIVMSG
(maps to PrivmsgMessage
) - Normal chat messagesROOMSTATE
(maps to RoomstateMessage
) - A change to a
channel's followers mode, subscribers-only mode, r9k mode, followers mode,
slow mode etc.USERNOTICE
(maps to UsernoticeMessage
) - Subs, resubs,
sub gifts, rituals, raids, etc. - See more details about how to handle this
message type below.USERSTATE
(maps to UserstateMessage
) - Your own state
(e.g. badges, color, display name, emote sets, mod status), sent on every
time you join a channel or send a PRIVMSG
to a channelGLOBALUSERSTATE
(maps to
GlobaluserstateMessage
) - Logged in user's "global
state", sent once on every login (Note that due to the used connection pool
you can receive this multiple times during your bot's runtime)WHISPER
(maps to WhisperMessage
) - Somebody else
whispering youJOIN
(maps to JoinMessage
) - You yourself joining a channel,
of if you have requestMembershipCapability
enabled, also other users
joining channels you are joined to.PART
(maps to JoinMessage
) - You yourself parting (leaving)
a channel, of if you have requestMembershipCapability
enabled, also other
users parting channels you are joined to.RECONNECT
(maps to ReconnectMessage
) - When the twitch
server tells a client to reconnect and re-join channels (You don't have to
listen for this yourself, this is done automatically already)PING
(maps to PingMessage
) - When the twitch server sends a
ping, expecting a pong back from the client to verify if the connection is
still alive. (You don't have to listen for this yourself, the client
automatically responds for you)PONG
(maps to PongMessage
) - When the twitch server responds
to our PING
requests (The library automatically sends a PING
request
every 30 seconds to verify connections are alive)CAP
(maps to CapMessage
) - Message type received once during
connection startup, acknowledging requested capabilities.
All other commands (if they don't have a special parsed type like the ones
listed above) will still be emitted under their command name as an
IRCMessage
, e.g.:
client.on("372", (msg) =>
console.log(`Server MOTD is: ${msg.ircParameters[1]}`)
);
Handling USERNOTICE
messages
The USERNOTICE
message type is special because it encapsulates a wide range of
events, including:
- Subs
- Resubs
- Gift subscription
- Incoming raid and
- Channel rituals,
which are all emitted under the USERNOTICE
event. See also
the offical documentation
about the USERNOTICE
command.
Every USERNOTICE
message is sent by a user, and always contains a
msg.systemMessage
(This is a message that twitch formats for you, e.g.
4 raiders from PotehtoO have joined!
for a raid
message.) Additionally,
every USERNOTICE
message can have a message that is additionally sent/shared
from the sending user, for example the "share this message with the streamer"
message sent with resubs and subs. If no message is sent by the user,
msg.messageText
is undefined
.
dank-twitch-irc
currently does not have special parsing code for each
USERNOTICE
messageTypeID
(e.g. sub
, resub
, raid
, etc...) - Instead the
parser assigns all msg-param-
tags to the msg.eventParams
object. See below
on what msg.eventParams
are available for each of the messageTypeID
s.
Sub and resub
When a user subscribes or resubscribes with his own money/prime (this is NOT
sent for gift subs, see below)
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isSub() && !msg.isResub()) {
return;
}
if (msg.isSub()) {
console.log(
msg.displayName +
" just subscribed to " +
msg.channelName +
" with a tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams.subPlanName +
") sub for the first time!"
);
} else if (msg.isResub()) {
let streakMessage = "";
if (msg.eventParams.shouldShareStreak) {
streakMessage =
", currently " + msg.eventParams.streakMonths + " months in a row";
}
console.log(
msg.displayName +
" just resubscribed to " +
msg.channelName +
" with a tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams.subPlanName +
") sub! They are resubscribing for " +
msg.eventParams.cumulativeMonths +
" months" +
streakMessage +
"!"
);
}
if (msg.messageText != null) {
console.log(
msg.displayName +
" shared the following message with the streamer: " +
msg.messageText
);
} else {
console.log("They did not share a message with the streamer.");
}
});
Incoming raids
Twitch says:
Incoming raid to a channel. Raid is a Twitch tool that allows broadcasters to
send their viewers to another channel, to help support and grow other members
in the community.)
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isRaid()) {
return;
}
console.log(
msg.displayName +
" just raided " +
msg.channelName +
" with " +
msg.eventParams.viewerCount +
" viewers!"
);
});
Subgift
When a user gifts somebody else a subscription.
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isSubgift()) {
return;
}
if (msg.eventParams.months === 1) {
console.log(
msg.displayName +
" just gifted " +
msg.eventParams.recipientDisplayName +
" a fresh tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams +
") sub to " +
msg.channelName +
"!"
);
} else {
console.log(
msg.displayName +
" just gifted " +
msg.eventParams.recipientDisplayName +
" a tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams +
") resub to " +
msg.channelName +
", that's " +
msg.eventParams.months +
" in a row!"
);
}
if (msg.senderUserID === "274598607") {
console.log("That (re)sub was gifted anonymously!");
}
});
Anonsubgift
When an anonymous user gifts a subscription to a viewer.
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isAnonSubgift()) {
return;
}
if (msg.eventParams.months === 1) {
console.log(
"An anonymous gifter just gifted " +
msg.eventParams.recipientDisplayName +
" a fresh tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams +
") sub to " +
msg.channelName +
"!"
);
} else {
console.log(
"An anonymous gifter just gifted " +
msg.eventParams.recipientDisplayName +
" a tier " +
msg.eventParams.subPlan +
" (" +
msg.eventParams +
") resub to " +
msg.channelName +
", that's " +
msg.eventParams.months +
" in a row!"
);
}
});
anongiftpaidupgrade, giftpaidupgrade
When a user commits to continue the gift sub by another user (or an anonymous
gifter).
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isAnonGiftPaidUpgrade()) {
return;
}
console.log(
msg.displayName +
" is continuing their " +
msg.channelName +
" gift sub they got from an anonymous user!"
);
});
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isGiftPaidUpgrade()) {
return;
}
console.log(
msg.displayName +
" is continuing their " +
msg.channelName +
" gift sub they got from " +
msg.msgParam.senderName +
"!"
);
});
ritual
Channel ritual. Twitch says:
Channel ritual. Many channels have special rituals to celebrate viewer
milestones when they are shared. The rituals notice extends the sharing of
these messages to other viewer milestones (initially, a new viewer chatting
for the first time).
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isRitual()) {
return;
}
if (msg.eventParams.ritualName === "new_chatter") {
console.log(
msg.displayName + " is new to " + msg.channelName + "'s chat! Say hello!"
);
} else {
console.warn(
"Unknown (unhandled) ritual type: " + msg.eventParams.ritualName
);
}
});
bitsbadgetier
When a user cheers and earns himself a new bits badge with that cheer (e.g. they
just cheered more than/exactly 10000 bits in total, and just earned themselves
the 10k bits badge)
chatClient.on("USERNOTICE", (msg) => {
if (!msg.isBitsBadgeTier()) {
return;
}
console.log(
msg.displayName +
" just earned themselves the " +
msg.threshold +
" bits badge in " +
msg.channelName +
"'s channel!"
);
});
ChatClient API
You probably will want to use these functions on ChatClient
most frequently:
client.join(channelName: string): Promise<void>
- Join (Listen to) the
channel given by the channel nameclient.joinAll(channelNames: string[]): Promise<void>
- Join (Listen to) all
of the listed channels at once (bulk join)client.part(channelName: string): Promise<void>
- Part (Leave/Unlisten) the
channel given by the channel nameclient.privmsg(channelName: string, message: string): Promise<void>
- Send a
raw PRIVMSG
to the given channel. You can issue chat commands with this
function, e.g. client.privmsg("forsen", "/timeout weeb123 5")
or normal
messages, e.g. client.privmsg("forsen", "Kappa Keepo PogChamp")
.client.say(channelName: string, message: string): Promise<void>
- Say a
normal chat message in the given channel. If a command is given as message
,
it will be escaped.client.me(channelName: string, message: string): Promise<void>
- Post a
/me
message in the given channel.client.timeout(channelName: string, username: string, length: number, reason?: string): Promise<void>
-
Timeout username
for length
seconds in channelName
. Optionally accepts a
reason to set.client.ping()
- Send a PING
on a connection from the pool, and awaits the
PONG
response. You can use this to measure server latency, for example.client.whisper(username: string, message: string)
- Send the user a whisper
from the bot.client.setColor(color: Color)
- set the username color of your bot account.
E.g. client.setColor({ r: 255, g: 0, b: 127 })
.
Extra functionality:
client.sendRaw(command: string): void
- Send a raw IRC command to a
connection in the connection pool.client.unconnected (boolean)
- Returns whether the client is unconnected.client.connecting (boolean)
- Returns whether the client is connecting.client.connected (boolean)
- Returns whether the client is connected
(Transport layer is connected).client.ready (boolean)
- Returns whether the client is ready (Logged into
IRC server).client.closed (boolean)
- Returns whether the client is closed.
Note that channel names in the above functions always refer to the "login name"
of a twitch channel. Channel names may not be capitalized, e.g. Forsen
would
be invalid, but forsen
not. This library also does not accept the leading #
character and never returns it on any message objects (e.g. msg.channelName
would be forsen
, not #forsen
).
API Documentation
Generated API documentation can be found here:
https://robotty.github.io/dank-twitch-irc
Client options
Pass options to the ChatClient
constructor. More available options are
documented in the Below are all possible options and their default values:
Note! ALL of these configuration options are optional! I highly recommend you
only set the very config options you need, the rest are usually at a reasonable default.
For most bots, you only need to set username
and password
:
let client = new ChatClient({
username: "your-bot-username",
password: "0123456789abcdef1234567",
});
Nevertheless, here are examples of all possible config options:
let client = new ChatClient({
username: "your-bot-username",
password: "0123456789abcdef1234567",
rateLimits: "default",
rateLimits: "knownBot",
rateLimits: "verifiedBot",
rateLimits: {
highPrivmsgLimits: 100,
lowPrivmsgLimits: 20,
},
connection: {
type: "tcp",
secure: false,
host: "custom-chat-server.com",
port: 1234,
},
connection: {
type: "websocket",
secure: true,
},
connection: {
type: "websocket",
url: "wss://custom-url.com/abc/def",
},
connection: {
type: "duplex",
stream: () => aNodeJsDuplexInstance,
preSetup: true,
},
maxChannelCountPerConnection: 100,
connectionRateLimits: {
parallelConnections: 5,
releaseTime: 1000,
},
requestMembershipCapability: false,
installDefaultMixins: false,
ignoreUnhandledPromiseRejections: true,
});
Features
This client currently supports the following features:
- Connection pooling and round-robin connection usage
- Automatic rate limiter for connection opening and chat commands
- All twitch-specific message types parsed (
CLEARCHAT
, CLEARMSG
,
GLOBALUSERSTATE
, HOSTTARGET
, JOIN
, NOTICE
, PART
, PING
, PONG
,
PRIVMSG
, RECONNECT
, ROOMSTATE
, USERNOTICE
, USERSTATE
, WHISPER
,
CAP
) - Accurate response to server responses (e.g. error thrown if you are banned
from channel/channel is suspended/login is invalid etc.)
- Bulk join functionality to join lots of channels quickly
- Implements the recommended connection control, utilizing
RECONNECT
, PING
and PONG
- Full tracking of room state (e.g. submode, emote-only mode, followers mode,
r9k etc.) and user state (badges, moderator state, color, etc).
- Most function calls return promises but errors can also be handled by
subscribing to the error event
- Slow-mode rate limiter for non-VIP/moderator bots (waits either the global
~1.3 sec/channel-specific slow mode)
- Support for different types of transport (in-memory, TCP, WebSocket)
There are some features you might find useful in your bot that are not necessary
for general client/bot operations, so they were packaged as mixins. You can
activate mixins by calling:
const { ChatClient, AlternateMessageModifier } = require("dank-twitch-irc");
let client = new ChatClient();
client.use(new AlternateMessageModifier(client));
Available mixins are:
new AlternateMessageModifier(client)
will allow your bot to send the same
message within a 30 seconds period. You must also use client.say
and
client.me
for this mixin to behave consistently and reliably.new SlowModeRateLimiter(client, /* optional */ maxWaitingMessages)
will rate
limit your messages in channels where your bot is not moderator, VIP or
broadcaster and has to wait a bit between sending messages. If more than
maxWaitingMessages
are waiting, the outgoing message will be dropped
silently. maxWaitingMessages
defaults to 10. Note this mixin only has an
effect on client.say
and client.me
functions, not client.privmsg
.
and the mixins installed by default:
new PrivmsgMessageRateLimiter(client)
- Rate limits outgoing messages
according to the rate limits imposed by Twitch. Configure the verified/known
status of your bot using the config (see above).new ConnectionRateLimiter(client)
- Rate limits new connections accoding to
the rate limits set in the config.new UserStateTracker(client)
- Used by other mixins. Keeps track of what
state your bot user has in all channels.new RoomStateTracker()
- Used by other mixins. Keeps track of each channel's
state, e.g. sub-mode etc.new IgnoreUnhandledPromiseRejectionsMixin()
- Silences
UnhandledPromiseRejectionWarning
s on promises returned by the client's
functions. (installed for you if you activate the
ignoreUnhandledPromiseRejections
client option)
Tests
npm run test
Test run report is available in ./mochawesome-report/mochawesome.html
.
Coverage report is produced as ./coverage/index.html
.
Lint and check code style
npm run lint
npm run lintfix