{"id":13758395,"url":"https://github.com/robotty/dank-twitch-irc","last_synced_at":"2025-05-10T08:30:24.883Z","repository":{"id":34933274,"uuid":"187227755","full_name":"robotty/dank-twitch-irc","owner":"robotty","description":"Connect to Twitch chat from Node.js","archived":true,"fork":false,"pushed_at":"2022-02-08T19:08:47.000Z","size":4785,"stargazers_count":87,"open_issues_count":14,"forks_count":24,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-04T19:02:34.875Z","etag":null,"topics":["irc","nodejs","npm-package","tmi","twitch"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/dank-twitch-irc","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robotty.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-17T14:10:19.000Z","updated_at":"2025-01-25T13:21:33.000Z","dependencies_parsed_at":"2022-08-08T03:00:48.176Z","dependency_job_id":null,"html_url":"https://github.com/robotty/dank-twitch-irc","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotty%2Fdank-twitch-irc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotty%2Fdank-twitch-irc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotty%2Fdank-twitch-irc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotty%2Fdank-twitch-irc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robotty","download_url":"https://codeload.github.com/robotty/dank-twitch-irc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253389382,"owners_count":21900750,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["irc","nodejs","npm-package","tmi","twitch"],"created_at":"2024-08-03T13:00:29.351Z","updated_at":"2025-05-10T08:30:23.711Z","avatar_url":"https://github.com/robotty.png","language":"TypeScript","funding_links":[],"categories":["Libraries"],"sub_categories":["JavaScript (Node.js)"],"readme":"# dank-twitch-irc\n\n**I (randers) am no longer supporting or updating dank-twitch-irc. While it may continue to work, I'm no longer making sure it will continue to do so in the future. This package will also not receive any dependency or security updates. For this reason I recommend you to choose a different library in your projects, or to fork this project to continue development on it. If somebody makes a high-quality fork of this project, you can contact me and I can link to your fork in this README here. Thank you.**\n\n![Build](https://github.com/robotty/dank-twitch-irc/workflows/Build/badge.svg)\n\nNode.js-only Twitch IRC lib, written in TypeScript.\n\nRequires Node.js 10 (LTS) or above.\n\n- [View on GitHub](https://github.com/robotty/dank-twitch-irc)\n- [View on npm](https://www.npmjs.com/package/dank-twitch-irc)\n- [View documentation](https://robotty.github.io/dank-twitch-irc/)\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n## Table of Contents\n\n- [Usage](#usage)\n- [Available client events](#available-client-events)\n- [Handling `USERNOTICE` messages](#handling-usernotice-messages)\n  - [Sub and resub](#sub-and-resub)\n  - [Incoming raids](#incoming-raids)\n  - [Subgift](#subgift)\n  - [Anonsubgift](#anonsubgift)\n  - [anongiftpaidupgrade, giftpaidupgrade](#anongiftpaidupgrade-giftpaidupgrade)\n  - [ritual](#ritual)\n  - [bitsbadgetier](#bitsbadgetier)\n- [ChatClient API](#chatclient-api)\n- [API Documentation](#api-documentation)\n- [Client options](#client-options)\n- [Features](#features)\n- [Extra Mixins](#extra-mixins)\n- [Tests](#tests)\n- [Lint and check code style](#lint-and-check-code-style)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Usage\n\n```javascript\nconst { ChatClient } = require(\"dank-twitch-irc\");\n\nlet client = new ChatClient();\n\nclient.on(\"ready\", () =\u003e console.log(\"Successfully connected to chat\"));\nclient.on(\"close\", (error) =\u003e {\n  if (error != null) {\n    console.error(\"Client closed due to error\", error);\n  }\n});\n\nclient.on(\"PRIVMSG\", (msg) =\u003e {\n  console.log(`[#${msg.channelName}] ${msg.displayName}: ${msg.messageText}`);\n});\n\n// See below for more events\n\nclient.connect();\nclient.join(\"forsen\");\n```\n\n## Available client events\n\n- **`client.on(\"connecting\", () =\u003e { /* ... */ })`**: Called when the client\n  starts connecting for the first time.\n- **`client.on(\"connect\", () =\u003e { /* ... */ })`**: Called when the client\n  connects for the first time. This is called when the transport layer\n  connections (e.g. TCP or WebSocket connection is established), not when login\n  to IRC succeeds.\n- **`client.on(\"ready\", () =\u003e { /* ... */ })`**: Called when the client becomes\n  ready for the first time (login to the chat server is successful.)\n- **`client.on(\"close\", (error?: Error) =\u003e { /* ... */ })`**: Called when the\n  client is terminated as a whole. Not called for individual connections that\n  were disconnected. Can be caused for example by a invalid OAuth token (failure\n  to login), or when `client.close()` or `client.destroy()` was called. `error`\n  is only non-null if the client was closed by a call to `client.close()`.\n- **`client.on(\"error\", (error: Error?) =\u003e { /* ... */ })`**: Called when any\n  error occurs on the client, including non-fatal errors such as a message that\n  could not be delivered due to an error.\n- **`client.on(\"rawCommand\", (cmd: string) =\u003e { /* ... */ })`**: Called when any\n  command is executed by the client.\n- **`client.on(\"message\", (message: IRCMessage) =\u003e { /* ... */ })`**: Called on\n  every incoming message. If the message is a message that is further parsed (I\n  called these \"twitch messages\" in this library) then the `message` passed to\n  this handler will already be the specific type, e.g. `PrivmsgMessage` if the\n  command is `PRIVMSG`.\n- **`client.on(\"PRIVMSG\", (message: PrivmsgMessage) =\u003e { /* ... */ })`**: Called\n  on incoming messages whose command is `PRIVMSG`. The `message` parameter is\n  always instanceof `PrivmsgMessage`. (See the API documentation for what\n  properties exist on all `PrivmsgMessage` instances)\n\n  For example:\n\n  ```javascript\n  client.on(\"CLEARCHAT\", (msg) =\u003e {\n    if (msg.isTimeout()) {\n      console.log(\n        `${msg.targetUsername} just got timed out for ` +\n          `${msg.banDuration} seconds in channel ${msg.channelName}`\n      );\n    }\n  });\n  ```\n\n  Other message types that have specific message parsing are:\n\n  - **`CLEARCHAT`** (maps to [`ClearchatMessage`][clearchat]) - Timeout and ban\n    messages\n  - **`CLEARMSG`** (maps to [`ClearmsgMessage`][clearmsg]) - Single message\n    deletions (initiated by `/delete`)\n  - **`HOSTTARGET`** (maps to [`HosttargetMessage`][hosttarget]) - A channel\n    entering or exiting host mode.\n  - **`NOTICE`** (maps to [`NoticeMessage`][notice]) - Various notices, such as\n    when you `/help`, a command fails, the error response when you are timed\n    out, etc.\n  - **`PRIVMSG`** (maps to [`PrivmsgMessage`][privmsg]) - Normal chat messages\n  - **`ROOMSTATE`** (maps to [`RoomstateMessage`][roomstate]) - A change to a\n    channel's followers mode, subscribers-only mode, r9k mode, followers mode,\n    slow mode etc.\n  - **`USERNOTICE`** (maps to [`UsernoticeMessage`][usernotice]) - Subs, resubs,\n    sub gifts, rituals, raids, etc. - See more details about how to handle this\n    message type below.\n  - **`USERSTATE`** (maps to [`UserstateMessage`][userstate]) - Your own state\n    (e.g. badges, color, display name, emote sets, mod status), sent on every\n    time you join a channel or send a `PRIVMSG` to a channel\n  - **`GLOBALUSERSTATE`** (maps to\n    [`GlobaluserstateMessage`][globaluserstate]) - Logged in user's \"global\n    state\", sent once on every login (Note that due to the used connection pool\n    you can receive this multiple times during your bot's runtime)\n  - **`WHISPER`** (maps to [`WhisperMessage`][whisper]) - Somebody else\n    whispering you\n  - **`JOIN`** (maps to [`JoinMessage`][join]) - You yourself joining a channel,\n    of if you have `requestMembershipCapability` enabled, also other users\n    joining channels you are joined to.\n  - **`PART`** (maps to [`JoinMessage`][part]) - You yourself parting (leaving)\n    a channel, of if you have `requestMembershipCapability` enabled, also other\n    users parting channels you are joined to.\n  - **`RECONNECT`** (maps to [`ReconnectMessage`][reconnect]) - When the twitch\n    server tells a client to reconnect and re-join channels (You don't have to\n    listen for this yourself, this is done automatically already)\n  - **`PING`** (maps to [`PingMessage`][ping]) - When the twitch server sends a\n    ping, expecting a pong back from the client to verify if the connection is\n    still alive. (You don't have to listen for this yourself, the client\n    automatically responds for you)\n  - **`PONG`** (maps to [`PongMessage`][pong]) - When the twitch server responds\n    to our `PING` requests (The library automatically sends a `PING` request\n    every 30 seconds to verify connections are alive)\n  - **`CAP`** (maps to [`CapMessage`][cap]) - Message type received once during\n    connection startup, acknowledging requested capabilities.\n\nAll other commands (if they don't have a special parsed type like the ones\nlisted above) will still be emitted under their command name as an\n[`IRCMessage`][ircmessage], e.g.:\n\n```javascript\n// :tmi.twitch.tv 372 botfactory :You are in a maze of twisty passages, all alike.\n// msg will be an instance of IRCMessage\nclient.on(\"372\", (msg) =\u003e\n  console.log(`Server MOTD is: ${msg.ircParameters[1]}`)\n);\n```\n\n## Handling `USERNOTICE` messages\n\nThe `USERNOTICE` message type is special because it encapsulates a wide range of\nevents, including:\n\n- Subs\n- Resubs\n- Gift subscription\n- Incoming raid and\n- Channel rituals,\n\nwhich are all emitted under the `USERNOTICE` event. See also\n[the offical documentation](https://dev.twitch.tv/docs/irc/tags/#usernotice-twitch-tags)\nabout the `USERNOTICE` command.\n\nEvery `USERNOTICE` message is sent by a user, and always contains a\n`msg.systemMessage` (This is a message that twitch formats for you, e.g.\n`4 raiders from PotehtoO have joined!` for a `raid` message.) Additionally,\nevery `USERNOTICE` message can have a message that is additionally sent/shared\nfrom the sending user, for example the \"share this message with the streamer\"\nmessage sent with resubs and subs. If no message is sent by the user,\n`msg.messageText` is `undefined`.\n\n`dank-twitch-irc` currently does not have special parsing code for each\n`USERNOTICE` `messageTypeID` (e.g. `sub`, `resub`, `raid`, etc...) - Instead the\nparser assigns all `msg-param-` tags to the `msg.eventParams` object. See below\non what `msg.eventParams` are available for each of the `messageTypeID`s.\n\n### Sub and resub\n\nWhen a user subscribes or resubscribes with his own money/prime (this is NOT\nsent for gift subs, see below)\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  // sub and resub messages have the same parameters, so we can handle them both the same way\n  if (!msg.isSub() \u0026\u0026 !msg.isResub()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   *\n   * {\n   *   \"cumulativeMonths\": 10,\n   *   \"cumulativeMonthsRaw\": \"10\",\n   *   \"subPlan\": \"1000\", // Prime, 1000, 2000 or 3000\n   *   \"subPlanName\": \"The Ninjas\",\n   *\n   *   // if shouldShareStreak is false, then\n   *   // streakMonths/streakMonthsRaw will be 0\n   *   // (the user did not share their sub streak in chat)\n   *   \"shouldShareStreak\": true,\n   *   \"streakMonths\": 7,\n   *   \"streakMonthsRaw\": \"7\"\n   * }\n   * Sender user of the USERNOTICE message is the user subbing/resubbing.\n   */\n\n  if (msg.isSub()) {\n    // Leppunen just subscribed to ninja with a tier 1000 (The Ninjas) sub for the first time!\n    console.log(\n      msg.displayName +\n        \" just subscribed to \" +\n        msg.channelName +\n        \" with a tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams.subPlanName +\n        \") sub for the first time!\"\n    );\n  } else if (msg.isResub()) {\n    let streakMessage = \"\";\n    if (msg.eventParams.shouldShareStreak) {\n      streakMessage =\n        \", currently \" + msg.eventParams.streakMonths + \" months in a row\";\n    }\n\n    // Leppunen just resubscribed to ninja with a tier 1000 (The Ninjas) sub!\n    // They are resubscribing for 10 months, currently 7 months in a row!\n    console.log(\n      msg.displayName +\n        \" just resubscribed to \" +\n        msg.channelName +\n        \" with a tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams.subPlanName +\n        \") sub! They are resubscribing for \" +\n        msg.eventParams.cumulativeMonths +\n        \" months\" +\n        streakMessage +\n        \"!\"\n    );\n  }\n\n  if (msg.messageText != null) {\n    // you also have access to lots of other properties also present on PRIVMSG messages,\n    // such as msg.badges, msg.senderUsername, msg.badgeInfo, msg.bits/msg.isCheer(),\n    // msg.color, msg.emotes, msg.messageID, msg.serverTimestamp, etc...\n    console.log(\n      msg.displayName +\n        \" shared the following message with the streamer: \" +\n        msg.messageText\n    );\n  } else {\n    console.log(\"They did not share a message with the streamer.\");\n  }\n});\n```\n\n### Incoming raids\n\nTwitch says:\n\n\u003e Incoming raid to a channel. Raid is a Twitch tool that allows broadcasters to\n\u003e send their viewers to another channel, to help support and grow other members\n\u003e in the community.)\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isRaid()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * {\n   *   \"displayName\": \"Leppunen\",\n   *   \"login\": \"leppunen\",\n   *   \"viewerCount\": 12,\n   *   \"viewerCountRaw\": \"12\"\n   * }\n   * Sender user of the USERNOTICE message is the user raiding this channel.\n   * Note that the display name and login present in msg.eventParams are\n   * the same as msg.displayName and msg.senderUsername, so it doesn't matter\n   * which one you use (although I recommend the properties directly on the\n   * message object, not in eventParams)\n   */\n\n  // source user is the channel/streamer raiding\n  // Leppunen just raided Supinic with 12 viewers!\n  console.log(\n    msg.displayName +\n      \" just raided \" +\n      msg.channelName +\n      \" with \" +\n      msg.eventParams.viewerCount +\n      \" viewers!\"\n  );\n});\n```\n\n### Subgift\n\nWhen a user gifts somebody else a subscription.\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isSubgift()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * {\n   *   \"months\": 5,\n   *   \"monthsRaw\": \"5\",\n   *   \"giftMonths\": 5,\n   *   \"giftMonthsRaw\": \"5\",\n   *   \"recipientDisplayName\": \"Leppunen\",\n   *   \"recipientID\": \"42239452\",\n   *   \"recipientUsername\": \"leppunen\",\n   *   \"subPlan\": \"1000\",\n   *   \"subPlanName\": \"The Ninjas\",\n   *   \"senderCount\": 5,\n   *   \"senderCountRaw\": \"5\",\n   * }\n   * Sender user of the USERNOTICE message is the user gifting the subscription.\n   */\n\n  if (msg.eventParams.months === 1) {\n    // Leppunen just gifted NymN a fresh tier 1000 (The Ninjas) sub to ninja!\n    console.log(\n      msg.displayName +\n        \" just gifted \" +\n        msg.eventParams.recipientDisplayName +\n        \" a fresh tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams +\n        \") sub to \" +\n        msg.channelName +\n        \"!\"\n    );\n  } else {\n    // Leppunen just gifted NymN a tier 1000 (The Ninjas) resub to ninja, that's 7 months in a row!\n    console.log(\n      msg.displayName +\n        \" just gifted \" +\n        msg.eventParams.recipientDisplayName +\n        \" a tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams +\n        \") resub to \" +\n        msg.channelName +\n        \", that's \" +\n        msg.eventParams.months +\n        \" in a row!\"\n    );\n  }\n\n  // note: if the subgift was from an anonymous user, the sender user for the USERNOTICE message will be\n  // AnAnonymousGifter (user ID 274598607)\n  if (msg.senderUserID === \"274598607\") {\n    console.log(\"That (re)sub was gifted anonymously!\");\n  }\n});\n```\n\n### Anonsubgift\n\nWhen an anonymous user gifts a subscription to a viewer.\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isAnonSubgift()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * {\n   *   \"months\": 5,\n   *   \"monthsRaw\": \"5\",\n   *   \"recipientDisplayName\": \"Leppunen\",\n   *   \"recipientID\": \"42239452\",\n   *   \"recipientUsername\": \"leppunen\",\n   *   \"subPlan\": \"1000\",\n   *   \"subPlanName\": \"The Ninjas\"\n   * }\n   *\n   * WARNING! Sender user of the USERNOTICE message is the broadcaster (e.g. Ninja\n   * in the example below)\n   */\n\n  if (msg.eventParams.months === 1) {\n    // An anonymous gifter just gifted NymN a fresh tier 1000 (The Ninjas) sub to ninja!\n    console.log(\n      \"An anonymous gifter just gifted \" +\n        msg.eventParams.recipientDisplayName +\n        \" a fresh tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams +\n        \") sub to \" +\n        msg.channelName +\n        \"!\"\n    );\n  } else {\n    // An anonymous gifter just gifted NymN a tier 1000 (The Ninjas) resub to ninja, that's 7 months in a row!\n    console.log(\n      \"An anonymous gifter just gifted \" +\n        msg.eventParams.recipientDisplayName +\n        \" a tier \" +\n        msg.eventParams.subPlan +\n        \" (\" +\n        msg.eventParams +\n        \") resub to \" +\n        msg.channelName +\n        \", that's \" +\n        msg.eventParams.months +\n        \" in a row!\"\n    );\n  }\n});\n```\n\n### anongiftpaidupgrade, giftpaidupgrade\n\nWhen a user commits to continue the gift sub by another user (or an anonymous\ngifter).\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isAnonGiftPaidUpgrade()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * EITHER: (ONLY when a promotion is running!)\n   * {\n   *   \"promoName\": \"Subtember 2018\",\n   *   \"promoGiftTotal\": 3987234,\n   *   \"promoGiftTotalRaw\": \"3987234\"\n   * }\n   * OR: (when no promotion is running)\n   * {}\n   *\n   * Sender user of the USERNOTICE message is the user continuing their sub.\n   */\n\n  // Leppunen is continuing their ninja gift sub they got from an anonymous user!\n  console.log(\n    msg.displayName +\n      \" is continuing their \" +\n      msg.channelName +\n      \" gift sub they got from an anonymous user!\"\n  );\n});\n```\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isGiftPaidUpgrade()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * EITHER: (ONLY when a promotion is running!)\n   * {\n   *   \"promoName\": \"Subtember 2018\",\n   *   \"promoGiftTotal\": 3987234,\n   *   \"promoGiftTotalRaw\": \"3987234\",\n   *   \"senderLogin\": \"krakenbul\",\n   *   \"senderName\": \"Krakenbul\"\n   * }\n   * OR: (when no promotion is running)\n   * {\n   *   \"senderLogin\": \"krakenbul\",\n   *   \"senderName\": \"Krakenbul\"\n   * }\n   *\n   * Sender user of the USERNOTICE message is the user continuing their sub.\n   */\n\n  // Leppunen is continuing their ninja gift sub they got from Krakenbul!\n  console.log(\n    msg.displayName +\n      \" is continuing their \" +\n      msg.channelName +\n      \" gift sub they got from \" +\n      msg.msgParam.senderName +\n      \"!\"\n  );\n});\n```\n\n### ritual\n\nChannel ritual. Twitch says:\n\n\u003e Channel _ritual_. Many channels have special rituals to celebrate viewer\n\u003e milestones when they are shared. The rituals notice extends the sharing of\n\u003e these messages to other viewer milestones (initially, a new viewer chatting\n\u003e for the first time).\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isRitual()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * {\n   *   \"ritualName\": \"new_chatter\"\n   * }\n   *\n   * Sender user of the USERNOTICE message is the user performing the\n   * ritual (e.g. the new chatter).\n   */\n\n  // Leppunen is new to ninja's chat! Say hello!\n  if (msg.eventParams.ritualName === \"new_chatter\") {\n    console.log(\n      msg.displayName + \" is new to \" + msg.channelName + \"'s chat! Say hello!\"\n    );\n  } else {\n    console.warn(\n      \"Unknown (unhandled) ritual type: \" + msg.eventParams.ritualName\n    );\n  }\n});\n```\n\n### bitsbadgetier\n\nWhen a user cheers and earns himself a new bits badge with that cheer (e.g. they\njust cheered more than/exactly 10000 bits in total, and just earned themselves\nthe 10k bits badge)\n\n```javascript\nchatClient.on(\"USERNOTICE\", (msg) =\u003e {\n  if (!msg.isBitsBadgeTier()) {\n    return;\n  }\n\n  /*\n   * msg.eventParams are:\n   * {\n   *   \"threshold\": 10000,\n   *   \"thresholdRaw\": \"10000\",\n   * }\n   *\n   * Sender user of the USERNOTICE message is the user cheering the bits.\n   */\n\n  // Leppunen just earned themselves the 10000 bits badge in ninja's channel!\n  console.log(\n    msg.displayName +\n      \" just earned themselves the \" +\n      msg.threshold +\n      \" bits badge in \" +\n      msg.channelName +\n      \"'s channel!\"\n  );\n});\n```\n\n## ChatClient API\n\nYou probably will want to use these functions on `ChatClient` most frequently:\n\n- `client.join(channelName: string): Promise\u003cvoid\u003e` - Join (Listen to) the\n  channel given by the channel name\n- `client.joinAll(channelNames: string[]): Promise\u003cvoid\u003e` - Join (Listen to) all\n  of the listed channels at once (bulk join)\n- `client.part(channelName: string): Promise\u003cvoid\u003e` - Part (Leave/Unlisten) the\n  channel given by the channel name\n- `client.privmsg(channelName: string, message: string): Promise\u003cvoid\u003e` - Send a\n  raw `PRIVMSG` to the given channel. You can issue chat commands with this\n  function, e.g. `client.privmsg(\"forsen\", \"/timeout weeb123 5\")` or normal\n  messages, e.g. `client.privmsg(\"forsen\", \"Kappa Keepo PogChamp\")`.\n- `client.say(channelName: string, message: string): Promise\u003cvoid\u003e` - Say a\n  normal chat message in the given channel. If a command is given as `message`,\n  it will be escaped.\n- `client.me(channelName: string, message: string): Promise\u003cvoid\u003e` - Post a\n  `/me` message in the given channel.\n- `client.timeout(channelName: string, username: string, length: number, reason?: string): Promise\u003cvoid\u003e` -\n  Timeout `username` for `length` seconds in `channelName`. Optionally accepts a\n  reason to set.\n- `client.ban(channelName: string, username: string, reason?: string): Promise\u003cvoid\u003e` -\n  Ban `username` in `channelName`. Optionally accepts a reason to set.\n- `client.ping()` - Send a `PING` on a connection from the pool, and awaits the\n  `PONG` response. You can use this to measure server latency, for example.\n- `client.whisper(username: string, message: string)` - Send the user a whisper\n  from the bot.\n- `client.setColor(color: Color)` - set the username color of your bot account.\n  E.g. `client.setColor({ r: 255, g: 0, b: 127 })`.\n- `client.getMods(channelName: string)` and `client.getVips(channelName: string)` -\n  Get a list of moderators/VIPs in a channel. Returns\n  a promise that resolves to an array of strings (login names of the moderators/VIPs).\n  Note that due to Twitch's restrictions, this function cannot be used with anonymous chat clients.\n  (The request will time out if your chat client is logged in as anonymous.)\n\nExtra functionality:\n\n- `client.sendRaw(command: string): void` - Send a raw IRC command to a\n  connection in the connection pool.\n- `client.unconnected (boolean)` - Returns whether the client is unconnected.\n- `client.connecting (boolean)` - Returns whether the client is connecting.\n- `client.connected (boolean)` - Returns whether the client is connected\n  (Transport layer is connected).\n- `client.ready (boolean)` - Returns whether the client is ready (Logged into\n  IRC server).\n- `client.closed (boolean)` - Returns whether the client is closed.\n\nNote that channel names in the above functions always refer to the \"login name\"\nof a twitch channel. Channel names may not be capitalized, e.g. `Forsen` would\nbe invalid, but `forsen` not. This library also does not accept the leading `#`\ncharacter and never returns it on any message objects (e.g. `msg.channelName`\nwould be `forsen`, not `#forsen`).\n\n## API Documentation\n\nGenerated API documentation can be found here:\nhttps://robotty.github.io/dank-twitch-irc\n\n## Client options\n\nPass options to the `ChatClient` constructor. More available options are\ndocumented in the Below are all possible options and their default values:\n\n**Note! ALL of these configuration options are _optional_!** I highly recommend you\nonly set the very config options you need, the rest are usually at a reasonable default.  \nFor most bots, you only need to set `username` and `password`:\n\n```javascript\nlet client = new ChatClient({\n  username: \"your-bot-username\",\n  password: \"0123456789abcdef1234567\",\n});\n```\n\nNevertheless, here are examples of all possible config options:\n\n```javascript\nlet client = new ChatClient({\n  username: \"your-bot-username\", // justinfan12345 by default - For anonymous chat connection\n  password: \"0123456789abcdef1234567\", // undefined by default (no password)\n\n  // Message rate limits configuration for verified and known bots\n  // pick one of the presets or configure custom rates as shown below:\n  rateLimits: \"default\",\n  // or:\n  rateLimits: \"knownBot\",\n  // or:\n  rateLimits: \"verifiedBot\",\n  // or:\n  rateLimits: {\n    highPrivmsgLimits: 100,\n    lowPrivmsgLimits: 20,\n  },\n\n  // Configuration options for the backing connections:\n  // Plain TCP or TLS\n  connection: {\n    type: \"tcp\", // tcp by default\n    secure: false, // true by default\n    // host and port must both be specified at once\n    host: \"custom-chat-server.com\", // irc.chat.twitch.tv by default\n    port: 1234, // 6697/6667 by default, depending on the \"secure\" setting\n  },\n  // or:\n  connection: {\n    type: \"websocket\",\n    secure: true, // use preset URL of irc-ws.chat.twitch.tv\n  },\n  // or:\n  connection: {\n    type: \"websocket\",\n    url: \"wss://custom-url.com/abc/def\", // custom URL\n  },\n  // or:\n  connection: {\n    type: \"duplex\",\n    stream: () =\u003e aNodeJsDuplexInstance, // read and write to a custom object\n    // implementing the Duplex interface from Node.js\n    // the function you specify is called for each new connection\n\n    preSetup: true, // false by default, makes the lib skip login\n    // and capabilities negotiation on connection startup\n  },\n\n  // how many channels each individual connection should join at max\n  maxChannelCountPerConnection: 100, // 90 by default\n\n  // custom parameters for connection rate limiting\n  connectionRateLimits: {\n    parallelConnections: 5, // 1 by default\n    // time to wait after each connection before a new connection can begin\n    releaseTime: 1000, // in milliseconds, 2 seconds by default\n  },\n\n  // I recommend you leave this off by default, it makes your bot faster\n  // If you need live update of who's joining and leaving chat,\n  // poll the tmi.twitch.tv chatters endpoint instead since it\n  // is also more reliable\n  requestMembershipCapability: false, // false by default\n\n  // read more about mixins below\n  // this disables the connection rate limiter, message rate limiter\n  // and Room- and Userstate trackers (which are important for other mixins)\n  installDefaultMixins: false, // true by default\n\n  // Silence UnandledPromiseRejectionWarnings on all client methods\n  // that return promises.\n  // With this option enabled, the returned promises will still be rejected/\n  // resolved as without this option, this option ONLY silences the\n  // UnhandledPromiseRejectionWarning.\n  ignoreUnhandledPromiseRejections: true, // false by default\n});\n```\n\n## Features\n\nThis client currently supports the following features:\n\n- Connection pooling and round-robin connection usage\n- Automatic rate limiter for connection opening and chat commands\n- All twitch-specific message types parsed (`CLEARCHAT`, `CLEARMSG`,\n  `GLOBALUSERSTATE`, `HOSTTARGET`, `JOIN`, `NOTICE`, `PART`, `PING`, `PONG`,\n  `PRIVMSG`, `RECONNECT`, `ROOMSTATE`, `USERNOTICE`, `USERSTATE`, `WHISPER`,\n  `CAP`)\n- Accurate response to server responses (e.g. error thrown if you are banned\n  from channel/channel is suspended/login is invalid etc.)\n- Bulk join functionality to join lots of channels quickly\n- Implements the recommended connection control, utilizing `RECONNECT`, `PING`\n  and `PONG`\n- Full tracking of room state (e.g. submode, emote-only mode, followers mode,\n  r9k etc.) and user state (badges, moderator state, color, etc).\n- Most function calls return promises but errors can also be handled by\n  subscribing to the error event\n- Slow-mode rate limiter for non-VIP/moderator bots (waits either the global\n  ~1.3 sec/channel-specific slow mode)\n- Support for different types of transport (in-memory, TCP, WebSocket)\n\n## Extra Mixins\n\nThere are some features you might find useful in your bot that are not necessary\nfor general client/bot operations, so they were packaged as **mixins**. You can\nactivate mixins by calling:\n\n```javascript\nconst { ChatClient, AlternateMessageModifier } = require(\"dank-twitch-irc\");\n\nlet client = new ChatClient();\n\nclient.use(new AlternateMessageModifier(client));\n```\n\nAvailable mixins are:\n\n- `new AlternateMessageModifier(client)` will allow your bot to send the same\n  message within a 30 seconds period. You must also use `client.say` and\n  `client.me` for this mixin to behave consistently and reliably.\n- `new SlowModeRateLimiter(client, /* optional */ maxWaitingMessages)` will rate\n  limit your messages in channels where your bot is not moderator, VIP or\n  broadcaster and has to wait a bit between sending messages. If more than\n  `maxWaitingMessages` are waiting, the outgoing message will be dropped\n  silently. `maxWaitingMessages` defaults to 10. Note this mixin only has an\n  effect on `client.say` and `client.me` functions, not `client.privmsg`.\n\nand the mixins installed by default:\n\n- `new PrivmsgMessageRateLimiter(client)` - Rate limits outgoing messages\n  according to the rate limits imposed by Twitch. Configure the verified/known\n  status of your bot using the config (see above).\n- `new ConnectionRateLimiter(client)` - Rate limits new connections accoding to\n  the rate limits set in the config.\n- `new UserStateTracker(client)` - Used by other mixins. Keeps track of what\n  state your bot user has in all channels.\n- `new RoomStateTracker()` - Used by other mixins. Keeps track of each channel's\n  state, e.g. sub-mode etc.\n- `new IgnoreUnhandledPromiseRejectionsMixin()` - Silences\n  `UnhandledPromiseRejectionWarning`s on promises returned by the client's\n  functions. (installed for you if you activate the\n  `ignoreUnhandledPromiseRejections` client option)\n\n## Tests\n\n    npm run test\n\nTest run report is available in `./mochawesome-report/mochawesome.html`.\nCoverage report is produced as `./coverage/index.html`.\n\n## Lint and check code style\n\n```bash\n# Run eslint and tslint rules and checks code style with prettier\nnpm run lint\n```\n\n```bash\n# Run eslint, tslint and pretter fixers\nnpm run lintfix\n```\n\n[clearchat]: https://robotty.github.io/dank-twitch-irc/classes/clearchatmessage.html\n[clearmsg]: https://robotty.github.io/dank-twitch-irc/classes/clearmsgmessage.html\n[hosttarget]: https://robotty.github.io/dank-twitch-irc/classes/hosttargetmessage.html\n[notice]: https://robotty.github.io/dank-twitch-irc/classes/noticemessage.html\n[privmsg]: https://robotty.github.io/dank-twitch-irc/classes/privmsgmessage.html\n[roomstate]: https://robotty.github.io/dank-twitch-irc/classes/roomstatemessage.html\n[usernotice]: https://robotty.github.io/dank-twitch-irc/classes/usernoticemessage.html\n[userstate]: https://robotty.github.io/dank-twitch-irc/classes/userstatemessage.html\n[globaluserstate]: https://robotty.github.io/dank-twitch-irc/classes/globaluserstatemessage.html\n[whisper]: https://robotty.github.io/dank-twitch-irc/classes/whispermessage.html\n[join]: https://robotty.github.io/dank-twitch-irc/classes/joinmessage.html\n[part]: https://robotty.github.io/dank-twitch-irc/classes/partmessage.html\n[reconnect]: https://robotty.github.io/dank-twitch-irc/classes/reconnectmessage.html\n[ping]: https://robotty.github.io/dank-twitch-irc/classes/pingmessage.html\n[pong]: https://robotty.github.io/dank-twitch-irc/classes/pongmessage.html\n[cap]: https://robotty.github.io/dank-twitch-irc/classes/capmessage.html\n[ircmessage]: https://robotty.github.io/dank-twitch-irc/classes/ircmessage.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobotty%2Fdank-twitch-irc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobotty%2Fdank-twitch-irc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobotty%2Fdank-twitch-irc/lists"}